From f8da6723075d4be836fe732706a07fc5c2504c7e Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Fri, 9 Feb 2024 15:57:08 +0100 Subject: [PATCH 001/271] [FEAT]: Allow forking without a repo ID Forking a repository via the web UI currently requires visiting a `/repo/fork/{{repoid}}` URL. This makes it cumbersome to create a link that starts a fork, because the repository ID is only available via the API. While it *is* possible to create a link, doing so requires extra steps. To make it easier to have a "Fork me!"-style links, introduce the `/{username}/{repo}/fork` route, which will start the forking process based on the repository in context instead. The old `/repo/fork/{repoid}` route (with a `GET` request) will remain there for the sake of backwards compatibility, but will redirect to the new URL instead. It's `POST` handler is removed. Tests that used the old route are updated to use the new one, and new tests are introduced to exercise the redirect. Signed-off-by: Gergely Nagy --- routers/web/repo/pull.go | 38 ++++---- routers/web/web.go | 7 +- templates/repo/header.tmpl | 4 +- tests/integration/repo_fork_test.go | 129 ++++++++++++++++++++++------ 4 files changed, 130 insertions(+), 48 deletions(-) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 7830c17ced..33aab4e2ce 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -115,21 +115,21 @@ func getRepository(ctx *context.Context, repoID int64) *repo_model.Repository { return repo } -func getForkRepository(ctx *context.Context) *repo_model.Repository { - forkRepo := getRepository(ctx, ctx.ParamsInt64(":repoid")) - if ctx.Written() { - return nil +func updateForkRepositoryInContext(ctx *context.Context, forkRepo *repo_model.Repository) bool { + if forkRepo == nil { + ctx.NotFound("No repository in context", nil) + return false } if forkRepo.IsEmpty { log.Trace("Empty repository %-v", forkRepo) - ctx.NotFound("getForkRepository", nil) - return nil + ctx.NotFound("updateForkRepositoryInContext", nil) + return false } if err := forkRepo.LoadOwner(ctx); err != nil { ctx.ServerError("LoadOwner", err) - return nil + return false } ctx.Data["repo_name"] = forkRepo.Name @@ -142,7 +142,7 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository { ownedOrgs, err := organization.GetOrgsCanCreateRepoByUserID(ctx, ctx.Doer.ID) if err != nil { ctx.ServerError("GetOrgsCanCreateRepoByUserID", err) - return nil + return false } var orgs []*organization.Organization for _, org := range ownedOrgs { @@ -170,7 +170,7 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository { traverseParentRepo, err = repo_model.GetRepositoryByID(ctx, traverseParentRepo.ForkID) if err != nil { ctx.ServerError("GetRepositoryByID", err) - return nil + return false } } @@ -184,7 +184,7 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository { } else { ctx.Data["CanForkRepo"] = false ctx.Flash.Error(ctx.Tr("repo.fork_no_valid_owners"), true) - return nil + return false } branches, err := git_model.FindBranchNames(ctx, git_model.FindBranchOptions{ @@ -198,14 +198,19 @@ func getForkRepository(ctx *context.Context) *repo_model.Repository { }) if err != nil { ctx.ServerError("FindBranchNames", err) - return nil + return false } ctx.Data["Branches"] = append([]string{ctx.Repo.Repository.DefaultBranch}, branches...) - return forkRepo + return true } -// Fork render repository fork page +// ForkByID redirects (with 301 Moved Permanently) to the repository's `/fork` page +func ForkByID(ctx *context.Context) { + ctx.Redirect(ctx.Repo.Repository.Link()+"/fork", http.StatusMovedPermanently) +} + +// Fork renders the repository fork page func Fork(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("new_fork") @@ -217,8 +222,7 @@ func Fork(ctx *context.Context) { ctx.Flash.Error(msg, true) } - getForkRepository(ctx) - if ctx.Written() { + if !updateForkRepositoryInContext(ctx, ctx.Repo.Repository) { return } @@ -236,8 +240,8 @@ func ForkPost(ctx *context.Context) { return } - forkRepo := getForkRepository(ctx) - if ctx.Written() { + forkRepo := ctx.Repo.Repository + if !updateForkRepositoryInContext(ctx, forkRepo) { return } diff --git a/routers/web/web.go b/routers/web/web.go index 1744ddb83a..a89d1aa3c3 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -967,10 +967,7 @@ func registerRoutes(m *web.Route) { m.Post("/create", web.Bind(forms.CreateRepoForm{}), repo.CreatePost) m.Get("/migrate", repo.Migrate) m.Post("/migrate", web.Bind(forms.MigrateRepoForm{}), repo.MigratePost) - m.Group("/fork", func() { - m.Combo("/{repoid}").Get(repo.Fork). - Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost) - }, context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader) + m.Get("/fork/{repoid}", context.RepoIDAssignment(), context.UnitTypes(), reqRepoCodeReader, repo.ForkByID) m.Get("/search", repo.SearchRepo) }, reqSignIn) @@ -1148,6 +1145,8 @@ func registerRoutes(m *web.Route) { // Grouping for those endpoints that do require authentication m.Group("/{username}/{reponame}", func() { + m.Combo("/fork", reqRepoCodeReader).Get(repo.Fork). + Post(web.Bind(forms.CreateRepoForm{}), repo.ForkPost) m.Group("/issues", func() { m.Group("/new", func() { m.Combo("").Get(context.RepoRef(), repo.NewIssue). diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index ef3e40eea8..3e29f1f29e 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -82,7 +82,7 @@ {{/*else is not required here, because the button shouldn't link to any site if you can't create a fork*/}} {{end}} {{else if not $.UserAndOrgForks}} - href="{{AppSubUrl}}/repo/fork/{{.ID}}" + href="{{$.RepoLink}}/fork" {{else}} data-modal="#fork-repo-modal" {{end}} @@ -103,7 +103,7 @@ {{if $.CanSignedUserFork}}
- {{ctx.Locale.Tr "repo.fork_to_different_account"}} + {{ctx.Locale.Tr "repo.fork_to_different_account"}} {{end}} diff --git a/tests/integration/repo_fork_test.go b/tests/integration/repo_fork_test.go index 594fba6796..c6e3fed7a9 100644 --- a/tests/integration/repo_fork_test.go +++ b/tests/integration/repo_fork_test.go @@ -7,36 +7,36 @@ import ( "fmt" "net/http" "net/http/httptest" + "net/url" "testing" + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" ) func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkOwnerName, forkRepoName string) *httptest.ResponseRecorder { + t.Helper() + forkOwner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: forkOwnerName}) // Step0: check the existence of the to-fork repo req := NewRequestf(t, "GET", "/%s/%s", forkOwnerName, forkRepoName) session.MakeRequest(t, req, http.StatusNotFound) - // Step1: go to the main page of repo - req = NewRequestf(t, "GET", "/%s/%s", ownerName, repoName) + // Step1: visit the /fork page + forkURL := fmt.Sprintf("/%s/%s/fork", ownerName, repoName) + req = NewRequest(t, "GET", forkURL) resp := session.MakeRequest(t, req, http.StatusOK) - // Step2: click the fork button + // Step2: fill the form of the forking htmlDoc := NewHTMLParser(t, resp.Body) - link, exists := htmlDoc.doc.Find("a.ui.button[href^=\"/repo/fork/\"]").Attr("href") - assert.True(t, exists, "The template has changed") - req = NewRequest(t, "GET", link) - resp = session.MakeRequest(t, req, http.StatusOK) - - // Step3: fill the form of the forking - htmlDoc = NewHTMLParser(t, resp.Body) - link, exists = htmlDoc.doc.Find("form.ui.form[action^=\"/repo/fork/\"]").Attr("action") + link, exists := htmlDoc.doc.Find(fmt.Sprintf("form.ui.form[action=\"%s\"]", forkURL)).Attr("action") assert.True(t, exists, "The template has changed") _, exists = htmlDoc.doc.Find(fmt.Sprintf(".owner.dropdown .item[data-value=\"%d\"]", forkOwner.ID)).Attr("data-value") assert.True(t, exists, fmt.Sprintf("Fork owner '%s' is not present in select box", forkOwnerName)) @@ -47,29 +47,108 @@ func testRepoFork(t *testing.T, session *TestSession, ownerName, repoName, forkO }) session.MakeRequest(t, req, http.StatusSeeOther) - // Step4: check the existence of the forked repo + // Step3: check the existence of the forked repo req = NewRequestf(t, "GET", "/%s/%s", forkOwnerName, forkRepoName) resp = session.MakeRequest(t, req, http.StatusOK) return resp } +func testRepoForkLegacyRedirect(t *testing.T, session *TestSession, ownerName, repoName string) { + t.Helper() + + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: ownerName}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: owner.ID, Name: repoName}) + + // Visit the /repo/fork/:id url + req := NewRequestf(t, "GET", "/repo/fork/%d", repo.ID) + resp := session.MakeRequest(t, req, http.StatusMovedPermanently) + + assert.Equal(t, repo.Link()+"/fork", resp.Header().Get("Location")) +} + func TestRepoFork(t *testing.T) { - defer tests.PrepareTestEnv(t)() - session := loginUser(t, "user1") - testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user5 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user5"}) + session := loginUser(t, user5.Name) + + t.Run("by name", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer func() { + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user5.ID, Name: "repo1"}) + repo_service.DeleteRepository(db.DefaultContext, user5, repo, false) + }() + testRepoFork(t, session, "user2", "repo1", "user5", "repo1") + }) + + t.Run("legacy redirect", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + testRepoForkLegacyRedirect(t, session, "user2", "repo1") + + t.Run("private 404", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Make sure the repo we try to fork is private + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 31, IsPrivate: true}) + + // user5 does not have access to user2/repo20 + req := NewRequestf(t, "GET", "/repo/fork/%d", repo.ID) // user2/repo20 + session.MakeRequest(t, req, http.StatusNotFound) + }) + t.Run("authenticated private redirect", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Make sure the repo we try to fork is private + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 31, IsPrivate: true}) + + // user1 has access to user2/repo20 + session := loginUser(t, "user1") + req := NewRequestf(t, "GET", "/repo/fork/%d", repo.ID) // user2/repo20 + session.MakeRequest(t, req, http.StatusMovedPermanently) + }) + t.Run("no code unit", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Make sure the repo we try to fork is private. + // We're also choosing user15/big_test_private_2, becase it has the Code unit disabled. + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 20, IsPrivate: true}) + + // user1, even though an admin, can't fork a repo without a code unit. + session := loginUser(t, "user1") + req := NewRequestf(t, "GET", "/repo/fork/%d", repo.ID) // user15/big_test_private_2 + session.MakeRequest(t, req, http.StatusNotFound) + }) + }) + }) } func TestRepoForkToOrg(t *testing.T) { - defer tests.PrepareTestEnv(t)() - session := loginUser(t, "user2") - testRepoFork(t, session, "user2", "repo1", "org3", "repo1") + onGiteaRun(t, func(t *testing.T, u *url.URL) { + session := loginUser(t, "user2") + org3 := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "org3"}) - // Check that no more forking is allowed as user2 owns repository - // and org3 organization that owner user2 is also now has forked this repository - req := NewRequest(t, "GET", "/user2/repo1") - resp := session.MakeRequest(t, req, http.StatusOK) - htmlDoc := NewHTMLParser(t, resp.Body) - _, exists := htmlDoc.doc.Find("a.ui.button[href^=\"/repo/fork/\"]").Attr("href") - assert.False(t, exists, "Forking should not be allowed anymore") + t.Run("by name", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer func() { + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: org3.ID, Name: "repo1"}) + repo_service.DeleteRepository(db.DefaultContext, org3, repo, false) + }() + + testRepoFork(t, session, "user2", "repo1", "org3", "repo1") + + // Check that no more forking is allowed as user2 owns repository + // and org3 organization that owner user2 is also now has forked this repository + req := NewRequest(t, "GET", "/user2/repo1") + resp := session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + _, exists := htmlDoc.doc.Find("a.ui.button[href^=\"/fork\"]").Attr("href") + assert.False(t, exists, "Forking should not be allowed anymore") + }) + + t.Run("legacy redirect", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + testRepoForkLegacyRedirect(t, session, "user2", "repo1") + }) + }) } From 5f2d8be38e9354b0feafd0246ed1ab8b6f74ecf7 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 6 Feb 2024 00:53:08 +0100 Subject: [PATCH 002/271] [FEAT]: New route to view lates run of specific workflows This adds a new route at `/actions/workflows/{workflow}/runs/latest`, which will redirect to the latest run of the given workflow. It can be further restricted by specifying an optional `?branch={branch}` query parameter. If no branch is specified, the route defaults to using the repo's default branch. This route is meant to go hand in hand with the Badge route that returns the result of the same workflow as a badge. This route can be used to link to the run that produced that result. Fixes #2303. Signed-off-by: Gergely Nagy --- routers/web/repo/actions/view.go | 29 +++++++++ routers/web/web.go | 5 +- tests/integration/actions_route_test.go | 81 ++++++++++++++++++++++++- 3 files changed, 112 insertions(+), 3 deletions(-) diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 63e3a352a5..757ff07c0d 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -22,6 +22,7 @@ import ( "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/base" context_module "code.gitea.io/gitea/modules/context" + "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -60,6 +61,34 @@ func ViewLatest(ctx *context_module.Context) { ctx.Redirect(run.HTMLURL(), http.StatusTemporaryRedirect) } +func ViewLatestWorkflowRun(ctx *context_module.Context) { + branch := ctx.FormString("branch") + if branch == "" { + branch = ctx.Repo.Repository.DefaultBranch + } + branch = fmt.Sprintf("refs/heads/%s", branch) + event := ctx.FormString("event") + + workflowFile := ctx.Params("workflow_name") + run, err := actions_model.GetLatestRunForBranchAndWorkflow(ctx, ctx.Repo.Repository.ID, branch, workflowFile, event) + if err != nil { + if errors.Is(err, util.ErrNotExist) { + ctx.NotFound("GetLatestRunForBranchAndWorkflow", err) + } else { + log.Error("GetLatestRunForBranchAndWorkflow: %v", err) + ctx.Error(http.StatusInternalServerError, "Unable to get latest run for workflow on branch") + } + return + } + + err = run.LoadAttributes(ctx) + if err != nil { + ctx.ServerError("LoadAttributes", err) + return + } + ctx.Redirect(run.HTMLURL(), http.StatusTemporaryRedirect) +} + type ViewRequest struct { LogCursors []struct { Step int `json:"step"` diff --git a/routers/web/web.go b/routers/web/web.go index 1744ddb83a..e9f3f76c54 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1403,7 +1403,10 @@ func registerRoutes(m *web.Route) { }) }) - m.Get("/workflows/{workflow_name}/badge.svg", badges.GetWorkflowBadge) + m.Group("/workflows/{workflow_name}", func() { + m.Get("/badge.svg", badges.GetWorkflowBadge) + m.Get("/runs/latest", actions.ViewLatestWorkflowRun) + }) }, reqRepoActionsReader, actions.MustEnableActions) m.Group("/wiki", func() { diff --git a/tests/integration/actions_route_test.go b/tests/integration/actions_route_test.go index df22fc8647..c941fca2e5 100644 --- a/tests/integration/actions_route_test.go +++ b/tests/integration/actions_route_test.go @@ -1,9 +1,11 @@ // Copyright 2023 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. // SPDX-License-Identifier: MIT package integration import ( + "context" "fmt" "net/http" "net/url" @@ -15,10 +17,82 @@ import ( "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" files_service "code.gitea.io/gitea/services/repository/files" + "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" ) +func TestActionsWebRouteLatestWorkflowRun(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + // create the repo + repo, _, f := CreateDeclarativeRepo(t, user2, "", + []unit_model.Type{unit_model.TypeActions}, nil, + []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: ".gitea/workflows/workflow-1.yml", + ContentReader: strings.NewReader("name: workflow-1\non:\n push:\njobs:\n job-1:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n"), + }, + { + Operation: "create", + TreePath: ".gitea/workflows/workflow-2.yml", + ContentReader: strings.NewReader("name: workflow-2\non:\n push:\njobs:\n job-2:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n"), + }, + }, + ) + defer f() + + t.Run("valid workflows", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // helpers + getWorkflowRunRedirectURI := func(workflow string) string { + req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/workflows/%s/runs/latest", repo.HTMLURL(), workflow)) + resp := MakeRequest(t, req, http.StatusTemporaryRedirect) + + return resp.Header().Get("Location") + } + + // two runs have been created + assert.Equal(t, 2, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID})) + + // Get the redirect URIs for both workflows + workflowOneURI := getWorkflowRunRedirectURI("workflow-1.yml") + workflowTwoURI := getWorkflowRunRedirectURI("workflow-2.yml") + + // Verify that the two are different. + assert.NotEqual(t, workflowOneURI, workflowTwoURI) + + // Verify that each points to the correct workflow. + workflowOne := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: repo.ID, Index: 1}) + err := workflowOne.LoadAttributes(context.Background()) + assert.NoError(t, err) + assert.Equal(t, workflowOneURI, workflowOne.HTMLURL()) + + workflowTwo := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: repo.ID, Index: 2}) + err = workflowTwo.LoadAttributes(context.Background()) + assert.NoError(t, err) + assert.Equal(t, workflowTwoURI, workflowTwo.HTMLURL()) + }) + + t.Run("existing workflow, non-existent branch", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/workflows/workflow-1.yml/runs/latest?branch=foobar", repo.HTMLURL())) + MakeRequest(t, req, http.StatusNotFound) + }) + + t.Run("non-existing workflow", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/workflows/workflow-3.yml/runs/latest", repo.HTMLURL())) + MakeRequest(t, req, http.StatusNotFound) + }) + }) +} + func TestActionsWebRouteLatestRun(t *testing.T) { onGiteaRun(t, func(t *testing.T, u *url.URL) { user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) @@ -44,7 +118,10 @@ func TestActionsWebRouteLatestRun(t *testing.T) { resp := MakeRequest(t, req, http.StatusTemporaryRedirect) // Verify that it redirects to the run we just created - expectedURI := fmt.Sprintf("%s/actions/runs/1", repo.HTMLURL()) - assert.Equal(t, expectedURI, resp.Header().Get("Location")) + workflow := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: repo.ID}) + err := workflow.LoadAttributes(context.Background()) + assert.NoError(t, err) + + assert.Equal(t, workflow.HTMLURL(), resp.Header().Get("Location")) }) } From 7c1bb0626bfbb3a7501457c833b8f7851a4d5d61 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Tue, 6 Feb 2024 00:23:28 +0000 Subject: [PATCH 003/271] [gitea] [skip ci] Updated translations via Crowdin (cherry picked from commit f69914dd0b5607861f36f89c362f4b2cc3f60403) --- options/locale/locale_zh-CN.ini | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index aab8ebb3fc..74498be9a4 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -1030,10 +1030,10 @@ reactions_more=再加载 %d unit_disabled=站点管理员已禁用此仓库单元。 language_other=其它 adopt_search=输入用户名以搜索未被收录的仓库... (留空以查找全部) -adopt_preexisting_label=收录文件 -adopt_preexisting=收录已存在的文件 +adopt_preexisting_label=收录仓库 +adopt_preexisting=收录已存在的仓库 adopt_preexisting_content=从 %s 创建仓库 -adopt_preexisting_success=从 %s 收录文件并创建仓库成功 +adopt_preexisting_success=从 %s 收录仓库成功 delete_preexisting_label=刪除 delete_preexisting=删除已存在的文件 delete_preexisting_content=删除 %s 中的文件 From a01777fd21eebf85c630e28befb3366b1c169ec0 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 6 Feb 2024 19:57:25 +0800 Subject: [PATCH 004/271] [gitea] Hide code links on release page if user cannot read code (#29064) On the release list page, if the user doesn't have the permission to read code, the code links will lead to 404 pages or api errors: image After this PR: image And this PR also removed some dead code. After #23465, the tag list page has an independent template, and all `IsTag` in the release list template are always false. (cherry picked from commit f9072dbf3c558ba5d4365b551d95936a52e4c94d) --- templates/repo/release/list.tmpl | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index ccea14c4e2..fb2fce2950 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -8,8 +8,8 @@ {{range $idx, $release := .Releases}}
  • - {{svg "octicon-tag" 16 "gt-mr-2"}}{{.TagName}} - {{if .Sha1}} + {{svg "octicon-tag" 16 "gt-mr-2"}}{{.TagName}} + {{if and .Sha1 ($.Permission.CanRead $.UnitTypeCode)}} {{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .Sha1}} {{template "repo/branch_dropdown" dict "root" $ "release" .}} {{end}} @@ -22,36 +22,18 @@ {{ctx.Locale.Tr "repo.release.draft"}} {{else if .IsPrerelease}} {{ctx.Locale.Tr "repo.release.prerelease"}} - {{else if not .IsTag}} + {{else}} {{ctx.Locale.Tr "repo.release.stable"}} {{end}}
    - {{if and $.CanCreateRelease (not .IsTag)}} + {{if $.CanCreateRelease}} {{svg "octicon-pencil"}} {{end}}
    - {{if .IsTag}} -

    - {{if gt .Publisher.ID 0}} - - {{ctx.AvatarUtils.Avatar .Publisher 20 "gt-mr-2"}} - {{.Publisher.Name}} - - - {{ctx.Locale.Tr "repo.tagged_this"}} - - {{if .CreatedUnix}} - {{TimeSinceUnix .CreatedUnix ctx.Locale}} - {{end}} - | - {{end}} - {{ctx.Locale.Tr "repo.release.ahead.commits" .NumCommitsBehind | Str2html}} {{ctx.Locale.Tr "repo.tag.ahead.target" .TargetBehind}} -

    - {{else}}

    {{if .OriginalAuthor}} @@ -69,11 +51,10 @@ {{if .CreatedUnix}} {{TimeSinceUnix .CreatedUnix ctx.Locale}} {{end}} - {{if not .IsDraft}} + {{if and (not .IsDraft) ($.Permission.CanRead $.UnitTypeCode)}} | {{ctx.Locale.Tr "repo.release.ahead.commits" .NumCommitsBehind | Str2html}} {{ctx.Locale.Tr "repo.release.ahead.target" .TargetBehind}} {{end}}

    - {{end}}
    {{Str2html .Note}}
    From 75937b5784be15b9ac18a4a720baacd99def666c Mon Sep 17 00:00:00 2001 From: silverwind Date: Wed, 7 Feb 2024 03:17:59 +0100 Subject: [PATCH 005/271] [gitea] Remove lightningcss (#29070) Remove lightningcss and minify with esbuild again. The size of output CSS will increase by around 1.4%, but I think it's worth it to allow building gitea in more cases like the one in the linked issue. We can reconsider once lightningcss becomes more stable. Fixes: https://github.com/go-gitea/gitea/issues/29058 (cherry picked from commit 5849d4fde347cd1e47f2243b3239724c73b1261d) --- package-lock.json | 302 ++++++---------------------------------------- package.json | 1 - webpack.config.js | 11 +- 3 files changed, 37 insertions(+), 277 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3572749b89..6918dc64b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,7 +30,6 @@ "jquery": "3.7.1", "katex": "0.16.9", "license-checker-webpack-plugin": "0.2.1", - "lightningcss-loader": "2.1.0", "mermaid": "10.7.0", "mini-css-extract-plugin": "2.8.0", "minimatch": "9.0.3", @@ -2261,13 +2260,13 @@ "dev": true }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.20.0.tgz", - "integrity": "sha512-p4rvHQRDTI1tGGMDFQm+GtxP1ZHyAh64WANVoyEcNMpaTFn3ox/3CcgtIlELnRfKzSs/DwYlDccJEtr3O6qBvA==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", + "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0" + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2278,9 +2277,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.20.0.tgz", - "integrity": "sha512-MM9mfZMAhiN4cOEcUOEx+0HmuaW3WBfukBZPCfwSqFnQy0grXYtngKCqpQN339X3RrwtzspWJrpbrupKYUSBXQ==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", + "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -2291,13 +2290,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.20.0.tgz", - "integrity": "sha512-RnRya9q5m6YYSpBN7IzKu9FmLcYtErkDkc8/dKv81I9QiLLtVBHrjz+Ev/crAqgMNW2FCsoZF4g2QUylMnJz+g==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", + "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/visitor-keys": "6.20.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -2319,17 +2318,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.20.0.tgz", - "integrity": "sha512-/EKuw+kRu2vAqCoDwDCBtDRU6CTKbUmwwI7SH7AashZ+W+7o8eiyy6V2cdOqN49KsTcASWsC5QeghYuRDTyOOg==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.20.0", - "@typescript-eslint/types": "6.20.0", - "@typescript-eslint/typescript-estree": "6.20.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "engines": { @@ -2344,12 +2343,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.20.0.tgz", - "integrity": "sha512-E8Cp98kRe4gKHjJD4NExXKz/zOJ1A2hhZc+IMVD6i7w4yjIvh6VyuRI0gRtxAsXtoC35uGMaQ9rjI2zJaXDEAw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", + "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.20.0", + "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2449,9 +2448,9 @@ } }, "node_modules/@vitest/snapshot/node_modules/magic-string": { - "version": "0.30.6", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.6.tgz", - "integrity": "sha512-n62qCLbPjNjyo+owKtveQxZFZTBm+Ms6YoGD23Wew6Vw337PElFNifQpknPruVRQV57kVShPnLGo9vWxVhpPvA==", + "version": "0.30.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", + "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" @@ -4372,17 +4371,6 @@ "node": ">=6" } }, - "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/diff": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", @@ -6993,224 +6981,6 @@ "node": ">=8" } }, - "node_modules/lightningcss": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.23.0.tgz", - "integrity": "sha512-SEArWKMHhqn/0QzOtclIwH5pXIYQOUEkF8DgICd/105O+GCgd7jxjNod/QPnBCSWvpRHQBGVz5fQ9uScby03zA==", - "dependencies": { - "detect-libc": "^1.0.3" - }, - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "lightningcss-darwin-arm64": "1.23.0", - "lightningcss-darwin-x64": "1.23.0", - "lightningcss-freebsd-x64": "1.23.0", - "lightningcss-linux-arm-gnueabihf": "1.23.0", - "lightningcss-linux-arm64-gnu": "1.23.0", - "lightningcss-linux-arm64-musl": "1.23.0", - "lightningcss-linux-x64-gnu": "1.23.0", - "lightningcss-linux-x64-musl": "1.23.0", - "lightningcss-win32-x64-msvc": "1.23.0" - } - }, - "node_modules/lightningcss-darwin-arm64": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.23.0.tgz", - "integrity": "sha512-kl4Pk3Q2lnE6AJ7Qaij47KNEfY2/UXRZBT/zqGA24B8qwkgllr/j7rclKOf1axcslNXvvUdztjo4Xqh39Yq1aA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-darwin-x64": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.23.0.tgz", - "integrity": "sha512-KeRFCNoYfDdcolcFXvokVw+PXCapd2yHS1Diko1z1BhRz/nQuD5XyZmxjWdhmhN/zj5sH8YvWsp0/lPLVzqKpg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-freebsd-x64": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.23.0.tgz", - "integrity": "sha512-xhnhf0bWPuZxcqknvMDRFFo2TInrmQRWZGB0f6YoAsZX8Y+epfjHeeOIGCfAmgF0DgZxHwYc8mIR5tQU9/+ROA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.23.0.tgz", - "integrity": "sha512-fBamf/bULvmWft9uuX+bZske236pUZEoUlaHNBjnueaCTJ/xd8eXgb0cEc7S5o0Nn6kxlauMBnqJpF70Bgq3zg==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.23.0.tgz", - "integrity": "sha512-RS7sY77yVLOmZD6xW2uEHByYHhQi5JYWmgVumYY85BfNoVI3DupXSlzbw+b45A9NnVKq45+oXkiN6ouMMtTwfg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.23.0.tgz", - "integrity": "sha512-cU00LGb6GUXCwof6ACgSMKo3q7XYbsyTj0WsKHLi1nw7pV0NCq8nFTn6ZRBYLoKiV8t+jWl0Hv8KkgymmK5L5g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.23.0.tgz", - "integrity": "sha512-q4jdx5+5NfB0/qMbXbOmuC6oo7caPnFghJbIAV90cXZqgV8Am3miZhC4p+sQVdacqxfd+3nrle4C8icR3p1AYw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-linux-x64-musl": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.23.0.tgz", - "integrity": "sha512-G9Ri3qpmF4qef2CV/80dADHKXRAQeQXpQTLx7AiQrBYQHqBjB75oxqj06FCIe5g4hNCqLPnM9fsO4CyiT1sFSQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/lightningcss-loader": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lightningcss-loader/-/lightningcss-loader-2.1.0.tgz", - "integrity": "sha512-mB+M/lvs/GdXT4yc8ZiNgLUAbYpPI9grDyC3ybz/Zo6s4GZv53iZnLTnkJT/Qm3Sh89dbFUm+omoHFXCfZtcXw==", - "dependencies": { - "browserslist": "^4.21.4", - "lightningcss": "^1.16.0", - "webpack-sources": "^3.2.3" - }, - "peerDependencies": { - "webpack": ">=5" - } - }, - "node_modules/lightningcss-loader/node_modules/webpack-sources": { - "version": "3.2.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", - "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.23.0.tgz", - "integrity": "sha512-1rcBDJLU+obPPJM6qR5fgBUiCdZwZLafZM5f9kwjFLkb/UBNIzmae39uCSmh71nzPCTXZqHbvwu23OWnWEz+eg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", @@ -10620,12 +10390,12 @@ "integrity": "sha512-B5CXihaVzXw+1UHhNFyAwUTMDk1EfoLP5Tj1VhD9yybZ1I8DZJEv8tZ1l0RJo0t0tk9ZhR8eG5tEsaCvRigmdQ==" }, "node_modules/ts-api-utils": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", - "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.2.1.tgz", + "integrity": "sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==", "dev": true, "engines": { - "node": ">=16.13.0" + "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.0" @@ -10769,9 +10539,9 @@ "dev": true }, "node_modules/ufo": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.3.2.tgz", - "integrity": "sha512-o+ORpgGwaYQXgqGDwd+hkS4PuZ3QnmqMMxRuajK/a38L6fTpcE5GPIfrf+L/KemFzfUpeUQc1rRS1iDBozvnFA==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.4.0.tgz", + "integrity": "sha512-Hhy+BhRBleFjpJ2vchUNN40qgkh0366FWJGqVLYBHev0vpHTrXSA0ryT+74UiW6KWsldNurQMKGqCm1M2zBciQ==", "dev": true }, "node_modules/uint8-to-base64": { @@ -11149,9 +10919,9 @@ } }, "node_modules/vitest/node_modules/magic-string": { - "version": "0.30.6", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.6.tgz", - "integrity": "sha512-n62qCLbPjNjyo+owKtveQxZFZTBm+Ms6YoGD23Wew6Vw337PElFNifQpknPruVRQV57kVShPnLGo9vWxVhpPvA==", + "version": "0.30.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", + "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" diff --git a/package.json b/package.json index 3defca1a3d..569955d815 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "jquery": "3.7.1", "katex": "0.16.9", "license-checker-webpack-plugin": "0.2.1", - "lightningcss-loader": "2.1.0", "mermaid": "10.7.0", "mini-css-extract-plugin": "2.8.0", "minimatch": "9.0.3", diff --git a/webpack.config.js b/webpack.config.js index 0071c01834..3779e860d9 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -55,12 +55,6 @@ const filterCssImport = (url, ...args) => { return true; }; -// in case lightningcss fails to load, fall back to esbuild for css minify -let LightningCssMinifyPlugin; -try { - ({LightningCssMinifyPlugin} = await import('lightningcss-loader')); -} catch {} - /** @type {import("webpack").Configuration} */ export default { mode: isProduction ? 'production' : 'development', @@ -110,12 +104,9 @@ export default { new EsbuildPlugin({ target: 'es2020', minify: true, - css: !LightningCssMinifyPlugin, + css: true, legalComments: 'none', }), - LightningCssMinifyPlugin && new LightningCssMinifyPlugin({ - sourceMap: sourceMaps === 'true', - }), ], splitChunks: { chunks: 'async', From 00370f17a4f7cba31e8a2617ebdbff139739d6d4 Mon Sep 17 00:00:00 2001 From: dark-angel <70754989+inferno-umar@users.noreply.github.com> Date: Wed, 7 Feb 2024 14:27:16 +0530 Subject: [PATCH 006/271] [gitea] fix: Elasticsearch: Request Entity Too Large #28117 (#29062) Fix for gitea putting everything into one request without batching and sending it to Elasticsearch for indexing as issued in #28117 This issue occured in large repositories while Gitea tries to index the code using ElasticSearch. I've applied necessary changes that takes batch length from below config (app.ini) ``` [queue.code_indexer] BATCH_LENGTH= ``` and batches all requests to Elasticsearch in chunks as configured in the above config (cherry picked from commit 5c0fc9087211f01375f208d679a1e6de0685320c) --- .../indexer/code/elasticsearch/elasticsearch.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/modules/indexer/code/elasticsearch/elasticsearch.go b/modules/indexer/code/elasticsearch/elasticsearch.go index 2fadbfeb06..0f70f13485 100644 --- a/modules/indexer/code/elasticsearch/elasticsearch.go +++ b/modules/indexer/code/elasticsearch/elasticsearch.go @@ -180,11 +180,17 @@ func (b *Indexer) Index(ctx context.Context, repo *repo_model.Repository, sha st } if len(reqs) > 0 { - _, err := b.inner.Client.Bulk(). - Index(b.inner.VersionedIndexName()). - Add(reqs...). - Do(ctx) - return err + esBatchSize := 50 + + for i := 0; i < len(reqs); i += esBatchSize { + _, err := b.inner.Client.Bulk(). + Index(b.inner.VersionedIndexName()). + Add(reqs[i:min(i+esBatchSize, len(reqs))]...). + Do(ctx) + if err != nil { + return err + } + } } return nil } From 73e32eae5ba65b4f16e0c4a44f40eacbf572db3e Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 8 Feb 2024 03:37:09 +0100 Subject: [PATCH 007/271] [gitea] Fix gitea-origin-url with default ports (#29085) When setting `url.host` on a URL object with no port specified (like is the case of default port), the resulting URL's port will not change. Workaround this quirk in the URL standard by explicitely setting port for the http and https protocols. Extracted the logic to a function for the purpose of testing. Initially I wanted to have the function in utils.js, but it turns out esbuild can not treeshake the unused functions which would result in the webcomponents chunk having all 2kB utils.js inlined, so it seemed not worth. Fixes: https://github.com/go-gitea/gitea/issues/29084 (cherry picked from commit b6bf8041d8e8ee845728687b1f358f1d482afff2) --- .eslintrc.yaml | 2 +- web_src/js/webcomponents/GiteaOriginUrl.js | 28 +++++++++++-------- .../js/webcomponents/GiteaOriginUrl.test.js | 17 +++++++++++ 3 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 web_src/js/webcomponents/GiteaOriginUrl.test.js diff --git a/.eslintrc.yaml b/.eslintrc.yaml index 78042a7598..fc6f38ec53 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -811,7 +811,7 @@ rules: wc/no-constructor-params: [2] wc/no-constructor: [2] wc/no-customized-built-in-elements: [2] - wc/no-exports-with-element: [2] + wc/no-exports-with-element: [0] wc/no-invalid-element-name: [2] wc/no-invalid-extends: [2] wc/no-method-prefixed-with-on: [2] diff --git a/web_src/js/webcomponents/GiteaOriginUrl.js b/web_src/js/webcomponents/GiteaOriginUrl.js index fca736064c..5d71d95c60 100644 --- a/web_src/js/webcomponents/GiteaOriginUrl.js +++ b/web_src/js/webcomponents/GiteaOriginUrl.js @@ -1,17 +1,21 @@ // Convert an absolute or relative URL to an absolute URL with the current origin +export function toOriginUrl(urlStr) { + try { + // only process absolute HTTP/HTTPS URL or relative URLs ('/xxx' or '//host/xxx') + if (urlStr.startsWith('http://') || urlStr.startsWith('https://') || urlStr.startsWith('/')) { + const {origin, protocol, hostname, port} = window.location; + const url = new URL(urlStr, origin); + url.protocol = protocol; + url.hostname = hostname; + url.port = port || (protocol === 'https:' ? '443' : '80'); + return url.toString(); + } + } catch {} + return urlStr; +} + window.customElements.define('gitea-origin-url', class extends HTMLElement { connectedCallback() { - const urlStr = this.getAttribute('data-url'); - try { - // only process absolute HTTP/HTTPS URL or relative URLs ('/xxx' or '//host/xxx') - if (urlStr.startsWith('http://') || urlStr.startsWith('https://') || urlStr.startsWith('/')) { - const url = new URL(urlStr, window.origin); - url.protocol = window.location.protocol; - url.host = window.location.host; - this.textContent = url.toString(); - return; - } - } catch {} - this.textContent = urlStr; + this.textContent = toOriginUrl(this.getAttribute('data-url')); } }); diff --git a/web_src/js/webcomponents/GiteaOriginUrl.test.js b/web_src/js/webcomponents/GiteaOriginUrl.test.js new file mode 100644 index 0000000000..f0629842b8 --- /dev/null +++ b/web_src/js/webcomponents/GiteaOriginUrl.test.js @@ -0,0 +1,17 @@ +import {toOriginUrl} from './GiteaOriginUrl.js'; + +test('toOriginUrl', () => { + const oldLocation = window.location; + for (const origin of ['https://example.com', 'https://example.com:3000']) { + window.location = new URL(`${origin}/`); + expect(toOriginUrl('/')).toEqual(`${origin}/`); + expect(toOriginUrl('/org/repo.git')).toEqual(`${origin}/org/repo.git`); + expect(toOriginUrl('https://another.com')).toEqual(`${origin}/`); + expect(toOriginUrl('https://another.com/')).toEqual(`${origin}/`); + expect(toOriginUrl('https://another.com/org/repo.git')).toEqual(`${origin}/org/repo.git`); + expect(toOriginUrl('https://another.com:4000')).toEqual(`${origin}/`); + expect(toOriginUrl('https://another.com:4000/')).toEqual(`${origin}/`); + expect(toOriginUrl('https://another.com:4000/org/repo.git')).toEqual(`${origin}/org/repo.git`); + } + window.location = oldLocation; +}); From 51c610319517e2ead0f76ea262e211f91bc46b28 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Thu, 8 Feb 2024 10:42:18 +0800 Subject: [PATCH 008/271] [gitea] Avoid showing unnecessary JS errors when there are elements with different origin on the page (#29081) Try to fix #29080 (cherry picked from commit f290c24d286b996ac6b512f49a30aa5aef1a2dbe) --- web_src/js/modules/dirauto.js | 5 +++-- web_src/js/modules/tippy.js | 12 +++++------- web_src/js/utils/dom.js | 11 +++++++++++ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/web_src/js/modules/dirauto.js b/web_src/js/modules/dirauto.js index c917bf8cff..cd90f8155b 100644 --- a/web_src/js/modules/dirauto.js +++ b/web_src/js/modules/dirauto.js @@ -1,5 +1,6 @@ -// for performance considerations, it only uses performant syntax +import {isDocumentFragmentOrElementNode} from '../utils/dom.js'; +// for performance considerations, it only uses performant syntax function attachDirAuto(el) { if (el.type !== 'hidden' && el.type !== 'checkbox' && @@ -18,7 +19,7 @@ export function initDirAuto() { const len = mutation.addedNodes.length; for (let i = 0; i < len; i++) { const addedNode = mutation.addedNodes[i]; - if (addedNode.nodeType !== Node.ELEMENT_NODE && addedNode.nodeType !== Node.DOCUMENT_FRAGMENT_NODE) continue; + if (!isDocumentFragmentOrElementNode(addedNode)) continue; if (addedNode.nodeName === 'INPUT' || addedNode.nodeName === 'TEXTAREA') attachDirAuto(addedNode); const children = addedNode.querySelectorAll('input, textarea'); const len = children.length; diff --git a/web_src/js/modules/tippy.js b/web_src/js/modules/tippy.js index 7f8423e319..27f371fd88 100644 --- a/web_src/js/modules/tippy.js +++ b/web_src/js/modules/tippy.js @@ -1,4 +1,5 @@ import tippy, {followCursor} from 'tippy.js'; +import {isDocumentFragmentOrElementNode} from '../utils/dom.js'; const visibleInstances = new Set(); @@ -136,8 +137,6 @@ function attachChildrenLazyTooltip(target) { } } -const elementNodeTypes = new Set([Node.ELEMENT_NODE, Node.DOCUMENT_FRAGMENT_NODE]); - export function initGlobalTooltips() { // use MutationObserver to detect new "data-tooltip-content" elements added to the DOM, or attributes changed const observerConnect = (observer) => observer.observe(document, { @@ -152,11 +151,10 @@ export function initGlobalTooltips() { if (mutation.type === 'childList') { // mainly for Vue components and AJAX rendered elements for (const el of mutation.addedNodes) { - if (elementNodeTypes.has(el.nodeType)) { - attachChildrenLazyTooltip(el); - if (el.hasAttribute('data-tooltip-content')) { - attachLazyTooltip(el); - } + if (!isDocumentFragmentOrElementNode(el)) continue; + attachChildrenLazyTooltip(el); + if (el.hasAttribute('data-tooltip-content')) { + attachLazyTooltip(el); } } } else if (mutation.type === 'attributes') { diff --git a/web_src/js/utils/dom.js b/web_src/js/utils/dom.js index 64a6a5affc..4dc55a518a 100644 --- a/web_src/js/utils/dom.js +++ b/web_src/js/utils/dom.js @@ -59,6 +59,17 @@ export function onDomReady(cb) { } } +// checks whether an element is owned by the current document, and whether it is a document fragment or element node +// if it is, it means it is a "normal" element managed by us, which can be modified safely. +export function isDocumentFragmentOrElementNode(el) { + try { + return el.ownerDocument === document && el.nodeType === Node.ELEMENT_NODE || el.nodeType === Node.DOCUMENT_FRAGMENT_NODE; + } catch { + // in case the el is not in the same origin, then the access to nodeType would fail + return false; + } +} + // autosize a textarea to fit content. Based on // https://github.com/github/textarea-autosize // --------------------------------------------------------------------- From 34450f7d35ed88750b768dcccf2b06453e062651 Mon Sep 17 00:00:00 2001 From: Wiktor Kwapisiewicz Date: Thu, 8 Feb 2024 04:09:15 +0100 Subject: [PATCH 009/271] [gitea] Fix missing link on outgoing new release notifications (#29079) Outgoing new release e-mail notifications were missing links to the actual release. An example from Codeberg.org e-mail: View it on Codeberg.org.
    This PR adds `"Link"` context property pointing to the release on the web interface. The change was tested using `[mailer] PROTOCOL=dummy`. Signed-off-by: Wiktor Kwapisiewicz (cherry picked from commit 37191dcfbdbd007266a4d15a3c85cdf94cec1a7a) --- services/mailer/mail_release.go | 1 + 1 file changed, 1 insertion(+) diff --git a/services/mailer/mail_release.go b/services/mailer/mail_release.go index 801c2476c2..5e8e5b6af3 100644 --- a/services/mailer/mail_release.go +++ b/services/mailer/mail_release.go @@ -74,6 +74,7 @@ func mailNewRelease(ctx context.Context, lang string, tos []string, rel *repo_mo "Release": rel, "Subject": subject, "Language": locale.Language(), + "Link": rel.HTMLURL(), } var mailBody bytes.Buffer From e87ab053e7f35971d366fb01ee787cfc5e28390c Mon Sep 17 00:00:00 2001 From: CEnnis91 Date: Thu, 8 Feb 2024 01:45:44 -0500 Subject: [PATCH 010/271] [gitea] Fix swift packages not resolving (#29095) Fixes #29094 (cherry picked from commit 6be3fda6fc746b1285e6dee72de1118177ba429e) --- routers/api/packages/swift/swift.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/routers/api/packages/swift/swift.go b/routers/api/packages/swift/swift.go index 427e262d06..6ad289e51e 100644 --- a/routers/api/packages/swift/swift.go +++ b/routers/api/packages/swift/swift.go @@ -157,7 +157,7 @@ func EnumeratePackageVersions(ctx *context.Context) { } type Resource struct { - Name string `json:"id"` + Name string `json:"name"` Type string `json:"type"` Checksum string `json:"checksum"` } From 4b1b774ccd0133f58eb29668bc19daf6834b77a0 Mon Sep 17 00:00:00 2001 From: CEnnis91 Date: Thu, 8 Feb 2024 03:53:44 -0500 Subject: [PATCH 011/271] [gitea] Fix incorrect link to swift doc and swift package-registry login command (#29096) Fixes a few mistakes in the Swift package registry documentation. Syntax for the `package-registry login` command can be found [here](https://github.com/apple/swift-package-manager/blob/main/Documentation/PackageRegistry/PackageRegistryUsage.md#registry-authentication). I was not sure the best way to compress all of that information, so I just focused on making sure the incorrect `package-registry set` command was fixed. (cherry picked from commit eb5ddc0a78ecfe007a6e279a3c59711cdfb3f701) --- docs/content/usage/packages/overview.en-us.md | 2 +- docs/content/usage/packages/swift.en-us.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/content/usage/packages/overview.en-us.md b/docs/content/usage/packages/overview.en-us.md index 44d18ff482..89fc6f286e 100644 --- a/docs/content/usage/packages/overview.en-us.md +++ b/docs/content/usage/packages/overview.en-us.md @@ -42,7 +42,7 @@ The following package managers are currently supported: | [PyPI](usage/packages/pypi.md) | Python | `pip`, `twine` | | [RPM](usage/packages/rpm.md) | - | `yum`, `dnf`, `zypper` | | [RubyGems](usage/packages/rubygems.md) | Ruby | `gem`, `Bundler` | -| [Swift](usage/packages/rubygems.md) | Swift | `swift` | +| [Swift](usage/packages/swift.md) | Swift | `swift` | | [Vagrant](usage/packages/vagrant.md) | - | `vagrant` | **The following paragraphs only apply if Packages are not globally disabled!** diff --git a/docs/content/usage/packages/swift.en-us.md b/docs/content/usage/packages/swift.en-us.md index 606fa20b36..38eb155641 100644 --- a/docs/content/usage/packages/swift.en-us.md +++ b/docs/content/usage/packages/swift.en-us.md @@ -26,7 +26,8 @@ To work with the Swift package registry, you need to use [swift](https://www.swi To register the package registry and provide credentials, execute: ```shell -swift package-registry set https://gitea.example.com/api/packages/{owner}/swift -login {username} -password {password} +swift package-registry set https://gitea.example.com/api/packages/{owner}/swift +swift package-registry login https://gitea.example.com/api/packages/{owner}/swift --username {username} --password {password} ``` | Placeholder | Description | From 02b03247de311f57a77827d22e459f56ac4d110f Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 8 Feb 2024 13:07:02 +0100 Subject: [PATCH 012/271] [gitea] Move vitest setup file to root (#29097) I'm using this convention in other projects and I think it makes sense for gitea too because the vitest setup file is loaded globally for all tests, not just ones in web_src, so it makes sense to be in the root. (cherry picked from commit 98e7e3a5f07b8bc620e26bc1ab6f7a86bccbb7cb) --- vitest.config.js | 2 +- web_src/js/{test/setup.js => vitest.setup.js} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename web_src/js/{test/setup.js => vitest.setup.js} (100%) diff --git a/vitest.config.js b/vitest.config.js index 9a6cb4e560..be6c0eadfa 100644 --- a/vitest.config.js +++ b/vitest.config.js @@ -5,7 +5,7 @@ import {stringPlugin} from 'vite-string-plugin'; export default defineConfig({ test: { include: ['web_src/**/*.test.js'], - setupFiles: ['./web_src/js/test/setup.js'], + setupFiles: ['web_src/js/vitest.setup.js'], environment: 'jsdom', testTimeout: 20000, open: false, diff --git a/web_src/js/test/setup.js b/web_src/js/vitest.setup.js similarity index 100% rename from web_src/js/test/setup.js rename to web_src/js/vitest.setup.js From 1fdd8341d662e67cddf0688fa766610d3dcaf0ed Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 8 Feb 2024 20:31:38 +0800 Subject: [PATCH 013/271] [gitea] Remove unnecessary parameter (#29092) The parameter extraConfigs has never been used anywhere. This PR just removed it. It can be taken back once it's needed. (cherry picked from commit 8c6ffdac378654f9d2171ebdbc46becf1571f7fe) --- models/unittest/testdb.go | 4 ++-- modules/setting/config_provider.go | 8 +------- modules/setting/setting.go | 4 ++-- 3 files changed, 5 insertions(+), 11 deletions(-) diff --git a/models/unittest/testdb.go b/models/unittest/testdb.go index 69f3505c28..af5c31f157 100644 --- a/models/unittest/testdb.go +++ b/models/unittest/testdb.go @@ -44,12 +44,12 @@ func fatalTestError(fmtStr string, args ...any) { } // InitSettings initializes config provider and load common settings for tests -func InitSettings(extraConfigs ...string) { +func InitSettings() { if setting.CustomConf == "" { setting.CustomConf = filepath.Join(setting.CustomPath, "conf/app-unittest-tmp.ini") _ = os.Remove(setting.CustomConf) } - setting.InitCfgProvider(setting.CustomConf, strings.Join(extraConfigs, "\n")) + setting.InitCfgProvider(setting.CustomConf) setting.LoadCommonSettings() if err := setting.PrepareAppDataPath(); err != nil { diff --git a/modules/setting/config_provider.go b/modules/setting/config_provider.go index e1c25ed9c7..12cf36aa59 100644 --- a/modules/setting/config_provider.go +++ b/modules/setting/config_provider.go @@ -197,7 +197,7 @@ func NewConfigProviderFromData(configContent string) (ConfigProvider, error) { // NewConfigProviderFromFile load configuration from file. // NOTE: do not print any log except error. -func NewConfigProviderFromFile(file string, extraConfigs ...string) (ConfigProvider, error) { +func NewConfigProviderFromFile(file string) (ConfigProvider, error) { cfg := ini.Empty(configProviderLoadOptions()) loadedFromEmpty := true @@ -214,12 +214,6 @@ func NewConfigProviderFromFile(file string, extraConfigs ...string) (ConfigProvi } } - for _, s := range extraConfigs { - if err := cfg.Append([]byte(s)); err != nil { - return nil, fmt.Errorf("unable to append more config: %v", err) - } - } - cfg.NameMapper = ini.SnackCase return &iniConfigProvider{ file: file, diff --git a/modules/setting/setting.go b/modules/setting/setting.go index c0d8d0ee23..87e7b08c6e 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -92,9 +92,9 @@ func PrepareAppDataPath() error { return nil } -func InitCfgProvider(file string, extraConfigs ...string) { +func InitCfgProvider(file string) { var err error - if CfgProvider, err = NewConfigProviderFromFile(file, extraConfigs...); err != nil { + if CfgProvider, err = NewConfigProviderFromFile(file); err != nil { log.Fatal("Unable to init config provider from %q: %v", file, err) } CfgProvider.DisableSaving() // do not allow saving the CfgProvider into file, it will be polluted by the "MustXxx" calls From b9f48332c781377ecdb2a1eb99c950e563b7211d Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Thu, 8 Feb 2024 21:25:09 +0800 Subject: [PATCH 014/271] [gitea] Fix push to create with capitalize repo name (#29090) Fix #29073 (cherry picked from commit 96ad1d6340038c0c841d9cad9a440daee3241aac) --- cmd/serv.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cmd/serv.go b/cmd/serv.go index e014877bc6..ac754ee12f 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -216,16 +216,18 @@ func runServ(c *cli.Context) error { } } - // LowerCase and trim the repoPath as that's how they are stored. - repoPath = strings.ToLower(strings.TrimSpace(repoPath)) - rr := strings.SplitN(repoPath, "/", 2) if len(rr) != 2 { return fail(ctx, "Invalid repository path", "Invalid repository path: %v", repoPath) } - username := strings.ToLower(rr[0]) - reponame := strings.ToLower(strings.TrimSuffix(rr[1], ".git")) + username := rr[0] + reponame := strings.TrimSuffix(rr[1], ".git") + + // LowerCase and trim the repoPath as that's how they are stored. + // This should be done after splitting the repoPath into username and reponame + // so that username and reponame are not affected. + repoPath = strings.ToLower(strings.TrimSpace(repoPath)) if alphaDashDotPattern.MatchString(reponame) { return fail(ctx, "Invalid repo name", "Invalid repo name: %s", reponame) From 0d62b52f4fb397c1a618dcba1e0b3c9c63c54c2d Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 8 Feb 2024 14:49:44 +0100 Subject: [PATCH 015/271] [gitea] Use defaults browserslist (#29098) IE usage has dropped enough to not be included in the defaults browserslist anymore as per https://browsersl.ist/#q=defaults, so we can use the defaults now. (cherry picked from commit ce9978bfd4e035ed065b02b28e02905674320b6a) --- package.json | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/package.json b/package.json index 569955d815..ef1fcca545 100644 --- a/package.json +++ b/package.json @@ -85,8 +85,6 @@ "vitest": "1.2.2" }, "browserslist": [ - "defaults", - "not ie > 0", - "not ie_mob > 0" + "defaults" ] } From 045bd097c6a12da32ba421d4002743c88edf280d Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Thu, 8 Feb 2024 23:01:19 +0100 Subject: [PATCH 016/271] [gitea] Display friendly error message (#29105) `ctx.Error` only displays the text but `ctx.ServerError` renders the usual error page. (cherry picked from commit da2f03750f9672c5aac48209539874f2af2673f1) --- routers/web/repo/actions/actions.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/routers/web/repo/actions/actions.go b/routers/web/repo/actions/actions.go index fe528a483b..5f6a1ec36a 100644 --- a/routers/web/repo/actions/actions.go +++ b/routers/web/repo/actions/actions.go @@ -61,17 +61,17 @@ func List(ctx *context.Context) { var workflows []Workflow if empty, err := ctx.Repo.GitRepo.IsEmpty(); err != nil { - ctx.Error(http.StatusInternalServerError, err.Error()) + ctx.ServerError("IsEmpty", err) return } else if !empty { commit, err := ctx.Repo.GitRepo.GetBranchCommit(ctx.Repo.Repository.DefaultBranch) if err != nil { - ctx.Error(http.StatusInternalServerError, err.Error()) + ctx.ServerError("GetBranchCommit", err) return } entries, err := actions.ListWorkflows(commit) if err != nil { - ctx.Error(http.StatusInternalServerError, err.Error()) + ctx.ServerError("ListWorkflows", err) return } @@ -95,7 +95,7 @@ func List(ctx *context.Context) { workflow := Workflow{Entry: *entry} content, err := actions.GetContentFromEntry(entry) if err != nil { - ctx.Error(http.StatusInternalServerError, err.Error()) + ctx.ServerError("GetContentFromEntry", err) return } wf, err := model.ReadWorkflow(bytes.NewReader(content)) @@ -172,7 +172,7 @@ func List(ctx *context.Context) { runs, total, err := db.FindAndCount[actions_model.ActionRun](ctx, opts) if err != nil { - ctx.Error(http.StatusInternalServerError, err.Error()) + ctx.ServerError("FindAndCount", err) return } @@ -181,7 +181,7 @@ func List(ctx *context.Context) { } if err := actions_model.RunList(runs).LoadTriggerUser(ctx); err != nil { - ctx.Error(http.StatusInternalServerError, err.Error()) + ctx.ServerError("LoadTriggerUser", err) return } @@ -189,7 +189,7 @@ func List(ctx *context.Context) { actors, err := actions_model.GetActors(ctx, ctx.Repo.Repository.ID) if err != nil { - ctx.Error(http.StatusInternalServerError, err.Error()) + ctx.ServerError("GetActors", err) return } ctx.Data["Actors"] = repo.MakeSelfOnTop(ctx.Doer, actors) From 83bb3cf86a349cbda676fd12f30bf94df01ea33b Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Fri, 9 Feb 2024 11:02:53 +0800 Subject: [PATCH 017/271] [gitea] Refactor parseSignatureFromCommitLine (#29054) Replace #28849. Thanks to @yp05327 for the looking into the problem. Fix #28840 The old behavior of newSignatureFromCommitline is not right. The new parseSignatureFromCommitLine: 1. never fails 2. only accept one format (if there is any other, it could be easily added) And add some tests. (cherry picked from commit a24e1da7e9e38fc5f5c84c083d122c0cc3da4b74) --- modules/git/repo.go | 2 +- modules/git/repo_tag.go | 6 +-- modules/git/repo_tag_test.go | 17 ++----- modules/git/signature.go | 45 +++++++++++++++-- modules/git/signature_gogit.go | 44 ----------------- modules/git/signature_nogogit.go | 84 +++----------------------------- modules/git/signature_test.go | 47 ++++++++++++++++++ modules/git/tag.go | 8 ++- 8 files changed, 104 insertions(+), 149 deletions(-) create mode 100644 modules/git/signature_test.go diff --git a/modules/git/repo.go b/modules/git/repo.go index db99d285a8..60078f3273 100644 --- a/modules/git/repo.go +++ b/modules/git/repo.go @@ -271,7 +271,7 @@ func GetLatestCommitTime(ctx context.Context, repoPath string) (time.Time, error return time.Time{}, err } commitTime := strings.TrimSpace(stdout) - return time.Parse(GitTimeLayout, commitTime) + return time.Parse("Mon Jan _2 15:04:05 2006 -0700", commitTime) } // DivergeObject represents commit count diverging commits diff --git a/modules/git/repo_tag.go b/modules/git/repo_tag.go index af9a75b29c..ae5dbd171f 100644 --- a/modules/git/repo_tag.go +++ b/modules/git/repo_tag.go @@ -183,11 +183,7 @@ func parseTagRef(objectFormat ObjectFormat, ref map[string]string) (tag *Tag, er } } - tag.Tagger, err = newSignatureFromCommitline([]byte(ref["creator"])) - if err != nil { - return nil, fmt.Errorf("parse tagger: %w", err) - } - + tag.Tagger = parseSignatureFromCommitLine(ref["creator"]) tag.Message = ref["contents"] // strip PGP signature if present in contents field pgpStart := strings.Index(tag.Message, beginpgp) diff --git a/modules/git/repo_tag_test.go b/modules/git/repo_tag_test.go index da7b1455a8..9816e311a8 100644 --- a/modules/git/repo_tag_test.go +++ b/modules/git/repo_tag_test.go @@ -227,7 +227,7 @@ func TestRepository_parseTagRef(t *testing.T) { ID: MustIDFromString("ab23e4b7f4cd0caafe0174c0e7ef6d651ba72889"), Object: MustIDFromString("ab23e4b7f4cd0caafe0174c0e7ef6d651ba72889"), Type: "commit", - Tagger: parseAuthorLine(t, "Foo Bar 1565789218 +0300"), + Tagger: parseSignatureFromCommitLine("Foo Bar 1565789218 +0300"), Message: "Add changelog of v1.9.1 (#7859)\n\n* add changelog of v1.9.1\n* Update CHANGELOG.md\n", Signature: nil, }, @@ -256,7 +256,7 @@ func TestRepository_parseTagRef(t *testing.T) { ID: MustIDFromString("8c68a1f06fc59c655b7e3905b159d761e91c53c9"), Object: MustIDFromString("3325fd8a973321fd59455492976c042dde3fd1ca"), Type: "tag", - Tagger: parseAuthorLine(t, "Foo Bar 1565789218 +0300"), + Tagger: parseSignatureFromCommitLine("Foo Bar 1565789218 +0300"), Message: "Add changelog of v1.9.1 (#7859)\n\n* add changelog of v1.9.1\n* Update CHANGELOG.md\n", Signature: nil, }, @@ -314,7 +314,7 @@ qbHDASXl ID: MustIDFromString("8c68a1f06fc59c655b7e3905b159d761e91c53c9"), Object: MustIDFromString("3325fd8a973321fd59455492976c042dde3fd1ca"), Type: "tag", - Tagger: parseAuthorLine(t, "Foo Bar 1565789218 +0300"), + Tagger: parseSignatureFromCommitLine("Foo Bar 1565789218 +0300"), Message: "Add changelog of v1.9.1 (#7859)\n\n* add changelog of v1.9.1\n* Update CHANGELOG.md", Signature: &CommitGPGSignature{ Signature: `-----BEGIN PGP SIGNATURE----- @@ -363,14 +363,3 @@ Add changelog of v1.9.1 (#7859) }) } } - -func parseAuthorLine(t *testing.T, committer string) *Signature { - t.Helper() - - sig, err := newSignatureFromCommitline([]byte(committer)) - if err != nil { - t.Fatalf("parse author line '%s': %v", committer, err) - } - - return sig -} diff --git a/modules/git/signature.go b/modules/git/signature.go index b5b17f23b0..f50a097758 100644 --- a/modules/git/signature.go +++ b/modules/git/signature.go @@ -4,7 +4,46 @@ package git -const ( - // GitTimeLayout is the (default) time layout used by git. - GitTimeLayout = "Mon Jan _2 15:04:05 2006 -0700" +import ( + "strconv" + "strings" + "time" + + "code.gitea.io/gitea/modules/log" ) + +// Helper to get a signature from the commit line, which looks like: +// +// full name 1378823654 +0200 +// +// Haven't found the official reference for the standard format yet. +// This function never fails, if the "line" can't be parsed, it returns a default Signature with "zero" time. +func parseSignatureFromCommitLine(line string) *Signature { + sig := &Signature{} + s1, sx, ok1 := strings.Cut(line, " <") + s2, s3, ok2 := strings.Cut(sx, "> ") + if !ok1 || !ok2 { + sig.Name = line + return sig + } + sig.Name, sig.Email = s1, s2 + + if strings.Count(s3, " ") == 1 { + ts, tz, _ := strings.Cut(s3, " ") + seconds, _ := strconv.ParseInt(ts, 10, 64) + if tzTime, err := time.Parse("-0700", tz); err == nil { + sig.When = time.Unix(seconds, 0).In(tzTime.Location()) + } + } else { + // the old gitea code tried to parse the date in a few different formats, but it's not clear why. + // according to public document, only the standard format "timestamp timezone" could be found, so drop other formats. + log.Error("suspicious commit line format: %q", line) + for _, fmt := range []string{ /*"Mon Jan _2 15:04:05 2006 -0700"*/ } { + if t, err := time.Parse(fmt, s3); err == nil { + sig.When = t + break + } + } + } + return sig +} diff --git a/modules/git/signature_gogit.go b/modules/git/signature_gogit.go index c984ad6e20..1fc6aabceb 100644 --- a/modules/git/signature_gogit.go +++ b/modules/git/signature_gogit.go @@ -7,52 +7,8 @@ package git import ( - "bytes" - "strconv" - "strings" - "time" - "github.com/go-git/go-git/v5/plumbing/object" ) // Signature represents the Author or Committer information. type Signature = object.Signature - -// Helper to get a signature from the commit line, which looks like these: -// -// author Patrick Gundlach 1378823654 +0200 -// author Patrick Gundlach Thu, 07 Apr 2005 22:13:13 +0200 -// -// but without the "author " at the beginning (this method should) -// be used for author and committer. -// -// FIXME: include timezone for timestamp! -func newSignatureFromCommitline(line []byte) (_ *Signature, err error) { - sig := new(Signature) - emailStart := bytes.IndexByte(line, '<') - if emailStart > 0 { // Empty name has already occurred, even if it shouldn't - sig.Name = strings.TrimSpace(string(line[:emailStart-1])) - } - emailEnd := bytes.IndexByte(line, '>') - sig.Email = string(line[emailStart+1 : emailEnd]) - - // Check date format. - if len(line) > emailEnd+2 { - firstChar := line[emailEnd+2] - if firstChar >= 48 && firstChar <= 57 { - timestop := bytes.IndexByte(line[emailEnd+2:], ' ') - timestring := string(line[emailEnd+2 : emailEnd+2+timestop]) - seconds, _ := strconv.ParseInt(timestring, 10, 64) - sig.When = time.Unix(seconds, 0) - } else { - sig.When, err = time.Parse(GitTimeLayout, string(line[emailEnd+2:])) - if err != nil { - return nil, err - } - } - } else { - // Fall back to unix 0 time - sig.When = time.Unix(0, 0) - } - return sig, nil -} diff --git a/modules/git/signature_nogogit.go b/modules/git/signature_nogogit.go index 25277f99d5..0d19c0abdc 100644 --- a/modules/git/signature_nogogit.go +++ b/modules/git/signature_nogogit.go @@ -7,21 +7,17 @@ package git import ( - "bytes" "fmt" - "strconv" - "strings" "time" + + "code.gitea.io/gitea/modules/util" ) -// Signature represents the Author or Committer information. +// Signature represents the Author, Committer or Tagger information. type Signature struct { - // Name represents a person name. It is an arbitrary string. - Name string - // Email is an email, but it cannot be assumed to be well-formed. - Email string - // When is the timestamp of the signature. - When time.Time + Name string // the committer name, it can be anything + Email string // the committer email, it can be anything + When time.Time // the timestamp of the signature } func (s *Signature) String() string { @@ -30,71 +26,5 @@ func (s *Signature) String() string { // Decode decodes a byte array representing a signature to signature func (s *Signature) Decode(b []byte) { - sig, _ := newSignatureFromCommitline(b) - s.Email = sig.Email - s.Name = sig.Name - s.When = sig.When -} - -// Helper to get a signature from the commit line, which looks like these: -// -// author Patrick Gundlach 1378823654 +0200 -// author Patrick Gundlach Thu, 07 Apr 2005 22:13:13 +0200 -// -// but without the "author " at the beginning (this method should) -// be used for author and committer. -// FIXME: there are a lot of "return sig, err" (but the err is also nil), that's the old behavior, to avoid breaking -func newSignatureFromCommitline(line []byte) (sig *Signature, err error) { - sig = new(Signature) - emailStart := bytes.LastIndexByte(line, '<') - emailEnd := bytes.LastIndexByte(line, '>') - if emailStart == -1 || emailEnd == -1 || emailEnd < emailStart { - return sig, err - } - - if emailStart > 0 { // Empty name has already occurred, even if it shouldn't - sig.Name = strings.TrimSpace(string(line[:emailStart-1])) - } - sig.Email = string(line[emailStart+1 : emailEnd]) - - hasTime := emailEnd+2 < len(line) - if !hasTime { - return sig, err - } - - // Check date format. - firstChar := line[emailEnd+2] - if firstChar >= 48 && firstChar <= 57 { - idx := bytes.IndexByte(line[emailEnd+2:], ' ') - if idx < 0 { - return sig, err - } - - timestring := string(line[emailEnd+2 : emailEnd+2+idx]) - seconds, _ := strconv.ParseInt(timestring, 10, 64) - sig.When = time.Unix(seconds, 0) - - idx += emailEnd + 3 - if idx >= len(line) || idx+5 > len(line) { - return sig, err - } - - timezone := string(line[idx : idx+5]) - tzhours, err1 := strconv.ParseInt(timezone[0:3], 10, 64) - tzmins, err2 := strconv.ParseInt(timezone[3:], 10, 64) - if err1 != nil || err2 != nil { - return sig, err - } - if tzhours < 0 { - tzmins *= -1 - } - tz := time.FixedZone("", int(tzhours*60*60+tzmins*60)) - sig.When = sig.When.In(tz) - } else { - sig.When, err = time.Parse(GitTimeLayout, string(line[emailEnd+2:])) - if err != nil { - return sig, err - } - } - return sig, err + *s = *parseSignatureFromCommitLine(util.UnsafeBytesToString(b)) } diff --git a/modules/git/signature_test.go b/modules/git/signature_test.go new file mode 100644 index 0000000000..92681feea9 --- /dev/null +++ b/modules/git/signature_test.go @@ -0,0 +1,47 @@ +// Copyright 2019 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package git + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestParseSignatureFromCommitLine(t *testing.T) { + tests := []struct { + line string + want *Signature + }{ + { + line: "a b 12345 +0100", + want: &Signature{ + Name: "a b", + Email: "c@d.com", + When: time.Unix(12345, 0).In(time.FixedZone("", 3600)), + }, + }, + { + line: "bad line", + want: &Signature{Name: "bad line"}, + }, + { + line: "bad < line", + want: &Signature{Name: "bad < line"}, + }, + { + line: "bad > line", + want: &Signature{Name: "bad > line"}, + }, + { + line: "bad-line ", + want: &Signature{Name: "bad-line "}, + }, + } + for _, test := range tests { + got := parseSignatureFromCommitLine(test.line) + assert.EqualValues(t, test.want, got) + } +} diff --git a/modules/git/tag.go b/modules/git/tag.go index 01a8d6f6a5..94e5cd7c63 100644 --- a/modules/git/tag.go +++ b/modules/git/tag.go @@ -7,6 +7,8 @@ import ( "bytes" "sort" "strings" + + "code.gitea.io/gitea/modules/util" ) const ( @@ -59,11 +61,7 @@ l: // A commit can have one or more parents tag.Type = string(line[spacepos+1:]) case "tagger": - sig, err := newSignatureFromCommitline(line[spacepos+1:]) - if err != nil { - return nil, err - } - tag.Tagger = sig + tag.Tagger = parseSignatureFromCommitLine(util.UnsafeBytesToString(line[spacepos+1:])) } nextline += eol + 1 case eol == 0: From 6fcb8e652ba7ac69b12a8990df50274188af3571 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 9 Feb 2024 04:59:39 +0100 Subject: [PATCH 018/271] [gitea] Rework spellchecking, add `lint-spell` (#29106) - Use maintained fork https://github.com/golangci/misspell - Rename `mispell-check` to `lint-spell`, add `lint-spell-fix` - Run `lint-spell` in separate actions step - Lint more files, fix discovered issues - Remove inaccurate and outdated info in docs (we do not need GOPATH for tools anymore) Maybe later we can add more spellchecking tools, but I have not found any good ones yet. (cherry picked from commit 9c39f8515fa88d644736c6773d7a05d070a02e82) Conflicts: .github/workflows/pull-compliance.yml Makefile --- Makefile | 22 +++++++++++++------ .../config-cheat-sheet.zh-cn.md | 2 +- .../development/hacking-on-gitea.en-us.md | 4 ++-- .../development/hacking-on-gitea.zh-cn.md | 4 ++-- .../content/installation/from-source.en-us.md | 8 +------ .../content/installation/from-source.zh-cn.md | 4 +--- templates/repo/branch_dropdown.tmpl | 2 +- .../repo/issue/view_content/comments.tmpl | 2 +- 8 files changed, 24 insertions(+), 24 deletions(-) diff --git a/Makefile b/Makefile index aea352e6d5..419992823e 100644 --- a/Makefile +++ b/Makefile @@ -31,7 +31,7 @@ EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-che GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0 GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2 GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 -MISSPELL_PACKAGE ?= github.com/client9/misspell/cmd/misspell@v0.3.4 +MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.5 XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 @@ -134,6 +134,8 @@ TAR_EXCLUDES := .git data indexers queues log node_modules $(EXECUTABLE) $(FOMAN GO_DIRS := build cmd models modules routers services tests WEB_DIRS := web_src/js web_src/css +SPELLCHECK_FILES := $(GO_DIRS) $(WEB_DIRS) docs/content templates options/locale/locale_en-US.ini .github + GO_SOURCES := $(wildcard *.go) GO_SOURCES += $(shell find $(GO_DIRS) -type f -name "*.go" ! -path modules/options/bindata.go ! -path modules/public/bindata.go ! -path modules/templates/bindata.go) GO_SOURCES += $(GENERATED_GO_DEST) @@ -212,6 +214,8 @@ help: @echo " - lint-swagger lint swagger files" @echo " - lint-templates lint template files" @echo " - lint-yaml lint yaml files" + @echo " - lint-spell lint spelling" + @echo " - lint-spell-fix lint spelling and fix issues" @echo " - checks run various consistency checks" @echo " - checks-frontend check frontend files" @echo " - checks-backend check backend files" @@ -303,10 +307,6 @@ fmt-check: fmt exit 1; \ fi -.PHONY: misspell-check -misspell-check: - go run $(MISSPELL_PACKAGE) -error $(GO_DIRS) $(WEB_DIRS) - .PHONY: $(TAGS_EVIDENCE) $(TAGS_EVIDENCE): @mkdir -p $(MAKE_EVIDENCE_DIR) @@ -368,10 +368,10 @@ checks: checks-frontend checks-backend checks-frontend: lockfile-check svg-check .PHONY: checks-backend -checks-backend: tidy-check swagger-check fmt-check misspell-check forgejo-api-validate swagger-validate security-check +checks-backend: tidy-check swagger-check fmt-check swagger-validate security-check .PHONY: lint -lint: lint-frontend lint-backend +lint: lint-frontend lint-backend lint-spell .PHONY: lint-fix lint-fix: lint-frontend-fix lint-backend-fix @@ -412,6 +412,14 @@ lint-swagger: node_modules lint-md: node_modules npx markdownlint docs *.md +.PHONY: lint-spell +lint-spell: + @go run $(MISSPELL_PACKAGE) -error $(SPELLCHECK_FILES) + +.PHONY: lint-spell-fix +lint-spell-fix: + @go run $(MISSPELL_PACKAGE) -w $(SPELLCHECK_FILES) + .PHONY: lint-go lint-go: $(GO) run $(GOLANGCI_LINT_PACKAGE) run $(GOLANGCI_LINT_ARGS) diff --git a/docs/content/administration/config-cheat-sheet.zh-cn.md b/docs/content/administration/config-cheat-sheet.zh-cn.md index 2cee70daab..8236852ad3 100644 --- a/docs/content/administration/config-cheat-sheet.zh-cn.md +++ b/docs/content/administration/config-cheat-sheet.zh-cn.md @@ -29,7 +29,7 @@ menu: [ini](https://github.com/go-ini/ini/#recursive-values) 这里的说明。 标注了 :exclamation: 的配置项表明除非你真的理解这个配置项的意义,否则最好使用默认值。 -在下面的默认值中,`$XYZ`代表环境变量`XYZ`的值(详见:`enviroment-to-ini`)。 _`XxYyZz`_是指默认配置的一部分列出的值。这些在 app.ini 文件中不起作用,仅在此处列出作为文档说明。 +在下面的默认值中,`$XYZ`代表环境变量`XYZ`的值(详见:`environment-to-ini`)。 _`XxYyZz`_是指默认配置的一部分列出的值。这些在 app.ini 文件中不起作用,仅在此处列出作为文档说明。 包含`#`或者`;`的变量必须使用引号(`` ` ``或者`""""`)包裹,否则会被解析为注释。 diff --git a/docs/content/development/hacking-on-gitea.en-us.md b/docs/content/development/hacking-on-gitea.en-us.md index 4b132c49d9..df8a9047d6 100644 --- a/docs/content/development/hacking-on-gitea.en-us.md +++ b/docs/content/development/hacking-on-gitea.en-us.md @@ -243,10 +243,10 @@ documentation using: make generate-swagger ``` -You should validate your generated Swagger file and spell-check it with: +You should validate your generated Swagger file: ```bash -make swagger-validate misspell-check +make swagger-validate ``` You should commit the changed swagger JSON file. The continuous integration diff --git a/docs/content/development/hacking-on-gitea.zh-cn.md b/docs/content/development/hacking-on-gitea.zh-cn.md index 364bbf1ffe..2dba3c92b6 100644 --- a/docs/content/development/hacking-on-gitea.zh-cn.md +++ b/docs/content/development/hacking-on-gitea.zh-cn.md @@ -228,10 +228,10 @@ Gitea Logo的 PNG 和 SVG 版本是使用 `TAGS="gitea" make generate-images` make generate-swagger ``` -您应该验证生成的 Swagger 文件并使用以下命令对其进行拼写检查: +您应该验证生成的 Swagger 文件: ```bash -make swagger-validate misspell-check +make swagger-validate ``` 您应该提交更改后的 swagger JSON 文件。持续集成服务器将使用以下方法检查是否已完成: diff --git a/docs/content/installation/from-source.en-us.md b/docs/content/installation/from-source.en-us.md index 601e074745..cd9fd56511 100644 --- a/docs/content/installation/from-source.en-us.md +++ b/docs/content/installation/from-source.en-us.md @@ -27,13 +27,7 @@ Next, [install Node.js with npm](https://nodejs.org/en/download/) which is required to build the JavaScript and CSS files. The minimum supported Node.js version is @minNodeVersion@ and the latest LTS version is recommended. -**Note**: When executing make tasks that require external tools, like -`make misspell-check`, Gitea will automatically download and build these as -necessary. To be able to use these, you must have the `"$GOPATH/bin"` directory -on the executable path. If you don't add the go bin directory to the -executable path, you will have to manage this yourself. - -**Note 2**: Go version @minGoVersion@ or higher is required. However, it is recommended to +**Note**: Go version @minGoVersion@ or higher is required. However, it is recommended to obtain the same version as our continuous integration, see the advice given in [Hacking on Gitea](development/hacking-on-gitea.md) diff --git a/docs/content/installation/from-source.zh-cn.md b/docs/content/installation/from-source.zh-cn.md index c2bd5785b2..3ff7efb4ed 100644 --- a/docs/content/installation/from-source.zh-cn.md +++ b/docs/content/installation/from-source.zh-cn.md @@ -21,9 +21,7 @@ menu: 接下来,[安装 Node.js 和 npm](https://nodejs.org/zh-cn/download/), 这是构建 JavaScript 和 CSS 文件所需的。最低支持的 Node.js 版本是 @minNodeVersion@,建议使用最新的 LTS 版本。 -**注意**:当执行需要外部工具的 make 任务(如`make misspell-check`)时,Gitea 将根据需要自动下载和构建这些工具。为了能够实现这个目的,你必须将`"$GOPATH/bin"`目录添加到可执行路径中。如果没有将 Go 的二进制目录添加到可执行路径中,你需要自行解决产生的问题。 - -**注意2**:需要 Go 版本 @minGoVersion@ 或更高版本。不过,建议获取与我们的持续集成(continuous integration, CI)相同的版本,请参阅在 [Hacking on Gitea](development/hacking-on-gitea.md) 中给出的建议。 +**注意**:需要 Go 版本 @minGoVersion@ 或更高版本。不过,建议获取与我们的持续集成(continuous integration, CI)相同的版本,请参阅在 [Hacking on Gitea](development/hacking-on-gitea.md) 中给出的建议。 ## 下载 diff --git a/templates/repo/branch_dropdown.tmpl b/templates/repo/branch_dropdown.tmpl index bee5363296..8a5cdc7cc7 100644 --- a/templates/repo/branch_dropdown.tmpl +++ b/templates/repo/branch_dropdown.tmpl @@ -1,7 +1,7 @@ {{/* Attributes: * root * ContainerClasses -* (TODO: search "branch_dropdown" in the template direcotry) +* (TODO: search "branch_dropdown" in the template directory) */}} {{$defaultSelectedRefName := $.root.BranchName}} {{if and .root.IsViewTag (not .noTag)}} diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 53c20a68bb..a8e0aaf430 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -371,7 +371,7 @@
    {{if .OriginalAuthor}} {{else}} - {{/* Some timeline avatars need a offset to correctly allign with their speech + {{/* Some timeline avatars need a offset to correctly align with their speech bubble. The condition depends on review type and for positive reviews whether there is a comment element or not */}} From 22c694a9e979fbe126cf2efc9f5ae18cf4de396f Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 9 Feb 2024 11:57:09 +0100 Subject: [PATCH 019/271] [gitea] add lint-spell-fix to lint-fix (#29111) Followup to https://github.com/go-gitea/gitea/pull/29106, fix this oversight. (cherry picked from commit c7a21cbb0c5f8302495fa24baf218dc3462de2c5) --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 419992823e..6ef4c521c9 100644 --- a/Makefile +++ b/Makefile @@ -374,7 +374,7 @@ checks-backend: tidy-check swagger-check fmt-check swagger-validate security-che lint: lint-frontend lint-backend lint-spell .PHONY: lint-fix -lint-fix: lint-frontend-fix lint-backend-fix +lint-fix: lint-frontend-fix lint-backend-fix lint-spell-fix .PHONY: lint-frontend lint-frontend: lint-js lint-css From 22fd29d3b73196543c5bb6aa9457bdf34d99945e Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Sat, 10 Feb 2024 00:22:56 +0000 Subject: [PATCH 020/271] [gitea] [skip ci] Updated translations via Crowdin (cherry picked from commit c1f7249056d4aa38927aebcbddc6459ee714c801) --- options/locale/locale_el-GR.ini | 154 +++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 2af215ae41..1d2efb8427 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -5,6 +5,7 @@ explore=Εξερεύνηση help=Βοήθεια logo=Λογότυπο sign_in=Είσοδος +sign_in_with_provider=Είσοδος με %s sign_in_or=ή sign_out=Έξοδος sign_up=Εγγραφή @@ -17,6 +18,7 @@ template=Πρότυπο language=Γλώσσα notifications=Ειδοποιήσεις active_stopwatch=Ενεργή Καταγραφή Χρόνου +tracked_time_summary=Περίληψη του χρόνου παρακολούθησης με βάση τα φίλτρα της λίστας ζητημάτων create_new=Δημιουργία… user_profile_and_more=Προφίλ και ρυθμίσεις… signed_in_as=Είσοδος ως @@ -80,6 +82,7 @@ milestones=Ορόσημα ok=OK cancel=Ακύρωση +retry=Επανάληψη rerun=Επανεκτέλεση rerun_all=Επανεκτέλεση όλων save=Αποθήκευση @@ -89,12 +92,15 @@ remove=Αφαίρεση remove_all=Αφαίρεση Όλων remove_label_str=`Αφαίρεση του αντικειμένου "%s"` edit=Επεξεργασία +view=Προβολή enabled=Ενεργοποιημένο disabled=Απενεργοποιημένο +locked=Κλειδωμένο copy=Αντιγραφή copy_url=Αντιγραφή URL +copy_hash=Αντιγραφή hash copy_content=Αντιγραφή περιεχομένου copy_branch=Αντιγραφή ονόματος κλάδου copy_success=Αντιγράφηκε! @@ -107,6 +113,7 @@ loading=Φόρτωση… error=Σφάλμα error404=Η σελίδα που προσπαθείτε να φτάσετε είτε δεν υπάρχει είτε δεν είστε εξουσιοδοτημένοι για να την δείτε. +go_back=Επιστροφή never=Ποτέ unknown=Άγνωστη @@ -128,7 +135,9 @@ concept_user_organization=Οργανισμός show_timestamps=Εμφάνιση χρονοσημάνσεων show_log_seconds=Εμφάνιση δευτερολέπτων show_full_screen=Εμφάνιση πλήρους οθόνης +download_logs=Λήψη καταγραφών +confirm_delete_selected=Επιβεβαιώνετε τη διαγραφή όλων των επιλεγμένων στοιχείων; name=Όνομα value=Τιμή @@ -167,6 +176,7 @@ string.desc=Z - A [error] occurred=Παρουσιάστηκε ένα σφάλμα +report_message=Αν πιστεύετε ότι αυτό είναι ένα πρόβλημα στο Gitea, παρακαλούμε αναζητήστε ζητήματα στο GitHub ή ανοίξτε ένα νέο ζήτημα εάν είναι απαραίτητο. missing_csrf=Bad Request: δεν υπάρχει διακριτικό CSRF invalid_csrf=Λάθος Αίτημα: μη έγκυρο διακριτικό CSRF not_found=Ο προορισμός δεν βρέθηκε. @@ -175,6 +185,7 @@ network_error=Σφάλμα δικτύου [startpage] app_desc=Μια ανώδυνη, αυτο-φιλοξενούμενη υπηρεσία Git install=Εύκολο στην εγκατάσταση +install_desc=Απλά εκτελέστε το αρχείο προγράμματος για την πλατφόρμα σας, χρήσιμοποιήστε το με το Docker, ή εγκαταστήστε το πακέτο. platform=Πολυπλατφορμικό platform_desc=Ο Forgejo τρέχει οπουδήποτε Go μπορεί να γίνει compile για: Windows, macOS, Linux, ARM, κλπ. Επιλέξτε αυτό που αγαπάτε! lightweight=Ελαφρύ @@ -219,6 +230,7 @@ repo_path_helper=Τα απομακρυσμένα αποθετήρια Git θα lfs_path=Ριζική Διαδρομή Git LFS lfs_path_helper=Τα αρχεία που παρακολουθούνται από το Git LFS θα αποθηκεύονται σε αυτόν τον φάκελο. Αφήστε κενό για να το απενεργοποιήσετε. run_user=Εκτέλεση Σαν Χρήστη +run_user_helper=Το όνομα του χρήστη του λειτουργικού συστήματος ο οποίος εκτελεί το Gitea. Επισημαίνεται ότι αυτός ο χρήστης πρέπει να έχει πρόσβαση στο ριζικό φάκελο του αποθετηρίου. domain=Domain Διακομιστή domain_helper=Όνομα domain διακομιστή ή η διεύθυνση του. ssh_port=Θύρα της υπηρεσίας SSH @@ -291,6 +303,7 @@ password_algorithm_helper=Ορίστε τον αλγόριθμο κατακερ enable_update_checker=Ενεργοποίηση Ελεγκτή Ενημερώσεων enable_update_checker_helper=Ελέγχει περιοδικά για νέες εκδόσεις κάνοντας σύνδεση στο gitea.io. env_config_keys=Ρυθμίσεις Περιβάλλοντος +env_config_keys_prompt=Οι ακόλουθες μεταβλητές περιβάλλοντος θα εφαρμοστούν επίσης στο αρχείο ρυθμίσεων σας: [home] uname_holder=Όνομα Χρήστη ή Διεύθυνση Email @@ -349,9 +362,11 @@ disable_register_prompt=Η εγγραφή είναι απενεργοποιημ disable_register_mail=Η Επιβεβαίωση email για την εγγραφή είναι απενεργοποιημένη. manual_activation_only=Επικοινωνήστε με το διαχειριστή της υπηρεσίας για να ολοκληρώσετε την ενεργοποίηση. remember_me=Απομνημόνευση αυτής της συσκευής +remember_me.compromised=Το διακριτικό σύνδεσης δεν είναι πλέον έγκυρο, αυτό ίσως υποδεικνύει έναν κλεμμένο λογαριασμό. Παρακαλώ ελέγξτε το λογαριασμό σας για ασυνήθιστες δραστηριότητες. forgot_password_title=Ξέχασα Τον Κωδικό Πρόσβασης forgot_password=Ξεχάσατε τον κωδικό πρόσβασης; sign_up_now=Χρειάζεστε λογαριασμό; Εγγραφείτε τώρα. +sign_up_successful=Ο λογαριασμός δημιουργήθηκε επιτυχώς. Καλώς ορίσατε! confirmation_mail_sent_prompt=Ένα νέο email επιβεβαίωσης έχει σταλεί στο %s. Παρακαλώ ελέγξτε τα εισερχόμενα σας μέσα στις επόμενες %s για να ολοκληρώσετε τη διαδικασία εγγραφής. must_change_password=Ενημερώστε τον κωδικό πρόσβασης σας allow_password_change=Απαιτείται από το χρήστη να αλλάξει τον κωδικό πρόσβασης (συνιστόμενο) @@ -359,6 +374,7 @@ reset_password_mail_sent_prompt=Ένα email επιβεβαίωσης έχει active_your_account=Ενεργοποιήστε Το Λογαριασμό Σας account_activated=Ο λογαριασμός έχει ενεργοποιηθεί prohibit_login=Απαγορεύεται η Σύνδεση +prohibit_login_desc=Ο λογαριασμός σας δεν επιτρέπεται να συνδεθεί, παρακαλούμε επικοινωνήστε με το διαχειριστή σας. resent_limit_prompt=Έχετε ήδη ζητήσει ένα email ενεργοποίησης πρόσφατα. Παρακαλώ περιμένετε 3 λεπτά και προσπαθήστε ξανά. has_unconfirmed_mail=Γεια σας %s, έχετε μια ανεπιβεβαίωτη διεύθυνση ηλεκτρονικού ταχυδρομείου (%s). Εάν δεν έχετε λάβει email επιβεβαίωσης ή χρειάζεται να αποστείλετε εκ νέου ένα νέο, παρακαλώ κάντε κλικ στο παρακάτω κουμπί. resend_mail=Κάντε κλικ εδώ για να στείλετε ξανά το email ενεργοποίησης @@ -368,6 +384,7 @@ reset_password=Ανάκτηση Λογαριασμού invalid_code=Ο κωδικός επιβεβαίωσης δεν είναι έγκυρος ή έχει λήξει. invalid_password=Ο κωδικός πρόσβασης σας δεν ταιριάζει με τον κωδικό που χρησιμοποιήθηκε για τη δημιουργία του λογαριασμού. reset_password_helper=Ανάκτηση Λογαριασμού +reset_password_wrong_user=Έχετε συνδεθεί ως %s, αλλά ο σύνδεσμος ανάκτησης λογαριασμού προορίζεται για το %s password_too_short=Το μήκος του κωδικού πρόσβασης δεν μπορεί να είναι μικρότερο από %d χαρακτήρες. non_local_account=Οι μη τοπικοί χρήστες δεν μπορούν να ενημερώσουν τον κωδικό πρόσβασής τους μέσω του διεπαφής web του Forgejo. verify=Επαλήθευση @@ -392,6 +409,7 @@ openid_connect_title=Σύνδεση σε υπάρχων λογαριασμό openid_connect_desc=Το επιλεγμένο OpenID URI είναι άγνωστο. Συνδέστε το με ένα νέο λογαριασμό εδώ. openid_register_title=Δημιουργία νέου λογαριασμού openid_register_desc=Το επιλεγμένο OpenID URI είναι άγνωστο. Συνδέστε το με ένα νέο λογαριασμό εδώ. +openid_signin_desc=Εισάγετε το OpenID URI σας. Για παράδειγμα: alice.openid.example.org ή https://openid.example.org/alice. disable_forgot_password_mail=Η ανάκτηση λογαριασμού είναι απενεργοποιημένη επειδή δεν έχει οριστεί email. Παρακαλούμε επικοινωνήστε με το διαχειριστή. disable_forgot_password_mail_admin=Η ανάκτηση λογαριασμού είναι διαθέσιμη μόνο όταν έχει οριστεί το email. Παρακαλούμε ορίστει το email σας για να ενεργοποιήσετε την ανάκτηση λογαριασμού. email_domain_blacklisted=Δεν μπορείτε να εγγραφείτε με τη διεύθυνση email σας. @@ -401,6 +419,7 @@ authorize_application_created_by=Αυτή η εφαρμογή δημιουργή authorize_application_description=Εάν παραχωρήσετε την πρόσβαση, θα μπορεί να έχει πρόσβαση και να γράφει σε όλες τις πληροφορίες του λογαριασμού σας, συμπεριλαμβανομένων των ιδιωτικών αποθετηρίων και οργανισμών. authorize_title=Εξουσιοδότηση του "%s" για έχει πρόσβαση στο λογαριασμό σας; authorization_failed=Αποτυχία εξουσιοδότησης +authorization_failed_desc=Η εξουσιοδότηση απέτυχε επειδή εντοπίστηκε μια μη έγκυρη αίτηση. Παρακαλούμε επικοινωνήστε με το συντηρητή της εφαρμογής που προσπαθήσατε να εξουσιοδοτήσετε. sspi_auth_failed=Αποτυχία ταυτοποίησης SSPI password_pwned_err=Δεν ήταν δυνατή η ολοκλήρωση του αιτήματος προς το HaveIBeenPwned @@ -416,6 +435,7 @@ activate_account.text_1=Γεια σας %[1]s, ευχαριστούμε activate_account.text_2=Παρακαλούμε κάντε κλικ στον παρακάτω σύνδεσμο για να ενεργοποιήσετε το λογαριασμό σας μέσα σε %s: activate_email=Επιβεβαιώστε τη διεύθυνση email σας +activate_email.title=%s, παρακαλώ επαληθεύστε τη διεύθυνση email σας activate_email.text=Παρακαλώ κάντε κλικ στον παρακάτω σύνδεσμο για να επαληθεύσετε τη διεύθυνση email σας στο %s: register_notify=Καλώς ήλθατε στο Forgejo @@ -585,6 +605,8 @@ user_bio=Βιογραφικό disabled_public_activity=Αυτός ο χρήστης έχει απενεργοποιήσει τη δημόσια προβολή της δραστηριότητας. email_visibility.limited=Η διεύθυνση email σας είναι ορατή σε όλους τους ταυτοποιημένους χρήστες email_visibility.private=Η διεύθυνση email σας είναι ορατή μόνο σε εσάς και στους διαχειριστές +show_on_map=Εμφάνιση της τοποθεσίας στο χάρτη +settings=Ρυθμίσεις Χρήστη form.name_reserved=Το όνομα χρήστη "%s" είναι δεσμευμένο. form.name_pattern_not_allowed=Το μοτίβο "%s" δεν επιτρέπεται μέσα σε ένα όνομα χρήστη. @@ -606,9 +628,12 @@ delete=Διαγραφή Λογαριασμού twofa=Έλεγχος Ταυτότητας Δύο Παραγόντων account_link=Συνδεδεμένοι Λογαριασμοί organization=Οργανισμοί +uid=UID webauthn=Κλειδιά Ασφαλείας public_profile=Δημόσιο Προφίλ +biography_placeholder=Πείτε μας λίγο για τον εαυτό σας! (Μπορείτε να γράψετε με Markdown) +location_placeholder=Μοιραστείτε την κατά προσέγγιση τοποθεσία σας με άλλους password_username_disabled=Οι μη τοπικοί χρήστες δεν επιτρέπεται να αλλάξουν το όνομα χρήστη τους. Επικοινωνήστε με το διαχειριστή σας για περισσότερες λεπτομέρειες. full_name=Πλήρες Όνομα website=Ιστοσελίδα @@ -620,6 +645,7 @@ update_language_not_found=Η γλώσσα "%s" δεν είναι διαθέσι update_language_success=Η γλώσσα ενημερώθηκε. update_profile_success=Το προφίλ σας έχει ενημερωθεί. change_username=Το όνομα χρήστη σας έχει αλλάξει. +change_username_redirect_prompt=Το παλιό όνομα χρήστη θα ανακατευθύνει μέχρι να ζητηθεί ξανά. continue=Συνέχεια cancel=Ακύρωση language=Γλώσσα @@ -644,6 +670,7 @@ comment_type_group_project=Έργο comment_type_group_issue_ref=Αναφορά ζητήματος saved_successfully=Οι ρυθμίσεις σας αποθηκεύτηκαν επιτυχώς. privacy=Απόρρητο +keep_activity_private=Απόκρυψη Δραστηριότητας από τη σελίδα προφίλ keep_activity_private_popup=Με αυτή την επιλογή η δραστηριότητα σας είναι ορατή μόνο σε εσάς και τους διαχειριστές lookup_avatar_by_mail=Αναζήτηση ενός Avatar με διεύθυνση email @@ -653,12 +680,14 @@ choose_new_avatar=Επιλέξτε νέα εικόνα update_avatar=Ενημέρωση Εικόνας delete_current_avatar=Διαγραφή Τρέχουσας Εικόνας uploaded_avatar_not_a_image=Το αρχείο που ανεβάσατε δεν είναι εικόνα. +uploaded_avatar_is_too_big=Το μέγεθος αρχείου που ανέβηκε (%d KiB) υπερβαίνει το μέγιστο μέγεθος (%d KiB). update_avatar_success=Η εικόνα σας έχει ενημερωθεί. update_user_avatar_success=Το avatar του χρήστη ενημερώθηκε. change_password=Ενημέρωση Κωδικού Πρόσβασης old_password=Τρέχων Κωδικός Πρόσβασης new_password=Νέος Κωδικός Πρόσβασης +retype_new_password=Επιβεβαίωση Νέου Κωδικού Πρόσβασης password_incorrect=Ο τρέχων κωδικός πρόσβασης είναι λάθος. change_password_success=Ο κωδικός πρόσβασής σας έχει ενημερωθεί. Από εδώ και τώρα συνδέεστε χρησιμοποιώντας τον νέο κωδικό πρόσβασής σας. password_change_disabled=Οι μη τοπικοί χρήστες δεν μπορούν να ενημερώσουν τον κωδικό πρόσβασής τους μέσω του διεπαφής web του Forgejo. @@ -667,6 +696,7 @@ emails=Διευθύνσεις Email manage_emails=Διαχείριση Διευθύνσεων Email manage_themes=Επιλέξτε προεπιλεγμένο θέμα διεπαφής manage_openid=Διαχείριση Διευθύνσεων OpenID +email_desc=Η κύρια διεύθυνση ηλεκτρονικού ταχυδρομείου σας θα χρησιμοποιηθεί για ειδοποιήσεις, ανάκτηση του κωδικού πρόσβασης και, εφόσον δεν είναι κρυμμένη, λειτουργίες Git στον ιστότοπο. theme_desc=Αυτό θα είναι το προεπιλεγμένο θέμα διεπαφής σας σε όλη την ιστοσελίδα. primary=Κύριο activated=Ενεργό @@ -674,6 +704,7 @@ requires_activation=Απαιτείται ενεργοποίηση primary_email=Αλλαγή κυριότητας activate_email=Αποστολή Ενεργοποίησης activations_pending=Εκκρεμούν Ενεργοποιήσεις +can_not_add_email_activations_pending=Εκκρεμεί μια ενεργοποίηση, δοκιμάστε ξανά σε λίγα λεπτά αν θέλετε να προσθέσετε ένα νέο email. delete_email=Αφαίρεση email_deletion=Αφαίρεση Διεύθυνσης Email email_deletion_desc=Η διεύθυνση ηλεκτρονικού ταχυδρομείου και οι σχετικές πληροφορίες θα αφαιρεθούν από τον λογαριασμό σας. Οι υποβολές Git από αυτή τη διεύθυνση email θα παραμείνουν αμετάβλητες. Συνέχεια; @@ -791,6 +822,7 @@ permissions_access_all=Όλα (δημόσια, ιδιωτικά, και περι select_permissions=Επιλέξτε δικαιώματα permission_no_access=Καμία Πρόσβαση permission_read=Αναγνωσμένες +permission_write=Ανάγνωση και Εγγραφή at_least_one_permission=Πρέπει να επιλέξετε τουλάχιστον ένα δικαίωμα για να δημιουργήσετε ένα διακριτικό permissions_list=Δικαιώματα: @@ -817,6 +849,7 @@ authorized_oauth2_applications=Εξουσιοδοτημένες Εφαρμογέ revoke_key=Ανάκληση revoke_oauth2_grant=Ανάκληση Πρόσβασης revoke_oauth2_grant_description=Η ανάκληση πρόσβασης για αυτή την εξωτερική εφαρμογή θα αποτρέψει αυτή την εφαρμογή από την πρόσβαση στα δεδομένα σας. Σίγουρα; +revoke_oauth2_grant_success=Η πρόσβαση ανακλήθηκε επιτυχώς. twofa_desc=Ο έλεγχος ταυτότητας δύο παραγόντων ενισχύει την ασφάλεια του λογαριασμού σας. twofa_is_enrolled=Ο λογαριασμός σας είναι εγγεγραμμένος σε έλεγχο ταυτότητας δύο παραγόντων. @@ -851,6 +884,7 @@ remove_account_link_success=Ο συνδεδεμένος λογαριασμός orgs_none=Δεν είστε μέλος σε κάποιο οργανισμό. +repos_none=Δεν κατέχετε κάποιο αποθετήριο. delete_account=Διαγραφή Του Λογαριασμού Σας delete_prompt=Αυτή η ενέργεια θα διαγράψει μόνιμα το λογαριασμό σας. ΔΕΝ ΘΑ ΜΠΟΡΕΙ να επανέλθει. @@ -883,6 +917,7 @@ template_helper=Μετατροπή σε πρότυπο αποθετήριο template_description=Τα πρότυπα αποθετήρια επιτρέπουν στους χρήστες να δημιουργήσουν νέα αποθετήρια με την ίδια δομή, αρχεία και προαιρετικές ρυθμίσεις. visibility=Ορατότητα visibility_description=Μόνο ο ιδιοκτήτης ή τα μέλη του οργανισμού εάν έχουν δικαιώματα, θα είναι σε θέση να το δουν. +visibility_helper=Αλλάξτε το αποθετήριο σε ιδιωτικό visibility_helper_forced=Ο διαχειριστής σας αναγκάζει τα νέα αποθετήρια να είναι ιδιωτικά. visibility_fork_helper=(Αλλάζοντας αυτό θα επηρεάσει όλα τα forks.) clone_helper=Χρειάζεστε βοήθεια για τη κλωνοποίηση; Επισκεφθείτε τη Βοήθεια. @@ -891,6 +926,8 @@ fork_from=Fork Από Το already_forked=Έχετε ήδη κάνει fork το %s fork_to_different_account=Fork σε διαφορετικό λογαριασμό fork_visibility_helper=Η ορατότητα ενός fork αποθετηρίου δεν μπορεί να αλλάξει. +fork_branch=Κλάδος που θα κλωνοποιηθεί στο fork +all_branches=Όλοι οι κλάδοι use_template=Χρήση αυτού του πρότυπου clone_in_vsc=Κλωνοποίηση στο VS Code download_zip=Λήψη ZIP @@ -919,6 +956,7 @@ trust_model_helper_collaborator_committer=Συνεργάτης+Υποβολέα trust_model_helper_default=Προεπιλογή: Χρησιμοποιήστε το προεπιλεγμένο μοντέλο εμπιστοσύνης για αυτήν την εγκατάσταση create_repo=Δημιουργία Αποθετηρίου default_branch=Προεπιλεγμένος Κλάδος +default_branch_label=προεπιλογή default_branch_helper=Ο προεπιλεγμένος κλάδος είναι ο βασικός κλάδος για pull requests και υποβολές κώδικα. mirror_prune=Καθαρισμός mirror_prune_desc=Αφαίρεση παρωχημένων αναφορών απομακρυσμένης-παρακολούθησης @@ -1095,6 +1133,9 @@ file_view_rendered=Προβολή Απόδοσης file_view_raw=Προβολή Ακατέργαστου file_permalink=Permalink file_too_large=Το αρχείο είναι πολύ μεγάλο για να εμφανιστεί. +invisible_runes_description=`Αυτό το αρχείο περιέχει αόρατους χαρακτήρες Unicode που δεν διακρίνονται από ανθρώπους, αλλά μπορεί να επεξεργάζονται διαφορετικά από έναν υπολογιστή. Αν νομίζετε ότι αυτό είναι σκόπιμο, μπορείτε να αγνοήσετε με ασφάλεια αυτή την προειδοποίηση. Χρησιμοποιήστε το κουμπί Escape για να τους αποκαλύψετε.` +ambiguous_runes_header=`Αυτό το αρχείο περιέχει ασαφείς χαρακτήρες Unicode ` +ambiguous_runes_description=`Αυτό το αρχείο περιέχει χαρακτήρες Unicode που μπορεί να συγχέονται με άλλους χαρακτήρες. Αν νομίζετε ότι αυτό είναι σκόπιμο, μπορείτε να αγνοήσετε με ασφάλεια αυτή την προειδοποίηση. Χρησιμοποιήστε το κουμπί Escape για να τους αποκαλύψετε.` invisible_runes_line=`Αυτή η γραμμή έχει αόρατους χαρακτήρες unicode ` ambiguous_runes_line=`Αυτή η γραμμή έχει ασαφείς χαρακτήρες unicode ` ambiguous_character=`ο %[1]c [U+%04[1]X] μπορεί να μπερδευτεί με τον %[2]c [U+%04[2]X]` @@ -1107,11 +1148,15 @@ video_not_supported_in_browser=Το πρόγραμμα περιήγησής σα audio_not_supported_in_browser=Το πρόγραμμα περιήγησής σας δεν υποστηρίζει την ετικέτα HTML5 'audio'. stored_lfs=Αποθηκεύτηκε με το Git LFS symbolic_link=Symbolic link +executable_file=Εκτελέσιμο Αρχείο commit_graph=Γράφημα Υποβολών commit_graph.select=Επιλογή κλάδων commit_graph.hide_pr_refs=Απόκρυψη Pull Requests commit_graph.monochrome=Μονόχρωμο commit_graph.color=Έγχρωμο +commit.contained_in=Αυτή η υποβολή περιλαμβάνεται σε: +commit.contained_in_default_branch=Αυτή η υποβολή είναι μέρος του προεπιλεγμένου κλάδου +commit.load_referencing_branches_and_tags=Φόρτωση κλάδων και ετικετών που παραπέμπουν σε αυτήν την υποβολή blame=Ευθύνη download_file=Λήψη αρχείου normal_view=Κανονική Προβολή @@ -1204,6 +1249,7 @@ commits.signed_by_untrusted_user=Υπογράφηκε από μη έμπιστο commits.signed_by_untrusted_user_unmatched=Υπογράφηκε από ένα μη έμπιστο χρήστη ο οποίος δεν ταιριάζει με τον υποβολέα commits.gpg_key_id=ID Κλειδιού GPG commits.ssh_key_fingerprint=Αποτύπωμα Κλειδιού SSH +commits.view_path=Προβολή σε αυτή τη στιγμή στο ιστορικό commit.operations=Λειτουργίες commit.revert=Απόσυρση @@ -1331,6 +1377,7 @@ issues.delete_branch_at=`διέγραψε το κλάδο %s %s` issues.filter_label=Σήμα issues.filter_label_exclude=`Χρησιμοποιήστε alt + κάντε κλικ/Enter για να εξαιρέσετε τις σημάνσεις` issues.filter_label_no_select=Όλα τα σήματα +issues.filter_label_select_no_label=Χωρίς ετικέτα issues.filter_milestone=Ορόσημο issues.filter_milestone_all=Όλα τα ορόσημα issues.filter_milestone_none=Χωρίς ορόσημα @@ -1381,9 +1428,10 @@ issues.opened_by_fake=άνοιξε το %[1]s από %[2]s issues.closed_by_fake=από %[2]s έκλεισαν %[1]s issues.previous=Προηγούμενο issues.next=Επόμενο -issues.open_title=Ανοιχτά +issues.open_title=Ανοικτό issues.closed_title=Κλειστά issues.draft_title=Προσχέδιο +issues.num_comments_1=%d σχόλιο issues.num_comments=%d σχόλια issues.commented_at=`σχολίασε %s` issues.delete_comment_confirm=Θέλετε σίγουρα να διαγράψετε αυτό το σχόλιο; @@ -1392,6 +1440,7 @@ issues.context.quote_reply=Παράθεση Απάντησης issues.context.reference_issue=Αναφορά σε νέο ζήτημα issues.context.edit=Επεξεργασία issues.context.delete=Διαγραφή +issues.no_content=Δεν υπάρχει περιγραφή. issues.close=Κλείσιμο Ζητήματος issues.comment_pull_merged_at=συγχώνευσε την υποβολή %[1]s στο %[2]s %[3]s issues.comment_manually_pull_merged_at=συγχώνευσε χειροκίνητα την υποβολή %[1]s στο %[2]s %[3]s @@ -1410,8 +1459,17 @@ issues.ref_closed_from=`έκλεισε αυτό το ζήτημ issues.ref_reopened_from=`άνοιξε ξανά αυτό το ζήτημα %[4]s %[2]s` issues.ref_from=`από %[1]s` issues.author=Συγγραφέας +issues.author_helper=Αυτός ο χρήστης είναι ο συγγραφέας. issues.role.owner=Ιδιοκτήτης +issues.role.owner_helper=Αυτός ο χρήστης είναι ο ιδιοκτήτης αυτού του αποθετηρίου. issues.role.member=Μέλος +issues.role.member_helper=Αυτός ο χρήστης είναι μέλος του οργανισμού που κατέχει αυτό το αποθετήριο. +issues.role.collaborator=Συνεργάτης +issues.role.collaborator_helper=Αυτός ο χρήστης έχει προσκληθεί να συνεργαστεί στο αποθετήριο. +issues.role.first_time_contributor=Συντελεστής για πρώτη φορά +issues.role.first_time_contributor_helper=Αυτή είναι η πρώτη συνεισφορά αυτού του χρήστη στο αποθετήριο. +issues.role.contributor=Συντελεστής +issues.role.contributor_helper=Αυτός ο χρήστης έχει προηγούμενές υποβολές στο αποθετήριο. issues.re_request_review=Επαναίτηση ανασκόπησης issues.is_stale=Έχουν υπάρξει αλλαγές σε αυτό το PR από αυτή την αναθεώρηση issues.remove_request_review=Αφαίρεση αιτήματος αναθεώρησης @@ -1426,6 +1484,9 @@ issues.label_title=Όνομα σήματος issues.label_description=Περιγραφή σήματος issues.label_color=Χρώμα σήματος issues.label_exclusive=Αποκλειστικό +issues.label_archive=Αρχειοθέτηση Σήματος +issues.label_archived_filter=Εμφάνιση αρχειοθετημένων σημάτων +issues.label_archive_tooltip=Τα αρχειοθετημένα σήματα εξαιρούνται από τις προτάσεις στην αναζήτηση με σήματα. issues.label_exclusive_desc=Ονομάστε το σήμα πεδίο/στοιχείο για να το κάνετε αμοιβαία αποκλειστικό με άλλα σήματα πεδίου/. issues.label_exclusive_warning=Τυχόν συγκρουόμενα σήματα θα αφαιρεθούν κατά την επεξεργασία των σημάτων ενός ζητήματος ή pull request. issues.label_count=%d σήματα @@ -1480,6 +1541,7 @@ issues.tracking_already_started=`Έχετε ήδη ξεκινήσει την κ issues.stop_tracking=Διακοπή Χρονομέτρου issues.stop_tracking_history=`σταμάτησε να εργάζεται %s` issues.cancel_tracking=Απόρριψη +issues.cancel_tracking_history=`ακύρωσε τη παρακολούθηση χρόνου %s` issues.add_time=Χειροκίνητη Προσθήκη Ώρας issues.del_time=Διαγραφή αυτού του αρχείου χρόνου issues.add_time_short=Προσθήκη Χρόνου @@ -1503,6 +1565,7 @@ issues.due_date_form=εεεε-μμ-ηη issues.due_date_form_add=Προσθήκη ημερομηνίας παράδοσης issues.due_date_form_edit=Επεξεργασία issues.due_date_form_remove=Διαγραφή +issues.due_date_not_writer=Χρειάζεστε πρόσβαση εγγραφής στο αποθετήριο για να ενημερώσετε την ημερομηνία λήξης ενός προβλήματος. issues.due_date_not_set=Δεν ορίστηκε ημερομηνία παράδοσης. issues.due_date_added=πρόσθεσε την ημερομηνία παράδοσης %s %s issues.due_date_modified=τροποποίησε την ημερομηνία παράδοσης από %[2]s σε %[1]s %[3]s @@ -1558,6 +1621,9 @@ issues.review.pending.tooltip=Αυτό το σχόλιο προς το παρό issues.review.review=Αξιολόγηση issues.review.reviewers=Εξεταστές issues.review.outdated=Παρωχημένο +issues.review.outdated_description=Το περιεχόμενο άλλαξε αφού έγινε αυτό το σχόλιο +issues.review.option.show_outdated_comments=Εμφάνιση παρωχημένων σχολίων +issues.review.option.hide_outdated_comments=Απόκρυψη παρωχημένων σχολίων issues.review.show_outdated=Εμφάνιση παροχημένων issues.review.hide_outdated=Απόκρυψη παροχημένων issues.review.show_resolved=Εμφάνιση επιλυμένων @@ -1597,6 +1663,13 @@ pulls.switch_comparison_type=Αλλαγή τύπου σύγκρισης pulls.switch_head_and_base=Αλλαγή κεφαλής και βάσης pulls.filter_branch=Φιλτράρισμα κλάδου pulls.no_results=Δεν βρέθηκαν αποτελέσματα. +pulls.show_all_commits=Εμφάνιση όλων των υποβολών +pulls.show_changes_since_your_last_review=Εμφάνιση αλλαγών από την τελευταία αξιολόγηση +pulls.showing_only_single_commit=Εμφάνιση μόνο αλλαγών της υποβολής %[1]s +pulls.showing_specified_commit_range=Εμφάνιση μόνο των αλλαγών μεταξύ %[1]s..%[2]s +pulls.select_commit_hold_shift_for_range=Επιλέξτε υποβολή. Κρατήστε πατημένο το shift + κάντε κλικ για να επιλέξετε ένα εύρος +pulls.review_only_possible_for_full_diff=Η αξιολόγηση είναι δυνατή μόνο κατά την προβολή της πλήρης διαφοράς +pulls.filter_changes_by_commit=Φιλτράρισμα κατά υποβολή pulls.nothing_to_compare=Αυτοί οι κλάδοι είναι όμοιοι. Δεν υπάρχει ανάγκη να δημιουργήσετε ένα pull request. pulls.nothing_to_compare_and_allow_empty_pr=Αυτοί οι κλάδοι είναι ίσοι. Αυτό το PR θα είναι κενό. pulls.has_pull_request=`Υπάρχει ήδη pull request μεταξύ αυτών των κλάδων: %[2]s#%[3]d` @@ -1628,6 +1701,8 @@ pulls.is_empty=Οι αλλαγές σε αυτόν τον κλάδο είναι pulls.required_status_check_failed=Ορισμένοι απαιτούμενοι έλεγχοι δεν ήταν επιτυχείς. pulls.required_status_check_missing=Λείπουν ορισμένοι απαιτούμενοι έλεγχοι. pulls.required_status_check_administrator=Ως διαχειριστής, μπορείτε ακόμα να συγχωνεύσετε αυτό το pull request. +pulls.blocked_by_rejection=Αυτό το Pull Request έχει αλλαγές που ζητούνται από έναν επίσημο εξεταστή. +pulls.blocked_by_official_review_requests=Αυτό το Pull Request έχει επίσημες αιτήσεις αξιολόγησης. pulls.can_auto_merge_desc=Αυτό το Pull Request μπορεί να συγχωνευθεί αυτόματα. pulls.cannot_auto_merge_desc=Αυτό το pull request δεν μπορεί να συγχωνευθεί αυτόματα λόγω συγκρούσεων. pulls.cannot_auto_merge_helper=Χειροκίνητη Συγχώνευση για την επίλυση των συγκρούσεων. @@ -1673,6 +1748,8 @@ pulls.status_checks_failure=Κάποιοι έλεγχοι απέτυχαν pulls.status_checks_error=Ορισμένοι έλεγχοι ανέφεραν σφάλματα pulls.status_checks_requested=Απαιτείται pulls.status_checks_details=Λεπτομέρειες +pulls.status_checks_hide_all=Απόκρυψη όλων των ελέγχων +pulls.status_checks_show_all=Εμφάνιση όλων των ελέγχων pulls.update_branch=Ενημέρωση κλάδου με συγχώνευση pulls.update_branch_rebase=Ενημέρωση κλάδου με rebase pulls.update_branch_success=Η ενημέρωση του κλάδου ήταν επιτυχής @@ -1681,6 +1758,7 @@ pulls.outdated_with_base_branch=Αυτός ο κλάδος δεν είναι ε pulls.close=Κλείσιμο Pull Request pulls.closed_at=`έκλεισε αυτό το pull request %[2]s` pulls.reopened_at=`άνοιξε ξανά αυτό το pull request %[2]s` +pulls.cmd_instruction_merge_title=Συγχώνευση pulls.clear_merge_message=Εκκαθάριση μηνύματος συγχώνευσης pulls.clear_merge_message_hint=Η εκκαθάριση του μηνύματος συγχώνευσης θα αφαιρέσει μόνο το περιεχόμενο του μηνύματος υποβολής και θα διατηρήσει τα παραγόμενα git trailers όπως "Co-Authored-By …". @@ -1700,6 +1778,7 @@ pulls.delete.title=Διαγραφή αυτού του pull request; pulls.delete.text=Θέλετε πραγματικά να διαγράψετε αυτό το pull request; (Αυτό θα σβήσει οριστικά όλο το περιεχόμενο του. Εξετάστε αν θέλετε να το κλείσετε, αν σκοπεύεται να το αρχειοθετήσετε) +pull.deleted_branch=(διαγράφηκε):%s milestones.new=Νέο Ορόσημο milestones.closed=Έκλεισε %s @@ -1707,6 +1786,7 @@ milestones.update_ago=Ενημερώθηκε %s milestones.no_due_date=Δεν υπάρχει ημερομηνία παράδοσης milestones.open=Άνοιγμα milestones.close=Κλείσιμο +milestones.new_subheader=Τα ορόσημα μπορούν να σας βοηθήσουν να οργανώσετε τα ζητήματα και να παρακολουθείτε την πρόοδό τους. milestones.completeness=%d%% Ολοκληρώθηκε milestones.create=Δημιουργία Ορόσημου milestones.title=Τίτλος @@ -1723,11 +1803,16 @@ milestones.edit_success=Το ορόσημο "%s" ενημερώθηκε. milestones.deletion=Διαγραφή Ορόσημου milestones.deletion_desc=Η διαγραφή ενός ορόσημου το αφαιρεί από όλα τα συναφή ζητήματα. Συνέχεια; milestones.deletion_success=Το ορόσημο έχει διαγραφεί. +milestones.filter_sort.earliest_due_data=Πλησιέστερη παράδοση +milestones.filter_sort.latest_due_date=Απώτερη παράδοση milestones.filter_sort.least_complete=Λιγότερο πλήρη milestones.filter_sort.most_complete=Περισσότερο πλήρη milestones.filter_sort.most_issues=Περισσότερα ζητήματα milestones.filter_sort.least_issues=Λιγότερα ζητήματα +signing.wont_sign.never=Οι υποβολές δεν υπογράφονται ποτέ. +signing.wont_sign.always=Οι υποβολές υπογράφονται πάντα. +signing.wont_sign.not_signed_in=Δεν είστε συνδεδεμένοι. ext_wiki=Πρόσβαση στο Εξωτερικό Wiki ext_wiki.desc=Σύνδεση σε ένα εξωτερικό wiki. @@ -2205,16 +2290,20 @@ settings.tags.protection.create=Προστασία Ετικέτας settings.tags.protection.none=Δεν υπάρχουν προστατευμένες ετικέτες. settings.bot_token=Διακριτικό Bot settings.chat_id=ID Συνομιλίας +settings.thread_id=ID Νήματος settings.matrix.homeserver_url=Homeserver URL settings.matrix.room_id=ID Δωματίου settings.matrix.message_type=Τύπος Μηνύματος settings.archive.button=Αρχειοθέτηση Αποθετηρίου settings.archive.header=Αρχειοθέτηση Αυτού του Αποθετηρίου +settings.archive.text=Η αρχειοθέτηση του αποθετηρίου θα το αλλάξει σε μόνο για ανάγνωση. Δε θα φαίνεται στον αρχικό πίνακα. Κανείς (ακόμα και εσείς!) δε θα μπορεί να κάνει νέες υποβολές, ή να ανοίξει ζητήματα ή pull request. settings.archive.success=Το αποθετήριο αρχειοθετήθηκε με επιτυχία. settings.archive.error=Παρουσιάστηκε σφάλμα κατά την προσπάθεια αρχειοθέτησης του αποθετηρίου. Δείτε το αρχείο καταγραφής για περισσότερες λεπτομέρειες. settings.archive.error_ismirror=Δε μπορείτε να αρχειοθετήσετε ένα είδωλο αποθετηρίου. settings.archive.branchsettings_unavailable=Οι ρυθμίσεις του κλάδου δεν είναι διαθέσιμες αν το αποθετήριο είναι αρχειοθετημένο. settings.archive.tagsettings_unavailable=Οι ρυθμίσεις της ετικέτας δεν είναι διαθέσιμες αν το αποθετήριο είναι αρχειοθετημένο. +settings.unarchive.button=Απο-Αρχειοθέτηση αποθετηρίου +settings.unarchive.header=Απο-Αρχειοθέτηση του αποθετηρίου settings.update_avatar_success=Η εικόνα του αποθετηρίου έχει ενημερωθεί. settings.lfs=LFS settings.lfs_filelist=Αρχεία LFS σε αυτό το αποθετήριο @@ -2281,6 +2370,7 @@ diff.show_more=Εμφάνιση Περισσότερων diff.load=Φόρτωση Διαφορών diff.generated=δημιουργημένο diff.vendored=εξωτερικό +diff.comment.add_line_comment=Προσθήκη σχολίου στη γραμμή diff.comment.placeholder=Αφήστε ένα σχόλιο diff.comment.markdown_info=Υποστηρίζεται στυλ με markdown. diff.comment.add_single_comment=Προσθέστε ένα σχόλιο @@ -2373,6 +2463,7 @@ branch.default_deletion_failed=Ο κλάδος "%s" είναι ο προεπιλ branch.restore=`Επαναφορά του Κλάδου "%s"` branch.download=`Λήψη του Κλάδου "%s"` branch.rename=`Μετονομασία Κλάδου "%s"` +branch.search=Αναζήτηση Κλάδου branch.included_desc=Αυτός ο κλάδος είναι μέρος του προεπιλεγμένου κλάδου branch.included=Περιλαμβάνεται branch.create_new_branch=Δημιουργία κλάδου από κλάδο: @@ -2433,6 +2524,7 @@ form.create_org_not_allowed=Δεν επιτρέπεται να δημιουργ settings=Ρυθμίσεις settings.options=Οργανισμός settings.full_name=Πλήρες Όνομα +settings.email=Email Επικοινωνίας settings.website=Ιστοσελίδα settings.location=Τοποθεσία settings.permission=Δικαιώματα @@ -2446,6 +2538,7 @@ settings.visibility.private_shortname=Ιδιωτικός settings.update_settings=Ενημέρωση Ρυθμίσεων settings.update_setting_success=Οι ρυθμίσεις του οργανισμού έχουν ενημερωθεί. +settings.change_orgname_prompt=Σημείωση: Η αλλαγή του ονόματος του οργανισμού θα αλλάξει επίσης τη διεύθυνση URL του οργανισμού σας και θα απελευθερώσει το παλιό όνομα. settings.change_orgname_redirect_prompt=Το παλιό όνομα θα ανακατευθύνει μέχρι να διεκδικηθεί. settings.update_avatar_success=Η εικόνα του οργανισμού έχει ενημερωθεί. settings.delete=Διαγραφή Οργανισμού @@ -2521,15 +2614,19 @@ teams.all_repositories_helper=Η ομάδα έχει πρόσβαση σε όλ teams.all_repositories_read_permission_desc=Αυτή η ομάδα χορηγεί πρόσβαση Ανάγνωσης σε όλα τα αποθετήρια: τα μέλη μπορούν να δουν και να κλωνοποιήσουν αποθετήρια. teams.all_repositories_write_permission_desc=Αυτή η ομάδα χορηγεί πρόσβαση Εγγραφής σε όλα τα αποθετήρια: τα μέλη μπορούν να διαβάσουν και να κάνουν push σε αποθετήρια. teams.all_repositories_admin_permission_desc=Αυτή η ομάδα παρέχει πρόσβαση Διαχείρισης σε όλα τα αποθετήρια: τα μέλη μπορούν να διαβάσουν, να κάνουν push και να προσθέσουν συνεργάτες στα αποθετήρια. +teams.invite.title=Έχετε προσκληθεί να συμμετάσχετε στην ομάδα %s του οργανισμού %s. teams.invite.by=Προσκλήθηκε από %s teams.invite.description=Παρακαλώ κάντε κλικ στον παρακάτω σύνδεσμο για συμμετοχή στην ομάδα. [admin] dashboard=Πίνακας Ελέγχου +identity_access=Ταυτότητα & Πρόσβαση users=Λογαριασμοί Χρήστη organizations=Οργανισμοί +assets=Στοιχεία Κώδικα repositories=Αποθετήρια hooks=Webhooks +integrations=Ενσωματώσεις authentication=Πηγές Ταυτοποίησης emails=Email Χρήστη config=Διαμόρφωση @@ -2538,6 +2635,7 @@ monitor=Παρακολούθηση first_page=Πρώτο last_page=Τελευταίο total=Σύνολο: %d +settings=Ρυθμίσεις Διαχειριστή dashboard.new_version_hint=Το Forgejo %s είναι διαθέσιμο, τώρα εκτελείτε το %s. Ανατρέξτε στο blog για περισσότερες λεπτομέρειες. dashboard.statistic=Περίληψη @@ -2615,6 +2713,8 @@ dashboard.gc_lfs=Συλλογή απορριμάτων στα μετα-αντι dashboard.stop_zombie_tasks=Διακοπή εργασιών ζόμπι dashboard.stop_endless_tasks=Διακοπή ατελείωτων εργασιών dashboard.cancel_abandoned_jobs=Ακύρωση εγκαταλελειμμένων εργασιών +dashboard.start_schedule_tasks=Έναρξη προγραμματισμένων εργασιών +dashboard.rebuild_issue_indexer=Αναδόμηση ευρετηρίου ζητημάτων users.user_manage_panel=Διαχείριση Λογαριασμών Χρηστών users.new_account=Δημιουργία Λογαριασμού Χρήστη @@ -2623,6 +2723,9 @@ users.full_name=Πλήρες Όνομα users.activated=Ενεργοποιήθηκε users.admin=Διαχειριστής users.restricted=Περιορισμένος +users.reserved=Δεσμευμένο +users.bot=Bot +users.remote=Απομακρυσμένο users.2fa=2FA users.repos=Αποθετήρια users.created=Δημιουργήθηκε @@ -2669,6 +2772,7 @@ users.list_status_filter.is_prohibit_login=Απαγόρευση Σύνδεσης users.list_status_filter.not_prohibit_login=Επιτρέπεται η Σύνδεση users.list_status_filter.is_2fa_enabled=2FA Ενεργοποιημένο users.list_status_filter.not_2fa_enabled=2FA Απενεργοποιημένο +users.details=Λεπτομέρειες Χρήστη emails.email_manage_panel=Διαχείριση Email Χρήστη emails.primary=Κύριο @@ -2699,10 +2803,12 @@ repos.stars=Αστέρια repos.forks=Forks repos.issues=Ζητήματα repos.size=Μέγεθος +repos.lfs_size=Μέγεθος LFS packages.package_manage_panel=Διαχείριση Πακέτων packages.total_size=Συνολικό Μέγεθος: %s packages.unreferenced_size=Μέγεθος Χωρίς Αναφορά: %s +packages.cleanup=Εκκαθάριση ληγμένων δεδομένων packages.owner=Ιδιοκτήτης packages.creator=Δημιουργός packages.name=Όνομα @@ -2849,6 +2955,7 @@ config.disable_router_log=Απενεργοποίηση Καταγραφής Δρ config.run_user=Εκτέλεση Σαν Χρήστη config.run_mode=Λειτουργία Εκτέλεσης config.git_version=Έκδοση Git +config.app_data_path=Διαδρομή Δεδομένων Εφαρμογής config.repo_root_path=Ριζική Διαδρομή Αποθετηρίων config.lfs_root_path=Ριζική Διαδρομή LFS config.log_file_root_path=Διαδρομή Καταγραφών @@ -2998,8 +3105,10 @@ monitor.queue.name=Όνομα monitor.queue.type=Τύπος monitor.queue.exemplar=Τύπος Υποδείγματος monitor.queue.numberworkers=Αριθμός Εργατών +monitor.queue.activeworkers=Ενεργοί Εργάτες monitor.queue.maxnumberworkers=Μέγιστος Αριθμός Εργατών monitor.queue.numberinqueue=Πλήθος Ουράς +monitor.queue.review_add=Εξέταση / Προσθήκη Εργατών monitor.queue.settings.title=Ρυθμίσεις Δεξαμενής monitor.queue.settings.desc=Οι δεξαμενές αυξάνονται δυναμικά όταν υπάρχει φραγή της ουράς των εργατών τους. monitor.queue.settings.maxnumberworkers=Μέγιστος Αριθμός Εργατών @@ -3205,6 +3314,8 @@ pub.install=Για να εγκαταστήσετε το πακέτο μέσω τ pypi.requires=Απαιτεί Python pypi.install=Για να εγκαταστήσετε το πακέτο χρησιμοποιώντας το pip, εκτελέστε την ακόλουθη εντολή: rpm.registry=Ρυθμίστε αυτό το μητρώο από τη γραμμή εντολών: +rpm.distros.redhat=σε διανομές βασισμένες στο RedHat +rpm.distros.suse=σε διανομές με βάση το SUSE rpm.install=Για να εγκαταστήσετε το πακέτο, εκτελέστε την ακόλουθη εντολή: rubygems.install=Για να εγκαταστήσετε το πακέτο χρησιμοποιώντας το gem, εκτελέστε την ακόλουθη εντολή: rubygems.install2=ή προσθέστε το στο Gemfile: @@ -3237,6 +3348,7 @@ owner.settings.cargo.rebuild.success=Το ευρετήριο Cargo αναδομ 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=Ο κανόνας εκκαθάρισης δεν ταιριάζει με κανένα πακέτο. @@ -3281,6 +3393,7 @@ status.waiting=Αναμονή status.running=Εκτελείται status.success=Επιτυχές status.failure=Αποτυχία +status.cancelled=Ακυρώθηκε status.skipped=Παρακάμφθηκε status.blocked=Αποκλείστηκε @@ -3297,6 +3410,7 @@ runners.labels=Ετικέτες runners.last_online=Τελευταία Ώρα Σύνδεσης runners.runner_title=Εκτελεστής runners.task_list=Πρόσφατες εργασίες στον εκτελεστή +runners.task_list.no_tasks=Δεν υπάρχει καμία εργασία ακόμα. runners.task_list.run=Εκτέλεση runners.task_list.status=Κατάσταση runners.task_list.repository=Αποθετήριο @@ -3317,16 +3431,49 @@ runners.status.idle=Αδρανής runners.status.active=Ενεργό runners.status.offline=Χωρίς Σύνδεση runners.version=Έκδοση +runners.reset_registration_token=Επαναφορά διακριτικού εγγραφής runners.reset_registration_token_success=Επιτυχής επανέκδοση διακριτικού εγγραφής του εκτελεστή runs.all_workflows=Όλες Οι Ροές Εργασίας runs.commit=Υποβολή +runs.scheduled=Προγραμματισμένα +runs.pushed_by=ωθήθηκε από runs.invalid_workflow_helper=Το αρχείο ροής εργασίας δεν είναι έγκυρο. Ελέγξτε το αρχείο σας: %s +runs.no_matching_online_runner_helper=Κανένας δικτυακός δρομέας με ετικέτα: %s +runs.actor=Φορέας runs.status=Κατάσταση +runs.actors_no_select=Όλοι οι φορείς +runs.status_no_select=Όλες οι καταστάσεις +runs.no_results=Δεν βρέθηκαν αποτελέσματα. +runs.no_workflows=Δεν υπάρχουν ροές εργασίας ακόμα. +runs.no_workflows.quick_start=Δεν ξέρετε πώς να ξεκινήσετε με τις Δράσεις Gitea; Συμβουλευτείτε τον οδηγό για γρήγορη αρχή. +runs.no_workflows.documentation=Για περισσότερες πληροφορίες σχετικά με τη Δράση Gitea, ανατρέξτε στην τεκμηρίωση. +runs.no_runs=Η ροή εργασίας δεν έχει τρέξει ακόμα. +runs.empty_commit_message=(κενό μήνυμα υποβολής) +workflow.disable=Απενεργοποίηση Ροής Εργασιών +workflow.disable_success=Η ροή εργασίας '%s' απενεργοποιήθηκε επιτυχώς. +workflow.enable=Ενεργοποίηση Ροής Εργασίας +workflow.enable_success=Η ροή εργασίας '%s' ενεργοποιήθηκε επιτυχώς. +workflow.disabled=Η ροή εργασιών είναι απενεργοποιημένη. need_approval_desc=Πρέπει να εγκριθεί η εκτέλεση ροών εργασίας για pull request από fork. +variables=Μεταβλητές +variables.management=Διαχείριση Μεταβλητών +variables.creation=Προσθήκη Μεταβλητής +variables.none=Δεν υπάρχουν μεταβλητές ακόμα. +variables.deletion=Αφαίρεση μεταβλητής +variables.deletion.description=Η αφαίρεση μιας μεταβλητής είναι μόνιμη και δεν μπορεί να αναιρεθεί. Συνέχεια; +variables.description=Η μεταβλητές θα δίνονται σε ορισμένες δράσεις και δεν μπορούν να διαβαστούν αλλιώς. +variables.id_not_exist=Η μεταβλητή με id %d δεν υπάρχει. +variables.edit=Επεξεργασία Μεταβλητής +variables.deletion.failed=Αποτυχία αφαίρεσης της μεταβλητής. +variables.deletion.success=Η μεταβλητή έχει αφαιρεθεί. +variables.creation.failed=Αποτυχία προσθήκης μεταβλητής. +variables.creation.success=Η μεταβλητή "%s" έχει προστεθεί. +variables.update.failed=Αποτυχία επεξεργασίας μεταβλητής. +variables.update.success=Η μεταβλητή έχει τροποποιηθεί. [projects] type-1.display_name=Ατομικό Έργο @@ -3334,6 +3481,11 @@ type-2.display_name=Έργο Αποθετηρίου type-3.display_name=Έργο Οργανισμού [git.filemode] +changed_filemode=%[1]s → %[2]s ; Ordered by git filemode value, ascending. E.g. directory has "040000", normal file has "100644", … +directory=Φάκελος +normal_file=Κανονικό αρχείο +executable_file=Εκτελέσιμο αρχείο symbolic_link=Symbolic link +submodule=Υπομονάδα From 9a1d5c549cb6d32219647ea1a771b8a82d5ac89f Mon Sep 17 00:00:00 2001 From: Gwyneth Morgan Date: Sat, 10 Feb 2024 03:40:48 +0000 Subject: [PATCH 021/271] [gitea] Drop "@" from email sender to avoid spam filters (#29109) Commit 360b3fd17c (Include username in email headers (#28981), 2024-02-03) adds usernames to the From field of notification emails in the form of `Display Name (@username)`, to prevent spoofing. However, some email filtering software flags "@" in the display name part of the From field as potential spoofing, as you could set the display name part to another email address than the one you are sending from (e.g. `From: "apparent@email-address" `). To avoid being flagged, instead send emails from `Display Name (username)`. Closes: #29107 --------- Co-authored-by: Giteabot (cherry picked from commit 5b2fd0fc19a2a77414c8e2989b4794b6617221f5) --- models/user/user.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/models/user/user.go b/models/user/user.go index 71f823dc31..0246843c9e 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -436,14 +436,14 @@ func (u *User) GetDisplayName() string { } // GetCompleteName returns the the full name and username in the form of -// "Full Name (@username)" if full name is not empty, otherwise it returns -// "@username". +// "Full Name (username)" if full name is not empty, otherwise it returns +// "username". func (u *User) GetCompleteName() string { trimmedFullName := strings.TrimSpace(u.FullName) if len(trimmedFullName) > 0 { - return fmt.Sprintf("%s (@%s)", trimmedFullName, u.Name) + return fmt.Sprintf("%s (%s)", trimmedFullName, u.Name) } - return fmt.Sprintf("@%s", u.Name) + return u.Name } func gitSafeName(name string) string { From 7c9387a242814994dd64143fcfa3eedd23f680b3 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sat, 10 Feb 2024 14:55:46 +0200 Subject: [PATCH 022/271] [gitea] Make blockquote border size less aggressive (#29124) It's too thick I made it match GitHub's size ![image](https://github.com/go-gitea/gitea/assets/20454870/08c05004-acd9-485e-9219-110d93fe1226) ![image](https://github.com/go-gitea/gitea/assets/20454870/e2e32b6c-4ba8-488e-9405-95d33f80adf7) Signed-off-by: Yarden Shoham (cherry picked from commit 5f5b5ba6e3e50ba5385e6cbf5957d4b73805769b) --- web_src/css/markup/content.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web_src/css/markup/content.css b/web_src/css/markup/content.css index caefa1605c..5eeef078a5 100644 --- a/web_src/css/markup/content.css +++ b/web_src/css/markup/content.css @@ -270,7 +270,7 @@ margin-left: 0; padding: 0 15px; color: var(--color-text-light-2); - border-left: 4px solid var(--color-secondary); + border-left: 0.25em solid var(--color-secondary); } .markup blockquote > :first-child { From 9c4e0c26f9025380cdc5a0488ef2949fdc7576fb Mon Sep 17 00:00:00 2001 From: silverwind Date: Sat, 10 Feb 2024 19:18:46 +0100 Subject: [PATCH 023/271] [gitea] Remove obsolete border-radius on comment content (#29128) This border-radius is obsolete since we changed the comment rendering a few months ago and it caused incorrect display on blockquotes. Before: Screenshot 2024-02-10 at 18 42 48 After: Screenshot 2024-02-10 at 18 42 40 (cherry picked from commit 9063fa096386362f9ae602fdf8a39ae8c972b8e0) --- web_src/css/repo.css | 4 ---- 1 file changed, 4 deletions(-) diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 55c6ec4817..610c3fcb55 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -2289,10 +2289,6 @@ padding: 1em; } -.comment-body .markup { - border-radius: 0 0 var(--border-radius) var(--border-radius); /* don't render outside box */ -} - .edit-label.modal .form .column, .new-label.modal .form .column { padding-right: 0; From 7b4dba3aa07b2a54c922010eab63f61078123ef2 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 11 Feb 2024 13:06:54 +0000 Subject: [PATCH 024/271] [ACTIONS] skip superflous pull request synchronized event (#2314) Skip a HookEventPullRequestSync event if it has the same CommitSHA as an existing HookEventPullRequest event in the ActionRun table. A HookEventPullRequestSync event must only create an ActionRun if the CommitSHA is different from what it was when the PR was open. This guards against a race that can happen when the following is done in parallel: * A commit C is pushed to a repo on branch B * A pull request with head on branch B it is then possible that the pull request is created first, successfully. The commit that was just pushed is not known yet but the PR only references the repository and the B branch so it is fine. A HookEventPullRequest event is sent to the notification queue but not processed immediately. The commit C is pushed and processed successfully. Since the PR already exists and has a head that matches the branch, the head of the PR is updated with the commit C and a HookEventPullRequestSync event is sent to the notification queue. The HookEventPullRequest event is processed and since the head of the PR was updated to be commit C, an ActionRun with CommitSHA C is created. The HookEventPullRequestSync event is then processed and also has a CommitSHA equal to C. Refs: https://codeberg.org/forgejo/forgejo/issues/2009 Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2314 Co-authored-by: Earl Warren Co-committed-by: Earl Warren --- services/actions/main_test.go | 17 ++++++++ services/actions/notifier_helper.go | 23 +++++++++++ services/actions/notifier_helper_test.go | 50 +++++++++++++++++++++++ tests/integration/actions_trigger_test.go | 4 ++ 4 files changed, 94 insertions(+) create mode 100644 services/actions/main_test.go create mode 100644 services/actions/notifier_helper_test.go diff --git a/services/actions/main_test.go b/services/actions/main_test.go new file mode 100644 index 0000000000..ea37ff507a --- /dev/null +++ b/services/actions/main_test.go @@ -0,0 +1,17 @@ +// Copyright 2024 The Forgejo Authors +// SPDX-License-Identifier: MIT + +package actions + +import ( + "testing" + + "code.gitea.io/gitea/models/unittest" + + _ "code.gitea.io/gitea/models/actions" + _ "code.gitea.io/gitea/models/activities" +) + +func TestMain(m *testing.M) { + unittest.MainTest(m) +} diff --git a/services/actions/notifier_helper.go b/services/actions/notifier_helper.go index a5565495d9..46be4ac0a7 100644 --- a/services/actions/notifier_helper.go +++ b/services/actions/notifier_helper.go @@ -152,6 +152,11 @@ func notify(ctx context.Context, input *notifyInput) error { return nil } + if SkipPullRequestEvent(ctx, input.Event, input.Repo.ID, commit.ID.String()) { + log.Trace("repo %s with commit %s skip event %v", input.Repo.RepoPath(), commit.ID, input.Event) + return nil + } + var detectedWorkflows []*actions_module.DetectedWorkflow actionsConfig := input.Repo.MustGetUnit(ctx, unit_model.TypeActions).ActionsConfig() workflows, schedules, err := actions_module.DetectWorkflows(gitRepo, commit, input.Event, input.Payload) @@ -203,6 +208,24 @@ func notify(ctx context.Context, input *notifyInput) error { return handleWorkflows(ctx, detectedWorkflows, commit, input, ref) } +func SkipPullRequestEvent(ctx context.Context, event webhook_module.HookEventType, repoID int64, commitSHA string) bool { + if event != webhook_module.HookEventPullRequestSync { + return false + } + + run := actions_model.ActionRun{ + Event: webhook_module.HookEventPullRequest, + RepoID: repoID, + CommitSHA: commitSHA, + } + exist, err := db.GetEngine(ctx).Exist(&run) + if err != nil { + log.Error("Exist ActionRun %v: %v", run, err) + return false + } + return exist +} + func skipWorkflowsForCommit(input *notifyInput, commit *git.Commit) bool { // skip workflow runs with a configured skip-ci string in commit message if the event is push or pull_request(_sync) // https://docs.github.com/en/actions/managing-workflow-runs/skipping-workflow-runs diff --git a/services/actions/notifier_helper_test.go b/services/actions/notifier_helper_test.go new file mode 100644 index 0000000000..3c23414b8e --- /dev/null +++ b/services/actions/notifier_helper_test.go @@ -0,0 +1,50 @@ +// Copyright 2024 The Forgejo Authors +// SPDX-License-Identifier: MIT + +package actions + +import ( + "testing" + + actions_model "code.gitea.io/gitea/models/actions" + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/unittest" + webhook_module "code.gitea.io/gitea/modules/webhook" + + "github.com/stretchr/testify/assert" +) + +func Test_SkipPullRequestEvent(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + repoID := int64(1) + commitSHA := "1234" + + // event is not webhook_module.HookEventPullRequestSync, never skip + assert.False(t, SkipPullRequestEvent(db.DefaultContext, webhook_module.HookEventPullRequest, repoID, commitSHA)) + + // event is webhook_module.HookEventPullRequestSync but there is nothing in the ActionRun table, do not skip + assert.False(t, SkipPullRequestEvent(db.DefaultContext, webhook_module.HookEventPullRequestSync, repoID, commitSHA)) + + // there is a webhook_module.HookEventPullRequest event but the SHA is different, do not skip + index := int64(1) + run := &actions_model.ActionRun{ + Index: index, + Event: webhook_module.HookEventPullRequest, + RepoID: repoID, + CommitSHA: "othersha", + } + unittest.AssertSuccessfulInsert(t, run) + assert.False(t, SkipPullRequestEvent(db.DefaultContext, webhook_module.HookEventPullRequestSync, repoID, commitSHA)) + + // there already is a webhook_module.HookEventPullRequest with the same SHA, skip + index++ + run = &actions_model.ActionRun{ + Index: index, + Event: webhook_module.HookEventPullRequest, + RepoID: repoID, + CommitSHA: commitSHA, + } + unittest.AssertSuccessfulInsert(t, run) + assert.True(t, SkipPullRequestEvent(db.DefaultContext, webhook_module.HookEventPullRequestSync, repoID, commitSHA)) +} diff --git a/tests/integration/actions_trigger_test.go b/tests/integration/actions_trigger_test.go index 5bcb39fcd5..33c1bef8ad 100644 --- a/tests/integration/actions_trigger_test.go +++ b/tests/integration/actions_trigger_test.go @@ -18,6 +18,8 @@ import ( actions_module "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" + webhook_module "code.gitea.io/gitea/modules/webhook" + actions_service "code.gitea.io/gitea/services/actions" pull_service "code.gitea.io/gitea/services/pull" repo_service "code.gitea.io/gitea/services/repository" files_service "code.gitea.io/gitea/services/repository/files" @@ -120,6 +122,8 @@ func TestPullRequestTargetEvent(t *testing.T) { } err = pull_service.NewPullRequest(git.DefaultContext, baseRepo, pullIssue, nil, nil, pullRequest, nil) assert.NoError(t, err) + // if a PR "synchronized" event races the "opened" event by having the same SHA, it must be skipped. See https://codeberg.org/forgejo/forgejo/issues/2009. + assert.True(t, actions_service.SkipPullRequestEvent(git.DefaultContext, webhook_module.HookEventPullRequestSync, baseRepo.ID, addFileToForkedResp.Commit.SHA)) // load and compare ActionRun assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: baseRepo.ID})) From db3b867b0272b17084ce6485000e8a8f38666c41 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 11 Feb 2024 15:36:58 +0100 Subject: [PATCH 025/271] [RELEASE] Gitea version is for interoperability only Forgejo now has its own lifecycle and its version is derived from the tag. The Gitea tags are no longer found in the Forgejo codebase and can no longer be used for that purpose. When a Forgejo release is published, for interoperability with the existing tools in the ecosystem, it advertises the supported Gitea version via /api/v1/version. It is set in the Makefile manually and cannot be automatically set. https://codeberg.org/forgejo-contrib/delightful-forgejo#packaging Existing Forgejo packages rely on the Makefile to build and the change must be done in a way that tries to not break their assumptions. From the point of view of a Forgejo package build, the following will happen on the next major release: - The package version will bump from v1.21.x to v7.0.0 - /api/v1/version will bump from v1.21.x to v1.22.0 - /api/forgejo/v1/version will bump from v6.x to v7.0.0 The Makefile uses the following variables: GITEA_VERSION is returned by /api/v1/version FORGEJO_VERSION is returned by /api/forgejo/v1/version VERSION is used in the name the binary file and the source archive Before: GITEA_VERSION is the computed version FORGEJO_VERSION is set manually VERSION defaults to GITEA_VERSION After: FORGEJO_VERSION is the computed version GITEA_VERSION is set manually VERSION defaults to FORGEJO_VERSION When the version is computed, it comes from: - The content of the VERSION file if it exists. It is inserted in the source archive because it does not contain a git repository - Otherwise the output of `git describe` --- Makefile | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/Makefile b/Makefile index 6ef4c521c9..ce854272bc 100644 --- a/Makefile +++ b/Makefile @@ -88,14 +88,13 @@ HUGO_VERSION ?= 0.111.3 STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null) ifneq ($(STORED_VERSION),) - GITEA_VERSION ?= $(STORED_VERSION) + FORGEJO_VERSION ?= $(STORED_VERSION) else - GITEA_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//') + FORGEJO_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//') endif -VERSION = ${GITEA_VERSION} +VERSION ?= ${FORGEJO_VERSION} -# SemVer -FORGEJO_VERSION := 7.0.0+0-gitea-1.22.0 +GITEA_VERSION ?= 1.22.0 LDFLAGS := $(LDFLAGS) -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)" -X "main.ForgejoVersion=$(FORGEJO_VERSION)" From 29007c09bd68dc8ce4960e3c91e12fd9698ee713 Mon Sep 17 00:00:00 2001 From: Alexandre Oliveira Date: Sun, 11 Feb 2024 18:02:56 +0000 Subject: [PATCH 026/271] Fix Alpine Registry packages with noarch not being found (#2285) Fixes #2173 ~~Still requires a bit of work to do, I'm not 100% happy with this solution.~~ The idea is to copy the noarch package to the architectures available in the package repository. Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2285 Reviewed-by: Earl Warren Co-authored-by: Alexandre Oliveira Co-committed-by: Alexandre Oliveira --- routers/api/packages/alpine/alpine.go | 115 +++++--- tests/integration/api_packages_alpine_test.go | 262 ++++++++++++++++++ 2 files changed, 337 insertions(+), 40 deletions(-) diff --git a/routers/api/packages/alpine/alpine.go b/routers/api/packages/alpine/alpine.go index bb14c5163a..3701fc97c9 100644 --- a/routers/api/packages/alpine/alpine.go +++ b/routers/api/packages/alpine/alpine.go @@ -14,6 +14,7 @@ import ( "strings" packages_model "code.gitea.io/gitea/models/packages" + alpine_model "code.gitea.io/gitea/models/packages/alpine" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/json" packages_module "code.gitea.io/gitea/modules/packages" @@ -30,6 +31,53 @@ func apiError(ctx *context.Context, status int, obj any) { }) } +func createOrAddToExisting(ctx *context.Context, pck *alpine_module.Package, branch, repository, architecture string, buf packages_module.HashedSizeReader, fileMetadataRaw []byte) { + _, _, err := packages_service.CreatePackageOrAddFileToExisting( + ctx, + &packages_service.PackageCreationInfo{ + PackageInfo: packages_service.PackageInfo{ + Owner: ctx.Package.Owner, + PackageType: packages_model.TypeAlpine, + Name: pck.Name, + Version: pck.Version, + }, + Creator: ctx.Doer, + Metadata: pck.VersionMetadata, + }, + &packages_service.PackageFileCreationInfo{ + PackageFileInfo: packages_service.PackageFileInfo{ + Filename: fmt.Sprintf("%s-%s.apk", pck.Name, pck.Version), + CompositeKey: fmt.Sprintf("%s|%s|%s", branch, repository, architecture), + }, + Creator: ctx.Doer, + Data: buf, + IsLead: true, + Properties: map[string]string{ + alpine_module.PropertyBranch: branch, + alpine_module.PropertyRepository: repository, + alpine_module.PropertyArchitecture: architecture, + alpine_module.PropertyMetadata: string(fileMetadataRaw), + }, + }, + ) + if err != nil { + switch err { + case packages_model.ErrDuplicatePackageVersion, packages_model.ErrDuplicatePackageFile: + apiError(ctx, http.StatusConflict, err) + case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize: + apiError(ctx, http.StatusForbidden, err) + default: + apiError(ctx, http.StatusInternalServerError, err) + } + return + } + + if err := alpine_service.BuildSpecificRepositoryFiles(ctx, ctx.Package.Owner.ID, branch, repository, pck.FileMetadata.Architecture); err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } +} + func GetRepositoryKey(ctx *context.Context) { _, pub, err := alpine_service.GetOrCreateKeyPair(ctx, ctx.Package.Owner.ID) if err != nil { @@ -133,49 +181,36 @@ func UploadPackageFile(ctx *context.Context) { return } - _, _, err = packages_service.CreatePackageOrAddFileToExisting( - ctx, - &packages_service.PackageCreationInfo{ - PackageInfo: packages_service.PackageInfo{ - Owner: ctx.Package.Owner, - PackageType: packages_model.TypeAlpine, - Name: pck.Name, - Version: pck.Version, - }, - Creator: ctx.Doer, - Metadata: pck.VersionMetadata, - }, - &packages_service.PackageFileCreationInfo{ - PackageFileInfo: packages_service.PackageFileInfo{ - Filename: fmt.Sprintf("%s-%s.apk", pck.Name, pck.Version), - CompositeKey: fmt.Sprintf("%s|%s|%s", branch, repository, pck.FileMetadata.Architecture), - }, - Creator: ctx.Doer, - Data: buf, - IsLead: true, - Properties: map[string]string{ - alpine_module.PropertyBranch: branch, - alpine_module.PropertyRepository: repository, - alpine_module.PropertyArchitecture: pck.FileMetadata.Architecture, - alpine_module.PropertyMetadata: string(fileMetadataRaw), - }, - }, - ) - if err != nil { - switch err { - case packages_model.ErrDuplicatePackageVersion, packages_model.ErrDuplicatePackageFile: - apiError(ctx, http.StatusConflict, err) - case packages_service.ErrQuotaTotalCount, packages_service.ErrQuotaTypeSize, packages_service.ErrQuotaTotalSize: - apiError(ctx, http.StatusForbidden, err) - default: + // Check whether the package being uploaded has no architecture defined. + // If true, loop through the available architectures in the repo and create + // the package file for the each architecture. If there are no architectures + // available on the repository, fallback to x86_64 + if pck.FileMetadata.Architecture == "noarch" { + architectures, err := alpine_model.GetArchitectures(ctx, ctx.Package.Owner.ID, repository) + if err != nil { apiError(ctx, http.StatusInternalServerError, err) + return } - return - } - if err := alpine_service.BuildSpecificRepositoryFiles(ctx, ctx.Package.Owner.ID, branch, repository, pck.FileMetadata.Architecture); err != nil { - apiError(ctx, http.StatusInternalServerError, err) - return + if len(architectures) == 0 { + architectures = []string{ + "x86_64", + } + } + + for _, arch := range architectures { + pck.FileMetadata.Architecture = arch + + fileMetadataRaw, err := json.Marshal(pck.FileMetadata) + if err != nil { + apiError(ctx, http.StatusInternalServerError, err) + return + } + + createOrAddToExisting(ctx, pck, branch, repository, pck.FileMetadata.Architecture, buf, fileMetadataRaw) + } + } else { + createOrAddToExisting(ctx, pck, branch, repository, pck.FileMetadata.Architecture, buf, fileMetadataRaw) } ctx.Status(http.StatusCreated) diff --git a/tests/integration/api_packages_alpine_test.go b/tests/integration/api_packages_alpine_test.go index 3cc7178e02..ea9735236b 100644 --- a/tests/integration/api_packages_alpine_test.go +++ b/tests/integration/api_packages_alpine_test.go @@ -227,3 +227,265 @@ Djfa/2q5bH4699v++uMAAAAAAAAAAAAAAAAAAAAAAHbgA/eXQh8AKAAA` } }) } + +func TestPackageAlpineNoArch(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + packageNames := []string{"forgejo-noarch-test", "forgejo-noarch-test-openrc"} + packageVersion := "1.0.0-r0" + + base64AlpinePackageContent := `H4sIAAAAAAACA9ML9nT30wsKdtSryMxLrExJLdIrKk7UKyhNYqAaMAACMxMTMA0E6LQhiG1oYmpm +ZGhqZGBkzmBgaGRsaM6gYMBAB1BaXJJYpKDAMEKBxuPYoD/96z0zNn4N0464vt6n6JW44rN8ppVn +DtjwvbEVF3xzIV5uT1rSlI7S7Qq75j/z29q5ZoeawuaviYKTK/cYCX/Zuzhi1h1Pjk31NWyJfvts +665n++ytWf6aoSylw+xYXv57tTdHPt7duGfS0oS+N8E/XVXnSqueii/8FKF6XURDXyj8gv27ORk8 +v8M8znXGXNze/lzwlKyTuXqc6svbH/7u6pv0uHGrjcEavud5PL8krmQ4bn3zt3Jeh9y6WTJfvcLn +5uy9s9vFyqSHh1dZiCOwqVCjg3nljDWs/06eTfQSuslTeE9pRUP1u6Yq69LDUxvenFmadW5y5cYN +P/+IMJx/pb8hNvDKimVlKT2dLlZNkkq+Z9eytdhlWjakXP/JMe15zOc1s9+4G4RMf33E/kzF55Lz +za7vP9cb8FkL6W3mvfYvzf7LjB1/8pes7NSOzzu6q17GSuZKmuA8fpFnpWuTVjst73gqctl1V6eW +irR9av9Rqcb4Lwyx34xDtWoTTvYvCdfxL3+hyNu2p1550dcEjZrJvKX7d9+wNmpJelfAuvKnzeXe +SvUbyuybQs4eefFb/IVlnFXkjyY7ma6tz3Rlrnw6nl2tXdg9o2wW26GTrm9nLvE0Xrj5XM9MVuFM +rhrGubNe8O4JrW12cTJaaLTreWXyep2Pb4/f49oQkFu67neQ0t4lt2uyXZQ+bn1dEeKy/L3292cA +2zwJWgAEAAAfiwgAAAAAAAID7VVdr9MwDO1zfkWkPa9L2vRjFUMgpA0Egklwn5GbpF1Ym0xpOnb5 +9bjbxMOVACHBldDuqSo7sWufOLIbL7Zweq1BaT8s4u3bzZv36w/R3wVD5EKcJeKhZCzJIy6yPOFZ +wpIiYpynRRbRU/QIGIcAHqlEtwnOqQym1ytGUIWrGj3hRvCPWv5P+p/j86D/WcHyiLLH7H/vXPiV +3+/s/ylmdKOt9hC0ovU9hXo0naJpzJOYzT0jMzoOxra0gb2eSkCP+KMwzlIep0nM0f5xtHSta4rj +g4uKZRUqd59eUbxKQQ771kKv6Yo2zrf6i5tbB17u5kEPYbJiODTymF3S4Y7Sg8St9cWfzhJepNTr +g3dqxFHlLBl9hw67EA5DtVhcA8coyJm9wsNMMQtW5DlLOScHkHtoz5nu7N66r5YM5tvklDBRMjIx +wsWFGnHetMb+hLJ0fW8CGkkPxgZ8z2FfdvoEVnmNWq+NAvqsNeEFOLgsY/zuOemM1HaY8m62744p +Fg/G4HqcuhK67p4qHbTEm6gInvZosBLoKntVXZl8nmqx+lEsPCjsYJioC2A1k1m25KWq67JcJk2u +5FJKIZXgItWsgbqsdZoL1bAmF0wsVV4XDVcNB8ieJv6N4jubJ8CtAAoAAB+LCAAAAAAAAgPt1r9K +w0AcB/CbO3eWWBcX2/ufdChYBCkotFChiyDX5GqrrZGkha5uPoe4+AC+gG/j4OrstTjUgErRRku/ +nyVHEkjg8v3+Uq60zLRhTWSTtDJJE7IC1NFSzo9O9kgp14RJpTlTnHKfUMaoEMSbkhxM0rFJ3KuQ +zcSYF44HI1ujBbc070sCG8JFvrLqZ8wi7iv1ef7d+mP+qRSMeCrP/CdxPP7qvu+ur/H+L0yA7uDq +X/S/lBr9j/6HPPLvQr/SGbB8/zNO0f+57v/CDOjFybm9iM8480Uu/c8Ez+y/UAr//3/Y/zrw6q2j +vZNm87hdDvs2vEwno3K7UWc1Iw1341kw21U26mkeBIFPlW+rmkktopAHTIWmihmyVvn/9dAv0/8i +8//Hqe9OebNMus+Q75Miub8rHmw9vrzu3l53ns1h7enm9AH9/3M72/PtT/uFgg37sVdq2OEw9jpx +MoxKyDAAAAAAAAAAAADA2noDOINxQwAoAAA=` + content, err := base64.StdEncoding.DecodeString(base64AlpinePackageContent) + assert.NoError(t, err) + + packageContents := map[string][]byte{} + packageContents["forgejo-noarch-test"] = content + + base64AlpinePackageContent = `H4sIAAAAAAACA9ML9nT30wsKdtSryMxLrExJLdIrKk7UKyhNYqAaMAACMxMTMA0E6LQhiG1oYmpm +ZGhqZGBkzmBgaGRsaM6gYMBAB1BaXJJYpKDAMEJBV8/bw4880tiXWbav8ygSDheyNpq/YubDz3sy +FI+wSHHGpNtx/wpYGTCzVFxz2/pdCvcWzJ3gY2k2L5I7dfvG43G+ja0KkSwPedaI8/05HFGq9ru0 +ye/lIfvchSobf0lGnFr8SWmnXR0DayuTQu70y3wRDR9ltIQ3OE6X2PZs2kv3tKerFs3YkL2XPyPx +h8TGDV8EFtwLXO35KOdkp/yS817if/vC9/J1bfzBXa8Y8mBvzd0dP5p5HkprPls69e0d39anVa9a ++7n2P1Uw0fyoIcP5zn8NS+blmRzXrrxMNpR8Lif37J/GbDWDyocte6f/fjYz62Lw+hPxt7/buhkJ +lJ742LRi+idxvn8B2tGB/Sotkle9Pb1XtJq912V6PHGSmWEie1WIeMvnY6pCPCt366o6uOSv7d4j +0qv2j2vps3tsCw7NnjU/+ixj1aK+GQLWy2+elC1fuL3iQsmatsb6WbGqz2bEvdwzXWhi5lO7C24S +TJt4jjBFff3Y++/P/NvhjakNT294TLnRJZrsHto4cYeSqlPsyhrPz/d0LmmbKeVu6CgMTNpuMl3U +ceaNiqs/xFSevWlUeSl7JT9dTHVi8MqmwPTlXkXF0jGbfioscdJg/cTwa39/jPzxnJ9vw101502Y +XXIpq0jgzsYT20SXnp5l2fZqF/MtG7mCju+uL9nO6Bro7taZnzJlyre/f9pP+Vb058+Sdv3zWHQD +AJIwfO8ABAAAH4sIAAAAAAACA+1V3W/TMBDPs/+Kk/oCD03tfDhtRRFoUgsCsQrYM3KcS2qa2JHj +jG1/Pdd24mGaQEgwCbafFN35fOf7cO4cz7bq6g2qCv0wi7fvNm8/rM+jPwtOkFl2pIS7lPNERiLL +ZSLyhCdFxIVIizyCq+gBMA5BeQolepwQAnQwHa44I1bdstETHgn+Usv/Tv8LLsWd/ueFyCLgD9n/ +3rnwM71f7f+jmMAGLXoVsILyGlQ5mraCNBZJzKeeswmMg7EN1GqPhxLAJT0UxlkQcZrEgvY/jRbW +WAKND5Eteb4k5uLzGdBVZqzfN1Z1CCuonW/wq5tap7zeTQMOYep6tF4flOhU0hExP3klSYWDJtH6 +ZAaTRBQpeOy9q0aaWBTBs3My/3gGxpoAg/amD8NzNvqWzHYh9MNyNrv1GhNhx9QqyvTgqeCFlDwV +gvVK71Vz9H9h99Z9s2wwN0clmc4zdgiXFqe4mfOmMfb+fJh2XUexrIB1ythA3/HY1y1eKVt5JK5D +Uyl40ZjwSjl1WsZk95K1RqMdDn432/eXKTOW/sy2/WJqEp0qdZ/T1Y+iTUCNwXU0wzXZXUOFATXd +65JR0mqnhkMai0xX1Iyasi8xSzGpy1woqoQUUhYokoVKRb6Qc6VLuShzFJmUtcwWRbGY10n69DT8 +X/gOnWH3xAAKAAAfiwgAAAAAAAID7dVNSsNAGAbg2bgwbnuAWDcKNpmZzCTpImAXhYJCC3Uv+Zlo +lCSSHyiKK8G1C8/gGbyLp9ADiBN1UQsqRZNa+j2bGZJAApP3/TR95E4Gwg1Eluui8FENsGQy9rZK +syvG1ESEcZMSTjG1ECbYsjhSJ6gBZV64mfwUtJoIUf0iioWDFbl1P7YIrAgZeb3ud1QRtzj/Ov9y +P5N/wzKQypvMf5amxXfP/XR/ic9/agJESVRowcL7Xy7Q/9D/oJH8v4e+vjEwf/8TbmLo/4bPf2oM +hGl2LE7TI0rkHK69/4lBZ86fVZeg/xfW/6at9kb7ncPh8GCs+SfCP8vLWBsPesTxbMZDEZIuDjzq +W9xysWebmBuBbbgm44R1mWGHFGbIsuX/b0M/R/8Twj7nnxJS9X+VSfkb0j3UQg/9l6fbx93yYuNm +zbm+77fu7Gfo/9/b2tRzL0r09Fwkmd/JykRR/DSO3SRw2nqZZ3p1d/rXaCtKIOTTwfaOeqmsJ0IE +aiIK5QoSDwAAAAAAAAAAAAAAAP/IK49O1e8AKAAA` + content, err = base64.StdEncoding.DecodeString(base64AlpinePackageContent) + assert.NoError(t, err) + + packageContents["forgejo-noarch-test-openrc"] = content + + branches := []string{"v3.16", "v3.17", "v3.18"} + repositories := []string{"main", "testing"} + + rootURL := fmt.Sprintf("/api/packages/%s/alpine", user.Name) + + t.Run("RepositoryKey", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", rootURL+"/key") + resp := MakeRequest(t, req, http.StatusOK) + + assert.Equal(t, "application/x-pem-file", resp.Header().Get("Content-Type")) + assert.Contains(t, resp.Body.String(), "-----BEGIN PUBLIC KEY-----") + }) + + for _, branch := range branches { + for _, repository := range repositories { + t.Run(fmt.Sprintf("[Branch:%s,Repository:%s]", branch, repository), func(t *testing.T) { + for _, pkg := range packageNames { + t.Run(fmt.Sprintf("Upload[Package:%s]", pkg), func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + uploadURL := fmt.Sprintf("%s/%s/%s", rootURL, branch, repository) + + req := NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{})) + MakeRequest(t, req, http.StatusUnauthorized) + + req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader([]byte{})). + AddBasicAuth(user.Name) + MakeRequest(t, req, http.StatusBadRequest) + + req = NewRequestWithBody(t, "PUT", uploadURL, bytes.NewReader(packageContents[pkg])). + AddBasicAuth(user.Name) + MakeRequest(t, req, http.StatusCreated) + + pvs, err := packages.GetVersionsByPackageName(db.DefaultContext, user.ID, packages.TypeAlpine, pkg) + assert.NoError(t, err) + assert.Len(t, pvs, 1) + + pd, err := packages.GetPackageDescriptor(db.DefaultContext, pvs[0]) + assert.NoError(t, err) + assert.Nil(t, pd.SemVer) + assert.IsType(t, &alpine_module.VersionMetadata{}, pd.Metadata) + assert.Equal(t, pkg, pd.Package.Name) + assert.Equal(t, packageVersion, pd.Version.Version) + + pfs, err := packages.GetFilesByVersionID(db.DefaultContext, pvs[0].ID) + assert.NoError(t, err) + assert.NotEmpty(t, pfs) + assert.Condition(t, func() bool { + seen := false + expectedFilename := fmt.Sprintf("%s-%s.apk", pkg, packageVersion) + expectedCompositeKey := fmt.Sprintf("%s|%s|x86_64", branch, repository) + for _, pf := range pfs { + if pf.Name == expectedFilename && pf.CompositeKey == expectedCompositeKey { + if seen { + return false + } + seen = true + + assert.True(t, pf.IsLead) + + pfps, err := packages.GetProperties(db.DefaultContext, packages.PropertyTypeFile, pf.ID) + assert.NoError(t, err) + + for _, pfp := range pfps { + switch pfp.Name { + case alpine_module.PropertyBranch: + assert.Equal(t, branch, pfp.Value) + case alpine_module.PropertyRepository: + assert.Equal(t, repository, pfp.Value) + case alpine_module.PropertyArchitecture: + assert.Equal(t, "x86_64", pfp.Value) + } + } + } + } + return seen + }) + }) + } + + t.Run("Index", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + url := fmt.Sprintf("%s/%s/%s/x86_64/APKINDEX.tar.gz", rootURL, branch, repository) + + req := NewRequest(t, "GET", url) + resp := MakeRequest(t, req, http.StatusOK) + + assert.Condition(t, func() bool { + br := bufio.NewReader(resp.Body) + + gzr, err := gzip.NewReader(br) + assert.NoError(t, err) + + for { + gzr.Multistream(false) + + tr := tar.NewReader(gzr) + for { + hd, err := tr.Next() + if err == io.EOF { + break + } + assert.NoError(t, err) + + if hd.Name == "APKINDEX" { + buf, err := io.ReadAll(tr) + assert.NoError(t, err) + + s := string(buf) + + assert.Contains(t, s, "C:Q14rbX8G4tErQO98k5J4uHsNaoiqk=\n") + assert.Contains(t, s, "P:"+packageNames[0]+"\n") + assert.Contains(t, s, "V:"+packageVersion+"\n") + assert.Contains(t, s, "A:x86_64\n") + assert.Contains(t, s, "T:Forgejo #2173 reproduction\n") + assert.Contains(t, s, "U:https://forgejo.org\n") + assert.Contains(t, s, "L:GPLv3\n") + assert.Contains(t, s, "S:1508\n") + assert.Contains(t, s, "I:20480\n") + assert.Contains(t, s, "o:forgejo-noarch-test\n") + assert.Contains(t, s, "m:Alexandre Almeida \n") + assert.Contains(t, s, "t:1707660311\n") + assert.Contains(t, s, "p:cmd:forgejo_2173=1.0.0-r0") + + assert.Contains(t, s, "C:Q1zTXZP03UbSled31mi4MXmsrgNQ4=\n") + assert.Contains(t, s, "P:"+packageNames[1]+"\n") + assert.Contains(t, s, "V:"+packageVersion+"\n") + assert.Contains(t, s, "A:x86_64\n") + assert.Contains(t, s, "T:Forgejo #2173 reproduction (OpenRC init scripts)\n") + assert.Contains(t, s, "U:https://forgejo.org\n") + assert.Contains(t, s, "L:GPLv3\n") + assert.Contains(t, s, "S:1569\n") + assert.Contains(t, s, "I:16384\n") + assert.Contains(t, s, "o:forgejo-noarch-test\n") + assert.Contains(t, s, "m:Alexandre Almeida \n") + assert.Contains(t, s, "t:1707660311\n") + assert.Contains(t, s, "i:openrc forgejo-noarch-test=1.0.0-r0") + + return true + } + } + + err = gzr.Reset(br) + if err == io.EOF { + break + } + assert.NoError(t, err) + } + + return false + }) + }) + + t.Run("Download", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/x86_64/%s-%s.apk", rootURL, branch, repository, packageNames[0], packageVersion)) + MakeRequest(t, req, http.StatusOK) + }) + }) + } + } + + t.Run("Delete", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + for _, branch := range branches { + for _, repository := range repositories { + for _, pkg := range packageNames { + req := NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s/x86_64/%s-%s.apk", rootURL, branch, repository, pkg, packageVersion)) + MakeRequest(t, req, http.StatusUnauthorized) + + req = NewRequest(t, "DELETE", fmt.Sprintf("%s/%s/%s/x86_64/%s-%s.apk", rootURL, branch, repository, pkg, packageVersion)). + AddBasicAuth(user.Name) + MakeRequest(t, req, http.StatusNoContent) + + } + // Deleting the last file of an architecture should remove that index + req := NewRequest(t, "GET", fmt.Sprintf("%s/%s/%s/x86_64/APKINDEX.tar.gz", rootURL, branch, repository)) + MakeRequest(t, req, http.StatusNotFound) + } + } + }) +} From cbbcfdd400ae6f38e57f957c6dee0e08b3895d14 Mon Sep 17 00:00:00 2001 From: Gusted Date: Mon, 12 Feb 2024 23:11:50 +0100 Subject: [PATCH 027/271] [FEAT] Don't log context cancelled SQL errors - I found this while doing some unrelated testing in Forgejo. It wasn't my intention to log failed SQL queries if they were cancelled (which can happen quite frequently for larger instances) as in those cases it's not interesting to know which SQL query was run. My intentation was only to log an SQL query if there was an error reported by the database. - Ref #2140 --- models/db/engine.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/models/db/engine.go b/models/db/engine.go index 41207674e4..f1162ebd6e 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -7,6 +7,7 @@ package db import ( "context" "database/sql" + "errors" "fmt" "io" "reflect" @@ -342,7 +343,7 @@ func (ErrorQueryHook) BeforeProcess(c *contexts.ContextHook) (context.Context, e } func (h *ErrorQueryHook) AfterProcess(c *contexts.ContextHook) error { - if c.Err != nil { + if c.Err != nil && !errors.Is(c.Err, context.Canceled) { h.Logger.Log(8, log.ERROR, "[Error SQL Query] %s %v - %v", c.SQL, c.Args, c.Err) } return nil From f68f88097491746c6d4a36dc8f9631f764f65ef1 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 13 Feb 2024 09:03:22 +0100 Subject: [PATCH 028/271] [BUG] Workaround borked Git version - In Git version v2.43.1, the behavior of `GIT_FLUSH` was accidentially flipped. This causes Forgejo to hang on the `check-attr` command, because no output was being flushed. - Workaround this by detecting if Git v2.43.1 is used and set `GIT_FLUSH=0` thus getting the correct behavior. - Ref: https://lore.kernel.org/git/CABn0oJvg3M_kBW-u=j3QhKnO=6QOzk-YFTgonYw_UvFS1NTX4g@mail.gmail.com/ - Resolves #2333. --- modules/git/git.go | 22 ++++++++++++++++++++-- modules/git/repo_attribute.go | 8 +++++++- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/modules/git/git.go b/modules/git/git.go index 89c23ff230..13a3127498 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -33,8 +33,9 @@ var ( // DefaultContext is the default context to run git commands in, must be initialized by git.InitXxx DefaultContext context.Context - SupportProcReceive bool // >= 2.29 - SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an ‘experimental curiosity’ + SupportProcReceive bool // >= 2.29 + SupportHashSha256 bool // >= 2.42, SHA-256 repositories no longer an ‘experimental curiosity’ + InvertedGitFlushEnv bool // 2.43.1 gitVersion *version.Version ) @@ -192,6 +193,8 @@ func InitFull(ctx context.Context) (err error) { log.Warn("sha256 hash support is disabled - requires Git >= 2.42. Gogit is currently unsupported") } + InvertedGitFlushEnv = CheckGitVersionEqual("2.43.1") == nil + if setting.LFS.StartServer { if CheckGitVersionAtLeast("2.1.2") != nil { return errors.New("LFS server support requires Git >= 2.1.2") @@ -320,6 +323,21 @@ func CheckGitVersionAtLeast(atLeast string) error { return nil } +// CheckGitVersionEqual checks if the git version is equal to the constraint version. +func CheckGitVersionEqual(equal string) error { + if _, err := loadGitVersion(); err != nil { + return err + } + atLeastVersion, err := version.NewVersion(equal) + if err != nil { + return err + } + if !gitVersion.Equal(atLeastVersion) { + return fmt.Errorf("installed git binary version %s is not equal to %s", gitVersion.Original(), equal) + } + return nil +} + func configSet(key, value string) error { stdout, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key).RunStdString(nil) if err != nil && !err.IsExitCode(1) { diff --git a/modules/git/repo_attribute.go b/modules/git/repo_attribute.go index 3c5a1429a9..3e09828ec5 100644 --- a/modules/git/repo_attribute.go +++ b/modules/git/repo_attribute.go @@ -133,7 +133,13 @@ func (c *CheckAttributeReader) Init(ctx context.Context) error { c.env = append(c.env, "GIT_WORK_TREE="+c.WorkTree) } - c.env = append(c.env, "GIT_FLUSH=1") + // Version 2.43.1 has a bug where the behavior of `GIT_FLUSH` is flipped. + // Ref: https://lore.kernel.org/git/CABn0oJvg3M_kBW-u=j3QhKnO=6QOzk-YFTgonYw_UvFS1NTX4g@mail.gmail.com + if InvertedGitFlushEnv { + c.env = append(c.env, "GIT_FLUSH=0") + } else { + c.env = append(c.env, "GIT_FLUSH=1") + } c.cmd.AddDynamicArguments(c.Attributes...) From fa73375e139d159caf697897b88068a2c66a48eb Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 23 Jan 2024 10:46:34 +0100 Subject: [PATCH 029/271] Split out repository unit settings This splits out the repository unit settings (formerly "Advanced settings" under the repository settings page) into their own, separate page. The primary reason for this is that the settings page became long and complicated, with a structure that not always made sense. A secondary reason is that toggling units on and off should not necessarily be an "advanced" setting. We want to make doing that easier, and having the units on their own page helps with that. This is basically a refactor, there is no new functionality introduced, just an extra pair of routes for the new page, and the supporting code. Signed-off-by: Gergely Nagy --- options/locale/locale_en-US.ini | 3 + routers/web/repo/setting/setting.go | 378 ++++++++++---------- routers/web/web.go | 2 + services/forms/repo_form.go | 28 +- templates/repo/settings/navbar.tmpl | 17 + templates/repo/settings/options.tmpl | 319 ----------------- templates/repo/settings/units.tmpl | 13 + templates/repo/settings/units/issues.tmpl | 102 ++++++ templates/repo/settings/units/overview.tmpl | 62 ++++ templates/repo/settings/units/pulls.tmpl | 121 +++++++ templates/repo/settings/units/wiki.tmpl | 51 +++ 11 files changed, 586 insertions(+), 510 deletions(-) create mode 100644 templates/repo/settings/units.tmpl create mode 100644 templates/repo/settings/units/issues.tmpl create mode 100644 templates/repo/settings/units/overview.tmpl create mode 100644 templates/repo/settings/units/pulls.tmpl create mode 100644 templates/repo/settings/units/wiki.tmpl diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 95ce92e882..be2a06b38f 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2066,6 +2066,9 @@ settings.mirror_settings.push_mirror.remote_url = Git Remote Repository URL settings.mirror_settings.push_mirror.add = Add Push Mirror settings.mirror_settings.push_mirror.edit_sync_time = Edit mirror sync interval +settings.units.units = Repository Units +settings.units.overview = Overview + settings.sync_mirror = Synchronize Now settings.pull_mirror_sync_in_progress = Pulling changes from the remote %s at the moment. settings.push_mirror_sync_in_progress = Pushing changes to the remote %s at the moment. diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 552507e57c..8a429c359c 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -41,6 +41,7 @@ import ( const ( tplSettingsOptions base.TplName = "repo/settings/options" + tplSettingsUnits base.TplName = "repo/settings/units" tplCollaboration base.TplName = "repo/settings/collaboration" tplBranches base.TplName = "repo/settings/branches" tplGithooks base.TplName = "repo/settings/githooks" @@ -89,6 +90,201 @@ func SettingsCtxData(ctx *context.Context) { ctx.Data["PushMirrors"] = pushMirrors } +// Units show a repositorys unit settings page +func Units(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.settings.units.units") + ctx.Data["PageIsRepoSettingsUnits"] = true + + ctx.HTML(http.StatusOK, tplSettingsUnits) +} + +func UnitsPost(ctx *context.Context) { + form := web.GetForm(ctx).(*forms.RepoUnitSettingForm) + + repo := ctx.Repo.Repository + + var repoChanged bool + var units []repo_model.RepoUnit + var deleteUnitTypes []unit_model.Type + + // This section doesn't require repo_name/RepoName to be set in the form, don't show it + // as an error on the UI for this action + ctx.Data["Err_RepoName"] = nil + + if repo.CloseIssuesViaCommitInAnyBranch != form.EnableCloseIssuesViaCommitInAnyBranch { + repo.CloseIssuesViaCommitInAnyBranch = form.EnableCloseIssuesViaCommitInAnyBranch + repoChanged = true + } + + if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() { + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypeCode, + }) + } else if !unit_model.TypeCode.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode) + } + + if form.EnableWiki && form.EnableExternalWiki && !unit_model.TypeExternalWiki.UnitGlobalDisabled() { + if !validation.IsValidExternalURL(form.ExternalWikiURL) { + ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error")) + ctx.Redirect(repo.Link() + "/settings/units") + return + } + + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypeExternalWiki, + Config: &repo_model.ExternalWikiConfig{ + ExternalWikiURL: form.ExternalWikiURL, + }, + }) + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki) + } else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() { + var wikiPermissions repo_model.UnitAccessMode + if form.GloballyWriteableWiki { + wikiPermissions = repo_model.UnitAccessModeWrite + } else { + wikiPermissions = repo_model.UnitAccessModeRead + } + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypeWiki, + Config: new(repo_model.UnitConfig), + DefaultPermissions: wikiPermissions, + }) + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki) + } else { + if !unit_model.TypeExternalWiki.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki) + } + if !unit_model.TypeWiki.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki) + } + } + + if form.EnableIssues && form.EnableExternalTracker && !unit_model.TypeExternalTracker.UnitGlobalDisabled() { + if !validation.IsValidExternalURL(form.ExternalTrackerURL) { + ctx.Flash.Error(ctx.Tr("repo.settings.external_tracker_url_error")) + ctx.Redirect(repo.Link() + "/settings/units") + return + } + if len(form.TrackerURLFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(form.TrackerURLFormat) { + ctx.Flash.Error(ctx.Tr("repo.settings.tracker_url_format_error")) + ctx.Redirect(repo.Link() + "/settings/units") + return + } + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypeExternalTracker, + Config: &repo_model.ExternalTrackerConfig{ + ExternalTrackerURL: form.ExternalTrackerURL, + ExternalTrackerFormat: form.TrackerURLFormat, + ExternalTrackerStyle: form.TrackerIssueStyle, + ExternalTrackerRegexpPattern: form.ExternalTrackerRegexpPattern, + }, + }) + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues) + } else if form.EnableIssues && !form.EnableExternalTracker && !unit_model.TypeIssues.UnitGlobalDisabled() { + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypeIssues, + Config: &repo_model.IssuesConfig{ + EnableTimetracker: form.EnableTimetracker, + AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime, + EnableDependencies: form.EnableIssueDependencies, + }, + }) + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) + } else { + if !unit_model.TypeExternalTracker.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) + } + if !unit_model.TypeIssues.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues) + } + } + + if form.EnableProjects && !unit_model.TypeProjects.UnitGlobalDisabled() { + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypeProjects, + }) + } else if !unit_model.TypeProjects.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects) + } + + if form.EnableReleases && !unit_model.TypeReleases.UnitGlobalDisabled() { + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypeReleases, + }) + } else if !unit_model.TypeReleases.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeReleases) + } + + if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() { + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypePackages, + }) + } else if !unit_model.TypePackages.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages) + } + + if form.EnableActions && !unit_model.TypeActions.UnitGlobalDisabled() { + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypeActions, + }) + } else if !unit_model.TypeActions.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeActions) + } + + if form.EnablePulls && !unit_model.TypePullRequests.UnitGlobalDisabled() { + units = append(units, repo_model.RepoUnit{ + RepoID: repo.ID, + Type: unit_model.TypePullRequests, + Config: &repo_model.PullRequestsConfig{ + IgnoreWhitespaceConflicts: form.PullsIgnoreWhitespace, + AllowMerge: form.PullsAllowMerge, + AllowRebase: form.PullsAllowRebase, + AllowRebaseMerge: form.PullsAllowRebaseMerge, + AllowSquash: form.PullsAllowSquash, + AllowManualMerge: form.PullsAllowManualMerge, + AutodetectManualMerge: form.EnableAutodetectManualMerge, + AllowRebaseUpdate: form.PullsAllowRebaseUpdate, + DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge, + DefaultMergeStyle: repo_model.MergeStyle(form.PullsDefaultMergeStyle), + DefaultAllowMaintainerEdit: form.DefaultAllowMaintainerEdit, + }, + }) + } else if !unit_model.TypePullRequests.UnitGlobalDisabled() { + deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests) + } + + if len(units) == 0 { + ctx.Flash.Error(ctx.Tr("repo.settings.update_settings_no_unit")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/units") + return + } + + if err := repo_model.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil { + ctx.ServerError("UpdateRepositoryUnits", err) + return + } + if repoChanged { + if err := repo_service.UpdateRepository(ctx, repo, false); err != nil { + ctx.ServerError("UpdateRepository", err) + return + } + } + log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) + + ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) + ctx.Redirect(ctx.Repo.RepoLink + "/settings/units") +} + // Settings show a repository's settings page func Settings(ctx *context.Context) { ctx.HTML(http.StatusOK, tplSettingsOptions) @@ -435,188 +631,6 @@ func SettingsPost(ctx *context.Context) { ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) ctx.Redirect(repo.Link() + "/settings") - case "advanced": - var repoChanged bool - var units []repo_model.RepoUnit - var deleteUnitTypes []unit_model.Type - - // This section doesn't require repo_name/RepoName to be set in the form, don't show it - // as an error on the UI for this action - ctx.Data["Err_RepoName"] = nil - - if repo.CloseIssuesViaCommitInAnyBranch != form.EnableCloseIssuesViaCommitInAnyBranch { - repo.CloseIssuesViaCommitInAnyBranch = form.EnableCloseIssuesViaCommitInAnyBranch - repoChanged = true - } - - if form.EnableCode && !unit_model.TypeCode.UnitGlobalDisabled() { - units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypeCode, - }) - } else if !unit_model.TypeCode.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeCode) - } - - if form.EnableWiki && form.EnableExternalWiki && !unit_model.TypeExternalWiki.UnitGlobalDisabled() { - if !validation.IsValidExternalURL(form.ExternalWikiURL) { - ctx.Flash.Error(ctx.Tr("repo.settings.external_wiki_url_error")) - ctx.Redirect(repo.Link() + "/settings") - return - } - - units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypeExternalWiki, - Config: &repo_model.ExternalWikiConfig{ - ExternalWikiURL: form.ExternalWikiURL, - }, - }) - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki) - } else if form.EnableWiki && !form.EnableExternalWiki && !unit_model.TypeWiki.UnitGlobalDisabled() { - var wikiPermissions repo_model.UnitAccessMode - if form.GloballyWriteableWiki { - wikiPermissions = repo_model.UnitAccessModeWrite - } else { - wikiPermissions = repo_model.UnitAccessModeRead - } - units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypeWiki, - Config: new(repo_model.UnitConfig), - DefaultPermissions: wikiPermissions, - }) - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki) - } else { - if !unit_model.TypeExternalWiki.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalWiki) - } - if !unit_model.TypeWiki.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeWiki) - } - } - - if form.EnableIssues && form.EnableExternalTracker && !unit_model.TypeExternalTracker.UnitGlobalDisabled() { - if !validation.IsValidExternalURL(form.ExternalTrackerURL) { - ctx.Flash.Error(ctx.Tr("repo.settings.external_tracker_url_error")) - ctx.Redirect(repo.Link() + "/settings") - return - } - if len(form.TrackerURLFormat) != 0 && !validation.IsValidExternalTrackerURLFormat(form.TrackerURLFormat) { - ctx.Flash.Error(ctx.Tr("repo.settings.tracker_url_format_error")) - ctx.Redirect(repo.Link() + "/settings") - return - } - units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypeExternalTracker, - Config: &repo_model.ExternalTrackerConfig{ - ExternalTrackerURL: form.ExternalTrackerURL, - ExternalTrackerFormat: form.TrackerURLFormat, - ExternalTrackerStyle: form.TrackerIssueStyle, - ExternalTrackerRegexpPattern: form.ExternalTrackerRegexpPattern, - }, - }) - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues) - } else if form.EnableIssues && !form.EnableExternalTracker && !unit_model.TypeIssues.UnitGlobalDisabled() { - units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypeIssues, - Config: &repo_model.IssuesConfig{ - EnableTimetracker: form.EnableTimetracker, - AllowOnlyContributorsToTrackTime: form.AllowOnlyContributorsToTrackTime, - EnableDependencies: form.EnableIssueDependencies, - }, - }) - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) - } else { - if !unit_model.TypeExternalTracker.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeExternalTracker) - } - if !unit_model.TypeIssues.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeIssues) - } - } - - if form.EnableProjects && !unit_model.TypeProjects.UnitGlobalDisabled() { - units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypeProjects, - }) - } else if !unit_model.TypeProjects.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeProjects) - } - - if form.EnableReleases && !unit_model.TypeReleases.UnitGlobalDisabled() { - units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypeReleases, - }) - } else if !unit_model.TypeReleases.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeReleases) - } - - if form.EnablePackages && !unit_model.TypePackages.UnitGlobalDisabled() { - units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypePackages, - }) - } else if !unit_model.TypePackages.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePackages) - } - - if form.EnableActions && !unit_model.TypeActions.UnitGlobalDisabled() { - units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypeActions, - }) - } else if !unit_model.TypeActions.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypeActions) - } - - if form.EnablePulls && !unit_model.TypePullRequests.UnitGlobalDisabled() { - units = append(units, repo_model.RepoUnit{ - RepoID: repo.ID, - Type: unit_model.TypePullRequests, - Config: &repo_model.PullRequestsConfig{ - IgnoreWhitespaceConflicts: form.PullsIgnoreWhitespace, - AllowMerge: form.PullsAllowMerge, - AllowRebase: form.PullsAllowRebase, - AllowRebaseMerge: form.PullsAllowRebaseMerge, - AllowSquash: form.PullsAllowSquash, - AllowManualMerge: form.PullsAllowManualMerge, - AutodetectManualMerge: form.EnableAutodetectManualMerge, - AllowRebaseUpdate: form.PullsAllowRebaseUpdate, - DefaultDeleteBranchAfterMerge: form.DefaultDeleteBranchAfterMerge, - DefaultMergeStyle: repo_model.MergeStyle(form.PullsDefaultMergeStyle), - DefaultAllowMaintainerEdit: form.DefaultAllowMaintainerEdit, - }, - }) - } else if !unit_model.TypePullRequests.UnitGlobalDisabled() { - deleteUnitTypes = append(deleteUnitTypes, unit_model.TypePullRequests) - } - - if len(units) == 0 { - ctx.Flash.Error(ctx.Tr("repo.settings.update_settings_no_unit")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings") - return - } - - if err := repo_model.UpdateRepositoryUnits(ctx, repo, units, deleteUnitTypes); err != nil { - ctx.ServerError("UpdateRepositoryUnits", err) - return - } - if repoChanged { - if err := repo_service.UpdateRepository(ctx, repo, false); err != nil { - ctx.ServerError("UpdateRepository", err) - return - } - } - log.Trace("Repository advanced settings updated: %s/%s", ctx.Repo.Owner.Name, repo.Name) - - ctx.Flash.Success(ctx.Tr("repo.settings.update_settings_success")) - ctx.Redirect(ctx.Repo.RepoLink + "/settings") - case "signing": changed := false trustModel := repo_model.ToTrustModel(form.TrustModel) diff --git a/routers/web/web.go b/routers/web/web.go index 1744ddb83a..688fe48160 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1037,6 +1037,8 @@ func registerRoutes(m *web.Route) { m.Combo("").Get(repo_setting.Settings). Post(web.Bind(forms.RepoSettingForm{}), repo_setting.SettingsPost) }, repo_setting.SettingsCtxData) + m.Combo("/units").Get(repo_setting.Units). + Post(web.Bind(forms.RepoUnitSettingForm{}), repo_setting.UnitsPost) m.Post("/avatar", web.Bind(forms.AvatarForm{}), repo_setting.SettingsAvatar) m.Post("/avatar/delete", repo_setting.SettingsDeleteAvatar) diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 7cc07532ef..9527916ae0 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -130,6 +130,24 @@ type RepoSettingForm struct { EnablePrune bool // Advanced settings + IsArchived bool + + // Signing Settings + TrustModel string + + // Admin settings + EnableHealthCheck bool + RequestReindexType string +} + +// Validate validates the fields +func (f *RepoSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { + ctx := context.GetValidateContext(req) + return middleware.Validate(errs, ctx.Data, f, ctx.Locale) +} + +// RepoUnitSettingForm form for changing repository unit settings +type RepoUnitSettingForm struct { EnableCode bool EnableWiki bool GloballyWriteableWiki bool @@ -161,18 +179,10 @@ type RepoSettingForm struct { EnableTimetracker bool AllowOnlyContributorsToTrackTime bool EnableIssueDependencies bool - IsArchived bool - - // Signing Settings - TrustModel string - - // Admin settings - EnableHealthCheck bool - RequestReindexType string } // Validate validates the fields -func (f *RepoSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { +func (f *RepoUnitSettingForm) Validate(req *http.Request, errs binding.Errors) binding.Errors { ctx := context.GetValidateContext(req) return middleware.Validate(errs, ctx.Data, f, ctx.Locale) } diff --git a/templates/repo/settings/navbar.tmpl b/templates/repo/settings/navbar.tmpl index 3bef0fa4c1..62f81a901e 100644 --- a/templates/repo/settings/navbar.tmpl +++ b/templates/repo/settings/navbar.tmpl @@ -4,6 +4,23 @@ {{ctx.Locale.Tr "repo.settings.options"}} +
    + {{ctx.Locale.Tr "repo.settings.units.units"}} + +
    {{ctx.Locale.Tr "repo.settings.collaboration"}} diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 191ac53967..6cfef31060 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -299,325 +299,6 @@
    {{end}} -

    - {{ctx.Locale.Tr "repo.settings.advanced_settings"}} -

    -
    -
    - {{.CsrfTokenHtml}} - - - {{$isCodeEnabled := .Repository.UnitEnabled $.Context $.UnitTypeCode}} - {{$isCodeGlobalDisabled := .UnitTypeCode.UnitGlobalDisabled}} -
    - -
    - - -
    -
    - - {{$isWikiEnabled := or (.Repository.UnitEnabled $.Context $.UnitTypeWiki) (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}} - {{$isWikiGlobalDisabled := .UnitTypeWiki.UnitGlobalDisabled}} - {{$isExternalWikiGlobalDisabled := .UnitTypeExternalWiki.UnitGlobalDisabled}} - {{$isBothWikiGlobalDisabled := and $isWikiGlobalDisabled $isExternalWikiGlobalDisabled}} -
    - -
    - - -
    -
    -
    -
    -
    - - -
    -
    - {{if (not .Repository.IsPrivate)}} -
    -
    -
    - - -
    -
    -
    - {{end}} -
    -
    - - -
    -
    -
    - - -

    {{ctx.Locale.Tr "repo.settings.external_wiki_url_desc"}}

    -
    -
    - -
    - - {{$isIssuesEnabled := or (.Repository.UnitEnabled $.Context $.UnitTypeIssues) (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}} - {{$isIssuesGlobalDisabled := .UnitTypeIssues.UnitGlobalDisabled}} - {{$isExternalTrackerGlobalDisabled := .UnitTypeExternalTracker.UnitGlobalDisabled}} - {{$isIssuesAndExternalGlobalDisabled := and $isIssuesGlobalDisabled $isExternalTrackerGlobalDisabled}} -
    - -
    - - -
    -
    -
    -
    -
    - - -
    -
    -
    - {{if .Repository.CanEnableTimetracker}} -
    -
    - - -
    -
    -
    -
    - - -
    -
    - {{end}} -
    -
    - - -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -

    {{ctx.Locale.Tr "repo.settings.external_tracker_url_desc"}}

    -
    -
    - - -

    {{ctx.Locale.Tr "repo.settings.tracker_url_format_desc" | Str2html}}

    -
    -
    - -
    -
    - {{$externalTracker := (.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker)}} - {{$externalTrackerStyle := $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle}} - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -

    {{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp_pattern_desc" | Str2html}}

    -
    -
    -
    - -
    - - {{$isProjectsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeProjects}} - {{$isProjectsGlobalDisabled := .UnitTypeProjects.UnitGlobalDisabled}} -
    - -
    - - -
    -
    - - {{$isReleasesEnabled := .Repository.UnitEnabled $.Context $.UnitTypeReleases}} - {{$isReleasesGlobalDisabled := .UnitTypeReleases.UnitGlobalDisabled}} -
    - -
    - - -
    -
    - - {{$isPackagesEnabled := .Repository.UnitEnabled $.Context $.UnitTypePackages}} - {{$isPackagesGlobalDisabled := .UnitTypePackages.UnitGlobalDisabled}} -
    - -
    - - -
    -
    - - {{if .EnableActions}} - {{$isActionsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeActions}} - {{$isActionsGlobalDisabled := .UnitTypeActions.UnitGlobalDisabled}} -
    - -
    - - -
    -
    - {{end}} - - {{if not .IsMirror}} -
    - {{$pullRequestEnabled := .Repository.UnitEnabled $.Context $.UnitTypePullRequests}} - {{$pullRequestGlobalDisabled := .UnitTypePullRequests.UnitGlobalDisabled}} - {{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}} -
    - -
    - - -
    -
    -
    -
    -

    - {{ctx.Locale.Tr "repo.settings.merge_style_desc"}} -

    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    - -
    -

    - {{ctx.Locale.Tr "repo.settings.default_merge_style_desc"}} -

    - -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    -
    - - -
    -
    -
    - {{end}} - -
    -
    - -
    -
    -
    -

    {{ctx.Locale.Tr "repo.settings.signing_settings"}}

    diff --git a/templates/repo/settings/units.tmpl b/templates/repo/settings/units.tmpl new file mode 100644 index 0000000000..66ed035964 --- /dev/null +++ b/templates/repo/settings/units.tmpl @@ -0,0 +1,13 @@ +{{template "repo/settings/layout_head" (dict "ctxData" . "pageClass" "repository settings options")}} +
    +
    + {{.CsrfTokenHtml}} + {{template "repo/settings/units/overview" .}} + {{template "repo/settings/units/issues" .}} + {{if not .IsMirror}} + {{template "repo/settings/units/pulls" .}} + {{end}} + {{template "repo/settings/units/wiki" .}} +
    +
    +{{template "repo/settings/layout_footer" .}} diff --git a/templates/repo/settings/units/issues.tmpl b/templates/repo/settings/units/issues.tmpl new file mode 100644 index 0000000000..a09b8edb2e --- /dev/null +++ b/templates/repo/settings/units/issues.tmpl @@ -0,0 +1,102 @@ +

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

    +
    + {{$isIssuesEnabled := or (.Repository.UnitEnabled $.Context $.UnitTypeIssues) (.Repository.UnitEnabled $.Context $.UnitTypeExternalTracker)}} + {{$isIssuesGlobalDisabled := .UnitTypeIssues.UnitGlobalDisabled}} + {{$isExternalTrackerGlobalDisabled := .UnitTypeExternalTracker.UnitGlobalDisabled}} + {{$isIssuesAndExternalGlobalDisabled := and $isIssuesGlobalDisabled $isExternalTrackerGlobalDisabled}} +
    + +
    + + +
    +
    +
    +
    +
    + + +
    +
    +
    + {{if .Repository.CanEnableTimetracker}} +
    +
    + + +
    +
    +
    +
    + + +
    +
    + {{end}} +
    +
    + + +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +

    {{ctx.Locale.Tr "repo.settings.external_tracker_url_desc"}}

    +
    +
    + + +

    {{ctx.Locale.Tr "repo.settings.tracker_url_format_desc" | Str2html}}

    +
    +
    + +
    +
    + {{$externalTracker := (.Repository.MustGetUnit $.Context $.UnitTypeExternalTracker)}} + {{$externalTrackerStyle := $externalTracker.ExternalTrackerConfig.ExternalTrackerStyle}} + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +

    {{ctx.Locale.Tr "repo.settings.tracker_issue_style.regexp_pattern_desc" | Str2html}}

    +
    +
    +
    + +
    + +
    + +
    +
    diff --git a/templates/repo/settings/units/overview.tmpl b/templates/repo/settings/units/overview.tmpl new file mode 100644 index 0000000000..816b45ce1d --- /dev/null +++ b/templates/repo/settings/units/overview.tmpl @@ -0,0 +1,62 @@ +

    + {{ctx.Locale.Tr "repo.settings.units.overview"}} +

    +
    + {{$isCodeEnabled := .Repository.UnitEnabled $.Context $.UnitTypeCode}} + {{$isCodeGlobalDisabled := .UnitTypeCode.UnitGlobalDisabled}} +
    + +
    + + +
    +
    + + {{$isProjectsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeProjects}} + {{$isProjectsGlobalDisabled := .UnitTypeProjects.UnitGlobalDisabled}} +
    + +
    + + +
    +
    + + {{$isReleasesEnabled := .Repository.UnitEnabled $.Context $.UnitTypeReleases}} + {{$isReleasesGlobalDisabled := .UnitTypeReleases.UnitGlobalDisabled}} +
    + +
    + + +
    +
    + + {{$isPackagesEnabled := .Repository.UnitEnabled $.Context $.UnitTypePackages}} + {{$isPackagesGlobalDisabled := .UnitTypePackages.UnitGlobalDisabled}} +
    + +
    + + +
    +
    + + {{if .EnableActions}} + {{$isActionsEnabled := .Repository.UnitEnabled $.Context $.UnitTypeActions}} + {{$isActionsGlobalDisabled := .UnitTypeActions.UnitGlobalDisabled}} +
    + +
    + + +
    +
    + {{end}} + +
    + +
    + +
    +
    diff --git a/templates/repo/settings/units/pulls.tmpl b/templates/repo/settings/units/pulls.tmpl new file mode 100644 index 0000000000..e735fe974c --- /dev/null +++ b/templates/repo/settings/units/pulls.tmpl @@ -0,0 +1,121 @@ +

    + {{ctx.Locale.Tr "repo.pulls"}} +

    +
    + {{$pullRequestEnabled := .Repository.UnitEnabled $.Context $.UnitTypePullRequests}} + {{$pullRequestGlobalDisabled := .UnitTypePullRequests.UnitGlobalDisabled}} + {{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}} +
    + +
    + + +
    +
    +
    +
    +

    + {{ctx.Locale.Tr "repo.settings.merge_style_desc"}} +

    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    + +
    +

    + {{ctx.Locale.Tr "repo.settings.default_merge_style_desc"}} +

    + +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    +
    + + +
    +
    +
    + +
    +
    + +
    +
    diff --git a/templates/repo/settings/units/wiki.tmpl b/templates/repo/settings/units/wiki.tmpl new file mode 100644 index 0000000000..c3be39f1cc --- /dev/null +++ b/templates/repo/settings/units/wiki.tmpl @@ -0,0 +1,51 @@ +

    + {{ctx.Locale.Tr "repo.wiki"}} +

    +
    + {{$isWikiEnabled := or (.Repository.UnitEnabled $.Context $.UnitTypeWiki) (.Repository.UnitEnabled $.Context $.UnitTypeExternalWiki)}} + {{$isWikiGlobalDisabled := .UnitTypeWiki.UnitGlobalDisabled}} + {{$isExternalWikiGlobalDisabled := .UnitTypeExternalWiki.UnitGlobalDisabled}} + {{$isBothWikiGlobalDisabled := and $isWikiGlobalDisabled $isExternalWikiGlobalDisabled}} +
    + +
    + + +
    +
    +
    +
    +
    + + +
    +
    + {{if (not .Repository.IsPrivate)}} +
    +
    +
    + + +
    +
    +
    + {{end}} +
    +
    + + +
    +
    +
    + + +

    {{ctx.Locale.Tr "repo.settings.external_wiki_url_desc"}}

    +
    +
    + +
    + +
    + +
    +
    From e07b0e75ff013dbab2ab85a69273661b60a816e6 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 23 Jan 2024 10:57:49 +0100 Subject: [PATCH 030/271] Add a direct link from repo header to unit settings If a repository administrator is viewing a repository, and there are units that can be enabled, display an "Add more..." link that leads to the repository unit settings page. The goal here is to allow instances to configure a small set of repo units to be enabled by default, but also highlight for repo admins that they can add more. Signed-off-by: Gergely Nagy --- models/unit/unit.go | 43 +++++++++++++++++++++++++++++++++ modules/context/repo.go | 26 ++++++++++++++++++++ options/locale/locale_en-US.ini | 1 + templates/repo/header.tmpl | 5 ++++ 4 files changed, 75 insertions(+) diff --git a/models/unit/unit.go b/models/unit/unit.go index b216712d37..e37adf995e 100644 --- a/models/unit/unit.go +++ b/models/unit/unit.go @@ -108,6 +108,10 @@ var ( // DisabledRepoUnits contains the units that have been globally disabled DisabledRepoUnits = []Type{} + + // AllowedRepoUnitGroups contains the units that have been globally enabled, + // with mutually exclusive units grouped together. + AllowedRepoUnitGroups = [][]Type{} ) // Get valid set of default repository units from settings @@ -162,6 +166,45 @@ func LoadUnitConfig() error { if len(DefaultForkRepoUnits) == 0 { return errors.New("no default fork repository units found") } + + // Collect the allowed repo unit groups. Mutually exclusive units are + // grouped together. + AllowedRepoUnitGroups = [][]Type{} + for _, unit := range []Type{ + TypeCode, + TypePullRequests, + TypeProjects, + TypePackages, + TypeActions, + } { + // If unit is globally disabled, ignore it. + if unit.UnitGlobalDisabled() { + continue + } + + // If it is allowed, add it to the group list. + AllowedRepoUnitGroups = append(AllowedRepoUnitGroups, []Type{unit}) + } + + addMutuallyExclusiveGroup := func(unit1, unit2 Type) { + var list []Type + + if !unit1.UnitGlobalDisabled() { + list = append(list, unit1) + } + + if !unit2.UnitGlobalDisabled() { + list = append(list, unit2) + } + + if len(list) > 0 { + AllowedRepoUnitGroups = append(AllowedRepoUnitGroups, list) + } + } + + addMutuallyExclusiveGroup(TypeIssues, TypeExternalTracker) + addMutuallyExclusiveGroup(TypeWiki, TypeExternalWiki) + return nil } diff --git a/modules/context/repo.go b/modules/context/repo.go index b48f6ded26..727c18cad6 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -81,6 +81,31 @@ func (r *Repository) CanCreateBranch() bool { return r.Permission.CanWrite(unit_model.TypeCode) && r.Repository.CanCreateBranch() } +// AllUnitsEnabled returns true if all units are enabled for the repo. +func (r *Repository) AllUnitsEnabled(ctx context.Context) bool { + hasAnyUnitEnabled := func(unitGroup []unit_model.Type) bool { + // Loop over the group of units + for _, unit := range unitGroup { + // If *any* of them is enabled, return true. + if r.Repository.UnitEnabled(ctx, unit) { + return true + } + } + + // If none are enabled, return false. + return false + } + + for _, unitGroup := range unit_model.AllowedRepoUnitGroups { + // If any disabled unit is found, return false immediately. + if !hasAnyUnitEnabled(unitGroup) { + return false + } + } + + return true +} + // RepoMustNotBeArchived checks if a repo is archived func RepoMustNotBeArchived() func(ctx *Context) { return func(ctx *Context) { @@ -1053,6 +1078,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() + ctx.Data["AllUnitsEnabled"] = ctx.Repo.AllUnitsEnabled(ctx) ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() if err != nil { diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index be2a06b38f..9c8b3fc541 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2068,6 +2068,7 @@ settings.mirror_settings.push_mirror.edit_sync_time = Edit mirror sync interval settings.units.units = Repository Units settings.units.overview = Overview +settings.units.add_more = Add more... settings.sync_mirror = Synchronize Now settings.pull_mirror_sync_in_progress = Pulling changes from the remote %s at the moment. diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index ef3e40eea8..681cba9ef4 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -219,6 +219,11 @@ {{end}} {{if .Permission.IsAdmin}} + {{if not .AllUnitsEnabled}} + + {{svg "octicon-diff-added"}} {{ctx.Locale.Tr "repo.settings.units.add_more"}} + + {{end}} {{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}} From 44424bfe60d1038101b7797bd85ab963fe982a84 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Sun, 11 Feb 2024 23:11:57 +0100 Subject: [PATCH 031/271] Add tests for the previous two commits Signed-off-by: Gergely Nagy --- tests/integration/repo_settings_test.go | 130 ++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 tests/integration/repo_settings_test.go diff --git a/tests/integration/repo_settings_test.go b/tests/integration/repo_settings_test.go new file mode 100644 index 0000000000..16e0bb3d7d --- /dev/null +++ b/tests/integration/repo_settings_test.go @@ -0,0 +1,130 @@ +// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "fmt" + "net/http" + "testing" + + "code.gitea.io/gitea/models/db" + repo_model "code.gitea.io/gitea/models/repo" + unit_model "code.gitea.io/gitea/models/unit" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" + repo_service "code.gitea.io/gitea/services/repository" + "code.gitea.io/gitea/tests" + + "github.com/stretchr/testify/assert" +) + +func TestRepoSettingsUnits(t *testing.T) { + defer tests.PrepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{OwnerID: user.ID, Name: "repo1"}) + session := loginUser(t, user.Name) + + req := NewRequest(t, "GET", fmt.Sprintf("%s/settings/units", repo.Link())) + session.MakeRequest(t, req, http.StatusOK) +} + +func TestRepoAddMoreUnits(t *testing.T) { + defer tests.PrepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{Name: "user2"}) + session := loginUser(t, user.Name) + + // Make sure there are no disabled repos in the settings! + setting.Repository.DisabledRepoUnits = []string{} + unit_model.LoadUnitConfig() + + // Create a known-good repo, with all units enabled. + repo, _, f := CreateDeclarativeRepo(t, user, "", []unit_model.Type{ + unit_model.TypeCode, + unit_model.TypePullRequests, + unit_model.TypeProjects, + unit_model.TypePackages, + unit_model.TypeActions, + unit_model.TypeIssues, + unit_model.TypeWiki, + }, nil, nil) + defer f() + + assertAddMore := func(t *testing.T, present bool) { + t.Helper() + + req := NewRequest(t, "GET", repo.Link()) + resp := session.MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + htmlDoc.AssertElement(t, fmt.Sprintf("a[href='%s/settings/units']", repo.Link()), present) + } + + t.Run("no add more with all units enabled", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + assertAddMore(t, false) + }) + + t.Run("add more if units can be enabled", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer func() { + repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{ + RepoID: repo.ID, + Type: unit_model.TypePackages, + }}, nil) + }() + + // Disable the Packages unit + err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, nil, []unit_model.Type{unit_model.TypePackages}) + assert.NoError(t, err) + + assertAddMore(t, true) + }) + + t.Run("no add more if unit is globally disabled", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer func() { + repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{ + RepoID: repo.ID, + Type: unit_model.TypePackages, + }}, nil) + setting.Repository.DisabledRepoUnits = []string{} + unit_model.LoadUnitConfig() + }() + + // Disable the Packages unit globally + setting.Repository.DisabledRepoUnits = []string{"repo.packages"} + unit_model.LoadUnitConfig() + + // Disable the Packages unit + err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, nil, []unit_model.Type{unit_model.TypePackages}) + assert.NoError(t, err) + + // The "Add more" link appears no more + assertAddMore(t, false) + }) + + t.Run("issues & ext tracker globally disabled", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + defer func() { + repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, []repo_model.RepoUnit{{ + RepoID: repo.ID, + Type: unit_model.TypeIssues, + }}, nil) + setting.Repository.DisabledRepoUnits = []string{} + unit_model.LoadUnitConfig() + }() + + // Disable both Issues and ExternalTracker units globally + setting.Repository.DisabledRepoUnits = []string{"repo.issues", "repo.ext_issues"} + unit_model.LoadUnitConfig() + + // Disable the Issues unit + err := repo_service.UpdateRepositoryUnits(db.DefaultContext, repo, nil, []unit_model.Type{unit_model.TypeIssues}) + assert.NoError(t, err) + + // The "Add more" link appears no more + assertAddMore(t, false) + }) +} From 807572182392cca3d21aae5d61ac00df3efd3b6f Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 13 Feb 2024 22:51:01 +0100 Subject: [PATCH 032/271] Minor cleanup in ViewLatestWorkflowRun Use `ctx.ServerError` instead of a separate `log.Error` + `ctx.Error`. `ctx.ServerError` does essentially the same thing, but better. Signed-off-by: Gergely Nagy --- routers/web/repo/actions/view.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index 757ff07c0d..94fdd79903 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -22,7 +22,6 @@ import ( "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/base" context_module "code.gitea.io/gitea/modules/context" - "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/storage" "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" @@ -75,8 +74,7 @@ func ViewLatestWorkflowRun(ctx *context_module.Context) { if errors.Is(err, util.ErrNotExist) { ctx.NotFound("GetLatestRunForBranchAndWorkflow", err) } else { - log.Error("GetLatestRunForBranchAndWorkflow: %v", err) - ctx.Error(http.StatusInternalServerError, "Unable to get latest run for workflow on branch") + ctx.ServerError("GetLatestRunForBranchAndWorkflow", err) } return } From 6c3895638cebda12169e30393b5eaa5f8f48791c Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Wed, 14 Feb 2024 11:01:06 +0000 Subject: [PATCH 033/271] [I18N] Translations update from Weblate (#2322) Translations update from [Weblate](https://translate.codeberg.org) for [Forgejo/forgejo](https://translate.codeberg.org/projects/forgejo/forgejo/). Current translation status: ![Weblate translation status](https://translate.codeberg.org/widget/forgejo/forgejo/horizontal-auto.svg) Co-authored-by: mondstern Co-authored-by: earl-warren Co-authored-by: Kaede Fujisaki Co-authored-by: 0que <0que@users.noreply.translate.codeberg.org> Co-authored-by: noureddin Co-authored-by: yeziruo Co-authored-by: fnetX Co-authored-by: nykula Co-authored-by: Werenter Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2322 Co-authored-by: Codeberg Translate Co-committed-by: Codeberg Translate --- options/locale/locale_ar.ini | 269 +++++++++++++++++++++++++++++--- options/locale/locale_de-DE.ini | 2 + options/locale/locale_fr-FR.ini | 36 ++--- options/locale/locale_ja-JP.ini | 6 +- options/locale/locale_ru-RU.ini | 23 ++- options/locale/locale_sl.ini | 222 +++++++++++++++++++++++++- options/locale/locale_uk-UA.ini | 19 ++- options/locale/locale_zh-CN.ini | 50 +++++- 8 files changed, 569 insertions(+), 58 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index 9f0dde5c94..a97c642842 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -211,26 +211,26 @@ 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". enable_update_checker = فعل فحص التحديثات -default_enable_timetracking_popup = فعل تتبع الوقت للمستودعات الجديدة افتراضياً. +default_enable_timetracking_popup = فعل تتبع الوقت للمستودعات الجديدة مبدئيا. run_user_not_match = مستخدم التشغيل غير مطابق لأسم المستخدم الحالي: %s -> %s invalid_db_setting = إعدادات قاعدة البيانات غير صالحة: %v invalid_db_table = جدول قاعدة البيانات "%s" غير صالح: %v -default_keep_email_private_popup = اخفي عناوين البريد الإلكتروني للحسابات الجديدة افتراضياً. +default_keep_email_private_popup = أخفِ عناوين البريد الإلكتروني للحسابات الجديدة مبدئيا. env_config_keys = إعدادات بيئية -default_allow_create_organization = اسمح بإنشاء المنظمات افتراضياً +default_allow_create_organization = اسمح بإنشاء المنظمات مبدئيا invalid_app_data_path = مسار بيانات التطبيق غير صالح: %v enable_update_checker_helper = يفحص لإيجاد اصدارات جديدة عن طريق الإتصال بسيرفرات فورجيو. invalid_repo_path = المسار الجزري للمستودع غير صالح: %v internal_token_failed = فشل توليد الرمز الداخلي: %v no_reply_address = نطاقات البريد الإلكتروني المخفية -default_keep_email_private = اخفي عناوين البريد الإلكتروني افتراضياً +default_keep_email_private = أخفِ عناوين البريد الإلكتروني مبدئيا admin_name = اسم مستخدم المدير -default_allow_create_organization_popup = اسمح بحسابات المستخدمين الجديدة بإنشاء المنظمات افتراضياً. +default_allow_create_organization_popup = اسمح بحسابات المستخدمين الجديدة بإنشاء المنظمات مبدئيا. password_algorithm = خوارزمية تجزئة كلمة المرور invalid_password_algorithm = خوارزمية تجزئة كلمة المرور غير صالحة password_algorithm_helper = اختر خوارزمية تجزئة كلمة المرور. الخوارزميات لديهم متطلبات وقوى مختلفة. خوارزمية argon2 هي آمنة ولكن تتطلب الكثير من الذاكرة ولذلك قد تكون غير ملائمة للأنظمة الصغيرة. @@ -449,7 +449,7 @@ gpg_key_verified_long = تم التحقق من المفتاح بواسطة رم change_username_redirect_prompt = اسم المستخدم القديم سوف يعاد توجيهه حتى يطالب به شخص آخر. add_key_success = تم إضافة مفتاح SSH "%s". key_name = اسم المفتاح -comment_type_group_time_tracking = تعقب الوقت +comment_type_group_time_tracking = تتبع الوقت gpg_invalid_token_signature = مفتاح GPG المزود، والتوقيع والرمز لا يتطابقوا أو الرمز قديم. ssh_key_verified = مفتاح مُتحقق منه ssh_key_deletion_success = تم إزالة مفتاح SSH. @@ -710,9 +710,9 @@ issues.role.first_time_contributor = مساهم لأول مرة issues.label_title = الاسم issues.filter_projects = تصفية المشروعات issues.role.collaborator = مشترك -issues.action_open = حالية +issues.action_open = افتح issues.filter_sort.latest = الأحدث -issues.action_close = تامة +issues.action_close = أغلق issues.is_stale = تغيّر طلب السحب هذا بعد المراجعة issues.dependency.remove = أزل issues.label_templates.info = لا توجد تصنيفات بعد. أنشئ تصنيفا بزر «تصنيف جديد» أو استخدم باقة تصنيفات معرّفة سابقا: @@ -797,7 +797,7 @@ branch.rename_branch_to = غيّر اسم "%s" إلى: issues.reopen_comment_issue = علّق وأعد فتحها issues.dependency.add = أضف اعتمادية… issues.label_deletion_desc = حذف تصنيف يحذفه من كل المسائل، أتريد الاستمرار؟ -labels = تصنيفات +labels = التصنيفات delete_preexisting_content = احذف الملفات في %s milestones.deletion = احذف الهدف issues.comment_pull_merged_at = دمج الإيداع %[1]s إلى %[2]s %[3]s @@ -1003,7 +1003,7 @@ settings.web_hook_name_discord = دسكورد settings.web_hook_name_telegram = تيليجرام editor.commit_signed_changes = أودع التعديلات الموقّعة editor.filename_is_invalid = اسم الملف غير صالح: "%s". -pulls.no_merge_access = أنت غير مأذون بدمج طلب السحب. +pulls.no_merge_access = ليس مسموحا لك بدمج هذا الطلب. visibility_helper = اجعل المستودع خاصًا visibility_helper_forced = يفرض مدير موقعك أن تكون المستودعات الجديدة خاصة. wiki.page_content = محتوى الصفحة @@ -1107,6 +1107,179 @@ settings.collaborator_deletion = أزل مشتركا settings.event_wiki = الموسوعة settings.event_wiki_desc = إنشاء صفحة موسوعة أو تغيير اسمها أو تعديلها أو حذفها. settings.transfer_desc = انقل ملكية هذا المستودع إلى مستخدم أو إلى منظمة تديرها. +settings.transfer.rejected = رُفض نقل ملكية المستودع. +pulls.cannot_auto_merge_desc = لا يمكن دمج هذا الطلب آليا بسبب النزاعات. +pulls.auto_merge_newly_scheduled = هذا الطلب مجدول للدمج عند نجاح جميع الفحوص. +pulls.approve_count_n = "%d موافقة" +pulls.cannot_auto_merge_helper = ادمجه آليا لحل النزاعات. +pulls.num_conflicting_files_n = "%d ملفا متنازع عليها" +pulls.approve_count_1 = "%d موافقة" +pulls.reject_count_1 = "%d طلب تغيير" +pulls.reject_count_n = "%d طلب تغيير" +pulls.waiting_count_1 = "%d انتظار مراجعة" +pulls.waiting_count_n = "%d انتظار مراجعة" +pulls.no_merge_desc = لا يمكن دمج هذا الطلب لأن كل خيارات الدمج في هذا المستودع معطّلة. +pulls.no_merge_helper = فعّل خيارات الدمج في إعدادات المستودع أو ادمج الطلب يدويا. +pulls.no_merge_not_ready = هذا الطلب ليس جاهزا للدمج؛ انظر حالة المراجعة وفحوص الحالة. +pulls.merge_pull_request = أنشئ إيداع دمج +pulls.rebase_merge_pull_request = أعد التأسيس ثم قم بالتسريع +pulls.rebase_merge_commit_pull_request = أعد التأسيس ثم أنشئ إيداع دمج +pulls.merge_manually = دُمِج يدويا +pulls.merge_commit_id = معرّف إيداع الدمج +pulls.invalid_merge_option = لا يمكنك استخدام خيار الدمج هذا مع هذا الطلب. +pulls.merge_conflict_summary = رسالة خطأ +pulls.rebase_conflict_summary = رسالة خطأ +pulls.push_rejected_summary = رسالة الرفض الكاملة +pulls.status_checking = في انتظار بعض الفحوص +pulls.status_checks_failure = بعض الفحوص فشلت +pulls.status_checks_success = جميع الفحوص ناجحة +pulls.status_checks_warning = بعض الفحوص تعطي تحذيرات +pulls.commit_ref_at = `أشار إلى طلب الدمج من إيداع %[2]s` +pulls.cmd_instruction_hint = `أظهر شرح استخدام سطر الأوامر.` +pulls.cmd_instruction_checkout_title = اسحب +pulls.cmd_instruction_checkout_desc = من مستودع مشروعك، اسحب (check out) فرعا جديدا واختبر التغييرات. +pulls.cmd_instruction_merge_title = ادمج +pulls.cmd_instruction_merge_desc = ادمج التغييرات وحدّث المستودع في فورجيو. +pulls.clear_merge_message = نظّف رسالة الدمج +pulls.clear_merge_message_hint = تنظيف رسالة الدمج إنما يزيل محتوى رسالة الإيداع ويُبقى التذييلات التي يضيفها جت، مثل "Co-Authored-By …". +pulls.auto_merge_when_succeed = ادمج آليا عند نجاح جميع الفحوص +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.delete.title = حذف هذا الطلب؟ +pulls.delete.text = هل تريد حقا حذف هذا الطلب؟ (هذا سيحذف كل محتواه إلى الأبد. فكّر في إغلاقه بدلا من ذلك، إذا أردت الاحتفاظ به مؤرشفا) +pulls.recently_pushed_new_branches = لقد دفعت إلى الفرع %[1]s %[2]s +milestones.new = هدف جديد +milestones.closed = أُغلق %s +milestones.update_ago = حُدِّث %s +milestones.open = حالي +milestones.close = تام +milestones.new_subheader = تساعدك الأهداف في تنظيم المسائل وتتبع سيرها. +milestones.completeness = %d%% مكتمل +milestones.create = أنشئ هدفا +search.match.tooltip = لا تأت إلا بالنتائج التي تطابق كلمة البحث تماما +search.results = نتائج البحث عن "%s" في %s +settings.collaboration = المشتركون +settings.collaboration.admin = مدير +settings.collaboration.write = تحرير +settings.collaboration.read = اطلاع +settings.basic_settings = إعدادات أساسية +settings.sync_mirror = زامن الآن +settings.pull_mirror_sync_in_progress = يجذب التغييرات من المستودع البعيد %s الآن. +settings.push_mirror_sync_in_progress = يدفع التغييرات إلى المستودع البعيد %s الآن. +settings.site = موقع الويب +settings.update_settings = حدّث الإعدادات +settings.branches.update_default_branch = حدّث الفرع المبدئي +settings.branches.add_new_rule = أضف قاعدة جديدة +settings.advanced_settings = إعدادات متقدمة +settings.wiki_desc = فعّل موسوعة المستودع +settings.use_internal_wiki = استعمل الموسوعة المدمجة +settings.use_external_wiki = استعمل موسوعة خارجية +settings.external_wiki_url = رابط الموسوعة الخارجية +settings.external_wiki_url_error = رابط الموسوعة الخارجية ليس رابطا صالحا. +settings.external_wiki_url_desc = سيُوّجه الزائرين إلى الموسوعة الخارجية عندما يضغطون على زر الموسوعة. +settings.issues_desc = فعّل متتبع المسائل في المستودع +settings.use_internal_issue_tracker = استعمل متتبع المسائل المدمج +settings.use_external_issue_tracker = استعمل متتبع مسائل خارجي +settings.external_tracker_url = رابط متتبع المسائل الخارجي +settings.external_tracker_url_error = رابط متتبع المسائل الخارجي ليس رابطا صالحا. +settings.external_tracker_url_desc = سيُوّجه الزائرين إلى متتبع المسائل الخارجي عندما يضغطون على زر المسائل. +settings.tracker_url_format = صيغة رابط متتبع المسائل الخارجي +settings.tracker_url_format_error = صيغة رابط متتبع المسائل الخارجي ليست رابطا صالحا. +settings.tracker_issue_style = صيغة رقم متتبع المسائل الخارجي +settings.tracker_issue_style.numeric = أرقام +settings.tracker_issue_style.alphanumeric = أرقام وحروف رومية +settings.tracker_issue_style.regexp = تعبير نمطي +settings.tracker_issue_style.regexp_pattern = نمط التعبير +settings.enable_timetracker = فعّل تتبع الوقت +settings.allow_only_contributors_to_track_time = لا تجعل إلا المشتركين في المستودع يتتبعون الوقت +settings.pulls_desc = فعّل طلب الدمج في المستودع +settings.pulls.ignore_whitespace = تجاهل المسافات في النزاعات +settings.danger_zone = منطقة الخطر +settings.new_owner_has_same_repo = المالك الجديد لديه مستودع بالاسم نفسه؛ برجاء اختيار اسم آخر. +settings.transfer = نقل الملكية +settings.transfer_abort = ألغِ نقل الملكية +settings.transfer_abort_invalid = لا يمكنك إلغاء عملية غير موجودة لنقل ملكية مستودع. +settings.transfer.success = نجح نقل ملكية المستودع. +settings.transfer_abort_success = أُلغي بنجاح نقل ملكية المستودع إلى %s. +settings.transfer_owner = المالك الجديد +settings.transfer_perform = أتم نقل الملكية +settings.transfer_succeed = تم نقل ملكية المستودع. +pulls.auto_merge_newly_scheduled_comment = `أمر بجدولة هذا الطلب للدمج آليا عند نجاح جميع الفحوص %[1]s +pulls.auto_merge_canceled_schedule_comment = `ألغى الدمج الآلي لهذا الطلب عند نجاح جميع الفحوص %[1]s +ext_issues.desc = رابط متتبع المسائل الخارجي. +projects.edit_subheader = تساعد المشروعات في تنظيم المسائل وتتبع سيرها. +issues.tracker = متتبع الوقت +issues.start_tracking_short = شغّل المؤقت +issues.start_tracking = ابدأ تتبع الوقت +issues.start_tracking_history = `بدأ العمل %s` +issues.tracker_auto_close = سيتوقف المؤقت تلقائيا عندما تُغلق هذه المسألة +issues.stop_tracking = أوقف المؤقت +issues.stop_tracking_history = `توقف عن العمل %s` +pulls.can_auto_merge_desc = يمكن دمج هذا الطلب آليا. +pulls.num_conflicting_files_1 = "%d ملف متنازع عليه" +pulls.status_checks_error = بعض الفحوص تعطي أخطاء +pulls.status_checks_requested = مطلوب +pulls.status_checks_details = تفاصيل +pulls.status_checks_hide_all = أخفِ كل الفحوص +pulls.status_checks_show_all = أظهر كل الفحوص +pulls.close = أغلق طلب الدمج +pulls.closed_at = `أغلق طلب الدمج %[2]s` +pulls.reopened_at = `أعاد فتح طلب الدمج %[2]s` +milestones.title = العنوان +milestones.desc = الوصف +milestones.edit = عدّل الهدف +milestones.edit_subheader = تساعد الأهداف في تنظيم المسائل وتتبع سيرها. +settings.options = المستودع +settings.branches.switch_default_branch = بدّل الفرع المبدئي +settings.admin_settings = إعدادات المدير +settings.trust_model.default = نموذج الثقة المبدئي +settings.trust_model.collaborator = مشترك +settings.trust_model.collaborator.long = مشترك: ثق بتوقيعات المشترِكين +wiki.file_revision = مراجعة الصفحة +wiki.wiki_page_revisions = مراجعات صفحة الموسوعة +wiki.back_to_wiki = عد إلى صفحة الموسوعة +search.type.tooltip = نوع البحث +search.fuzzy = تقريبي +search.fuzzy.tooltip = ائت بالنتائج القريبة من كلمة البحث +search.match = مطابق +settings = الإعدادات +settings.mirror_settings.push_mirror.remote_url = رابط مستودع جت البعيد +settings.releases_desc = فعّل الإصدارات في المستودع +settings.projects_desc = فعّل المشروعات في المستودع +settings.signing_settings = إعدادات التحقق من التوقيعات +settings.trust_model = نموذج الثقة في التوقيعات +settings.trust_model.committer = مودِع +settings.trust_model.collaboratorcommitter = مشترك+مودع +settings.trust_model.collaboratorcommitter.long = مشترك+مودع: ثق بتوقيعات المشتركين التي تطابق المودع +tagged_this = وسم هذا +branches = الفروع +tags = الوسوم +issues = المسائل +pulls = طلبات الدمج +project_board = المشروعات +packages = الحزم +actions = الإجراءات +released_this = أصدر هذا +commit.load_referencing_branches_and_tags = حمّل الفروع والوسوم التي تشير إلى هذا الإيداع +issues.opened_by = أنشأها %[1]s %[3]s +issues.closed_by = من %[3]s أُغلقت %[1]s +issues.opened_by_fake = أنشأها %[1]s %[2]s +issues.closed_by_fake = من %[2]s أُغلقت %[1]s +issues.num_comments_1 = %d تعليق +issues.num_comments = %d تعليقا +issues.commented_at = `علّق %s` +issues.commit_ref_at = `أشار إلى هذه المسألة من إيداع %[2]s` +issues.ref_issue_from = `أشار إلى هذه المسألة %[4]s %[2]s` +issues.ref_pull_from = `أشار إلى هذا الطلب %[4]s %[2]s` +issues.ref_closing_from = `أشار إلى طلب دمج %[4]s سيغلق هذه المسألة %[2]s` +issues.ref_reopening_from = `أشار إلى طلب دمج %[4]s سيعيد فتح هذه المسألة %[2]s` +issues.ref_closed_from = `أغلق هذه المسألة %[4]s %[2]s` +issues.ref_reopened_from = `أعاد فتح هذه المسألة %[4]s %[2]s` +issues.reference_issue.body = المحتوى +issues.reference_link = للإشارة: %s +settings.actions_desc = فعّل الإجراءات في المستودع [mail] admin.new_user.text = من فضلك اضغط هنا لإدارة المستخدم من لوحة الإدارة. @@ -1145,23 +1318,23 @@ activate_email.title = %s، يرجى تأكيد عنوان بريدك الإلك release.note = ملاحظة: issue.action.close = @%[1]s أغلق #%[2]d. issue.action.merge = @%[1]s دمج #%[2]d مع %[3]s. -issue.action.force_push = %[1]s فرض الدفع بـ%[2]s من %[3]s إلى %[4]s. +issue.action.force_push = %[1]s فرض دفع %[2]s من %[3]s إلى %[4]s. repo.transfer.body = للقبول أو الرفض، زر %s أو تجاهل. team_invite.text_3 = ملاحظة: هذه الدعوة معنيه إلى %[1]s. إن لم تكن مترقب هذه الدعوة، يمكنك تجاهل هذه الرسالة. issue.action.reopen = @%[1]s أعاد فتح #%[2]d. issue.action.approve = @%[1]s وافق على هذا الطلب للسحب. -issue.action.push_1 = @%[1]s دفع بـ %[3]d إيداع إلى %[2]s -release.new.text = @%[1]s اصدر %[2]s في %[3]s +issue.action.push_1 = @%[1]s دفع %[3]d إيداع إلى %[2]s +release.new.text = @%[1]s أصدر %[2]s في %[3]s issue.action.reject = @%[1]s طلب تغييرات في هذا الطلب للسحب. release.download.zip = البرمجية (ZIP) release.download.targz = البرمجية (TAR.GZ) issue.action.review = @%[1]s علّق على هذا الطلب للسحب. issue.action.new = @%[1]s انشأ #%[2]d. issue_assigned.issue = @%[1]s عيّنك إلى مسألة %[2]s في مستودع %[3]s. -issue.action.push_n = @%[1]s دفع بـ %[3]d إيداعات إلى %[2]s -release.new.subject = صُدر %s في %s -repo.transfer.subject_to_you = %s يود نقل "%s" إليك -repo.transfer.subject_to = %s يود نقل "%s" إلى %s +issue.action.push_n = @%[1]s دفع %[3]d إيداعات إلى %[2]s +release.new.subject = أُصدر %s في %s +repo.transfer.subject_to_you = %s يود نقل ملكية "%s" إليك +repo.transfer.subject_to = %s يود نقل ملكية "%s" إلى %s issue.action.ready_for_review = @%[1]s علّم هذا الطلب للسحب كجاهز للمراجعة. issue_assigned.pull = @%[1]s عيّنك إلى طلب سحب %[2]s في مستودع %[3]s. issue.action.review_dismissed = @%[1]s أستبعد آخر مراجعة من %[2]s لهذا الطلب للسحب. @@ -1445,6 +1618,11 @@ repos.issues = المسائل users.created = أُنشئ auths.oauth2_authURL = رابط الأذن dashboard.resync_all_sshkeys = حدّث ملف '.ssh/authorized_keys' بمفاتيح SSH لفورجيو. +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 = اسمح للمشتركين في المستودع موحدهم بتتبع الوقت [form] username_error_no_dots = ` يُمكن أن يحتوي فقط على أرقام "0-9 "، أبجدية "A-Z" ،"a-z"، شرطة "-"، وخط أسفل "_" ولا يمكن أن تبدأ أو تنتهي بغير الأبجدية الرقمية، كما يحظر تتالي رموز غير أبجدية رقمية.` @@ -1589,29 +1767,50 @@ runners.status.active = نشيط runners.status = الحالة runners.description = الوصف runners.update_runner = حدّث التغييرات -runners.name = الأسم -runners.version = الأصدار +runners.name = الاسم +runners.version = النسخة runs.status = الحالة status.unknown = "مجهول" runners.owner_type = النوع status.waiting = "ينتظر" -runners.labels = التسميات +runners.labels = التصنيفات runners.status.unspecified = مجهول runs.commit = إيداع status.success = "نجح" -runs.no_workflows.documentation = +runs.no_workflows.documentation =لمعرفة المزيد عن إجراءات فورجيو، برجاء رؤية التوثيق. runs.empty_commit_message = (رسالة إيداع فارغة) status.cancelled = "ملغي" runs.status_no_select = كل الحالات runs.scheduled = مُجدوَل -variables.edit = تعديل المتغير -variables.update.success = تم تحرير المتغير. +variables.edit = عدّل المتغير +variables.update.success = عُدِّل المتغير. variables.update.failed = فشل تعديل المتغير. variables.deletion.failed = فشل حذف المتغير. variables.creation.failed = فشل إضافة المتغير. variables.creation.success = تم إضافة المتغير "%s". variables.deletion.success = تم حذف المتغير. -variables.id_not_exist = المتغير ذو المعرّف %d ليس موجود. +variables.id_not_exist = المتغير ذو المعرّف %d ليس موجودا. +actions = الإجراءات +unit.desc = أدر الإجراءات +status.skipped = متخطى +runners = المشغلون +runners.runner_manage_panel = إدارة المشغلين +runners.new = أنشئ مشغلا جديدا +runners.new_notice = كيف تبدأ مشغلا (بالإنجليزية) +runners.id = المعرّف +runners.last_online = آخر مرة كان متصلا +runners.none = لا مشغّل متاح +runners.status.offline = غير متصل +runs.pushed_by = دفعه +runs.no_matching_online_runner_helper = لا يوجد +runners.edit_runner = عدّل المشغّل +runners.update_runner_success = نجح تحديث المشغّل +runners.update_runner_failed = تعذر تحديث المشغّل +runners.delete_runner = احذف هذا المشغّل +runners.delete_runner_success = نجح حذف المشغّل +runners.delete_runner_failed = تعذر حذف المشغّل +runners.delete_runner_header = تأكيد حذف هذا المشغّل +variables.description = تمرر المتغيرات إلى إجراءات معينة ولا يمكن قراءتها بطريقة أخرى. [modal] no = لا @@ -1673,6 +1872,26 @@ compare_commits_general = قارن الإيداعات review_dismissed_reason = السبب: compare_branch = قارن compare_commits = قارن %d إيداع +create_repo = أنشأ المستودع %s +rename_repo = غيّر اسم المستودع %[1]s إلى %[3]s +commit_repo = دفع إلى %[3]s في %[4]s +create_issue = `أنشأ المسألة %[3]s#%[2]s` +close_issue = `أغلق المسألة %[3]s#%[2]s` +reopen_issue = `أعاد فتح المسألة %[3]s#%[2]s` +create_pull_request = `أنشأ طلب الدمج %[3]s#%[2]s` +close_pull_request = `أغلق طلب الدمج %[3]s#%[2]s` +reopen_pull_request = `أعاد فتح طلب الدمج %[3]s#%[2]s` +comment_issue = `علّق على المسألة %[3]s#%[2]s` +comment_pull = `علّق على طلب الدمج %[3]s#%[2]s` +merge_pull_request = `دمج الطلب %[3]s#%[2]s` +create_branch = أنشأ الفرع %[3]s في %[4]s +transfer_repo = نقل ملكية المستودع %s إلى %s +push_tag = دفع الوسم %[3]s إلى %[4]s +delete_tag = حذف الوسم %[2]s من %[3]s +delete_branch = حذف الفرع %[2]s من %[3]s +approve_pull_request = `قبِل %[3]s#%[2]s` +reject_pull_request = `اقترح تغييرات في %[3]s#%[2]s` +publish_release = `أصدر "%[4]s" في %[3]s` [units] unit = وحدة \ No newline at end of file diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 17ed1b2a5b..6045e2a8b1 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -625,6 +625,8 @@ form.name_pattern_not_allowed=Das Muster "%s" ist nicht in einem Benutzernamen e form.name_chars_not_allowed=Benutzername "%s" enthält ungültige Zeichen. block_user = Nutzer Blockieren block_user.detail = Bitte beachten Sie, dass andere Maßnahmen ergriffen werden, wenn Sie diesen Benutzer blockieren, wie: +block_user.detail_2 = Dieser Benutzer kann nicht mit ihrem Repository interagieren, Issues erstellen und kommentieren. +block_user.detail_1 = Dieser Benutzer folgt ihnen nicht mehr. [settings] profile=Profil diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index aed59e601c..e8287e711e 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -438,29 +438,29 @@ hi_user_x=Bonjour %s, activate_account=Veuillez activer votre compte activate_account.title=%s, veuillez activer votre compte -activate_account.text_1=Bonjour %[1]s, merci de votre inscription chez %[2]s! -activate_account.text_2=Veuillez cliquer sur ce lien pour activer votre compte chez %s: +activate_account.text_1=Bonjour %[1]s, merci de votre inscription chez %[2]s ! +activate_account.text_2=Veuillez cliquer sur ce lien pour activer votre compte chez %s : activate_email=Veuillez vérifier votre adresse courriel activate_email.title=%s, veuillez vérifier votre adresse courriel -activate_email.text=Veuillez cliquer sur le lien suivant pour vérifier votre adresse courriel dans %s: +activate_email.text=Veuillez cliquer sur le lien suivant pour vérifier votre adresse courriel dans %s : register_notify=Bienvenue sur Forgejo register_notify.title=%[1]s, bienvenue à %[2]s -register_notify.text_1=ceci est votre courriel de confirmation d'inscription pour %s! +register_notify.text_1=ceci est votre courriel de confirmation d'inscription pour %s ! register_notify.text_2=Vous pouvez maintenant vous connecter avec le nom d'utilisateur : %s. register_notify.text_3=Si ce compte a été créé pour vous, veuillez définir votre mot de passe d'abord. reset_password=Récupérer votre compte reset_password.title=%s, vous avez demandé à récupérer votre compte -reset_password.text=Veuillez cliquer sur le lien suivant pour récupérer votre compte dans %s: +reset_password.text=Veuillez cliquer sur le lien suivant pour récupérer votre compte dans %s : register_success=Inscription réussie issue_assigned.pull=@%[1]s vous a assigné à la demande d’ajout %[2]s dans le dépôt %[3]s. issue_assigned.issue=@%[1]s vous a assigné le ticket %[2]s dans le dépôt %[3]s. -issue.x_mentioned_you=@%s vous a mentionné: +issue.x_mentioned_you=@%s vous a mentionné : issue.action.force_push=%[1]s a forcé %[2]s de %[3]s vers %[4]s. issue.action.push_1=@%[1]s a soumis %[3]d révision sur %[2]s issue.action.push_n=@%[1]s a soumis %[3]d révisions sur %[2]s @@ -473,11 +473,11 @@ issue.action.review=@%[1]s a commenté sur cette demande d'ajout. issue.action.review_dismissed=@%[1]s a révoqué la dernière évaluation de %[2]s pour cette demande d'ajout. issue.action.ready_for_review=La demande d’ajout de @%[1]s est prête à être évaluée. issue.action.new=@%[1]s a créé #%[2]d. -issue.in_tree_path=Dans %s: +issue.in_tree_path=Dans %s : release.new.subject=%s publiée dans %s release.new.text=@%[1]s a publié [2]s dans %[3]s -release.title=Titre: %s +release.title=Titre : %s release.note=Remarque : release.downloads=Téléchargements : release.download.zip=Code source (ZIP) @@ -566,7 +566,7 @@ email_been_used=Cette adresse courriel est déjà utilisée. email_invalid=Cette adresse courriel est invalide. openid_been_used=Adresse OpenID "%s" déjà utilisée. username_password_incorrect=Identifiant ou mot de passe invalide. -password_complexity=Le mot de passe ne respecte pas les exigences de complexité: +password_complexity=Le mot de passe ne respecte pas les exigences de complexité : password_lowercase_one=Au moins un caractère minuscule password_uppercase_one=Au moins un caractère majuscule password_digit_one=Au moins un chiffre @@ -586,14 +586,14 @@ invalid_ssh_key=Impossible de vérifier votre clé SSH : %s invalid_gpg_key=Impossible de vérifier votre clé GPG : %s invalid_ssh_principal=Principal invalide : %s must_use_public_key=La clé que vous avez fournie est une clé privée. Veuillez ne pas divulguer votre clé privée. Utilisez votre clé publique à la place. -unable_verify_ssh_key=Impossible de valider la clé SSH, vérifiez à nouveau s’il y a des erreurs. +unable_verify_ssh_key="Impossible de valider la clé SSH, vérifiez à nouveau s’il y a des erreurs." auth_failed=Échec d'authentification : %v -still_own_repo=Votre compte possède toujours un ou plusieurs dépôts, vous devez d’abord les supprimer ou les transférer. -still_has_org=Votre compte est un membre d’une ou plusieurs organisations, veuillez d’abord les quitter. -still_own_packages=Votre compte possède toujours un ou plusieurs paquets, vous devez d’abord les supprimer ou les transférer. -org_still_own_repo=Cette organisation possède encore un ou plusieurs dépôts. Vous devez d’abord les supprimer ou les transférer. -org_still_own_packages=Cette organisation possède encore un ou plusieurs paquets. Vous devez d’abord les supprimer. +still_own_repo="Votre compte possède toujours un ou plusieurs dépôts, vous devez d’abord les supprimer ou les transférer." +still_has_org="Votre compte est un membre d’une ou plusieurs organisations, veuillez d’abord les quitter." +still_own_packages="Votre compte possède toujours un ou plusieurs paquets, vous devez d’abord les supprimer ou les transférer." +org_still_own_repo="Cette organisation possède encore un ou plusieurs dépôts. Vous devez d’abord les supprimer ou les transférer." +org_still_own_packages="Cette organisation possède encore un ou plusieurs paquets. Vous devez d’abord les supprimer." target_branch_not_exist=La branche cible n'existe pas. username_error_no_dots = ` peut uniquement contenir des caractères alphanumériques ('0-9','a-z','A-Z'), tiret ('-') et souligné ('_'). Ne peut commencer ou terminer avec un caractère non-alphanumérique, et l'utilisation de caractères non-alphanumériques consécutifs n'est pas permise.` @@ -673,7 +673,7 @@ cancel=Annuler 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_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.issue_ref_tooltip=Commentaires où l’utilisateur change la branche/étiquette associée au ticket comment_type_group_reference=Référence @@ -1763,7 +1763,7 @@ pulls.required_status_check_missing=Certains contrôles requis sont manquants. pulls.required_status_check_administrator=En tant qu'administrateur, vous pouvez toujours fusionner cette requête de pull. pulls.blocked_by_approvals=Cette demande d'ajout n’est pas suffisamment approuvée. %d approbations obtenues sur %d. pulls.blocked_by_rejection=Cette demande d’ajout nécessite des corrections sollicitées par un évaluateur officiel. -pulls.blocked_by_official_review_requests=Cette demande d’ajout a des sollicitations officielles d’évaluation. +pulls.blocked_by_official_review_requests="Cette demande d’ajout est bloquée car il manque une ou plusieurs approbations officielles." pulls.blocked_by_outdated_branch=Cette demande d’ajout est bloquée car elle est obsolète. pulls.blocked_by_changed_protected_files_1=Cette demande d'ajout est bloquée car elle modifie un fichier protégé : pulls.blocked_by_changed_protected_files_n=Cette demande d'ajout est bloquée car elle modifie des fichiers protégés : @@ -2595,7 +2595,7 @@ admin.flags_replaced = Drapeaux du dépôt remplacés rss.must_be_on_branch = Vous devez vous trouver sur une branche pour obtenir un flux RSS. admin.manage_flags = Gérer les drapeaux admin.enabled_flags = Drapeaux actifs pour le dépôt: -clone_in_vscodium = Clone dans VS Codium +clone_in_vscodium = Clone dans VSCodium object_format_helper = Format des objets d'un dépôt. Ne peut pas être changé. SHA1 est le plus compatible. mirror_sync = synchronisé object_format = Format de l'objet diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 2bb8f67c83..791daa240e 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -187,7 +187,7 @@ app_desc=自分で立てる、超簡単 Git サービス install=簡単インストール install_desc=シンプルに、プラットフォームに応じてバイナリを実行したり、Dockerで動かしたり、パッケージを使うだけ。 platform=クロスプラットフォーム -platform_desc=ForgejoはGoでコンパイルできる環境ならどこでも動きます: Windows、macOS、Linux、ARM等々、好きなものを選んでください! +platform_desc=ForgejoはGoでコンパイルできる環境ならどこでも動きます: Windows、macOS、Linux、ARM等々、好きなものを選んでください! lightweight=軽量 lightweight_desc=Forgejo の最小動作要件は小さくて、安価な Raspberry Pi でも動きます。エネルギー消費を節約しましょう! license=オープンソース @@ -925,6 +925,8 @@ visibility.limited=限定 visibility.limited_tooltip=認証されたユーザーのみに表示されます visibility.private=プライベート visibility.private_tooltip=あなたが参加した組織のメンバーのみに表示されます +blocked_users_none = あなたはまだ誰もユーザーをブロックしていません。 +blocked_users = ブロックしたユーザー [repo] new_repo_helper=リポジトリには、プロジェクトのすべてのファイルとリビジョン履歴が入ります。 すでにほかの場所でホストしていますか? リポジトリを移行 もどうぞ。 @@ -2537,7 +2539,7 @@ branch.download=ブランチ "%s" をダウンロード branch.rename=ブランチ名 "%s" を変更 branch.search=ブランチを検索 branch.included_desc=このブランチはデフォルトブランチに含まれています -branch.included=埋没 +branch.included=含有 branch.create_new_branch=このブランチをもとに作成します: branch.confirm_create_branch=ブランチを作成 branch.warning_rename_default_branch=デフォルトブランチの名前を変更しようとしています。 diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 81865fdff6..009167873d 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -996,7 +996,7 @@ auto_init=Инициализировать репозиторий (Добавл trust_model_helper=Выберите модель доверия для проверки подписи. Возможные варианты: trust_model_helper_collaborator=Соавтор: Доверять подписям соавторов trust_model_helper_committer=Автор коммита: доверять подписям, соответствующим авторам коммитов -trust_model_helper_collaborator_committer=Соавтор+Коммитер: Доверять подписям соавторов, которые соответствуют автору коммита +trust_model_helper_collaborator_committer=Соавтор+Коммитер: доверять подписям соавторов, которые соответствуют автору коммита trust_model_helper_default=По умолчанию: используйте модель доверия по умолчанию для этой установки create_repo=Создать репозиторий default_branch=Ветка по умолчанию @@ -1202,7 +1202,7 @@ commit_graph.color=Цвет commit.contained_in=Этот коммит содержится в: commit.contained_in_default_branch=Этот коммит является частью ветки по умолчанию commit.load_referencing_branches_and_tags=Загрузить ветки и теги, ссылающиеся на этот коммит -blame=Ответственный +blame=Авторство download_file=Скачать файл normal_view=Обычный вид line=строка @@ -2093,7 +2093,7 @@ settings.trust_model.committer=Коммитер settings.trust_model.committer.long=Коммитер: Доверять подписям, соответствующим коммитерам (Это совпадает с GitHub и заставит подписать коммиты Forgejo в качестве коммитера) settings.trust_model.committer.desc=Действительные подписи будут помечены «доверенными», только если они соответствуют автору коммита, в противном случае они будут помечены «не совпадающими». Это заставит Forgejo быть автором подписанных коммитов, а фактический автор будет обозначен в трейлерах Co-Authored-By: и Co-Committed-By: коммита. Ключ Forgejo по умолчанию должен соответствовать пользователю в базе данных. settings.trust_model.collaboratorcommitter=Соавтор+Коммитер -settings.trust_model.collaboratorcommitter.long=Соавтор+Коммитер: Доверять подписям соавторов, которые соответствуют автору коммита +settings.trust_model.collaboratorcommitter.long=Соавтор+Коммитер: доверять подписям соавторов, которые соответствуют автору коммита settings.trust_model.collaboratorcommitter.desc=Действительные подписи соавторов этого репозитория будут помечены «доверенными», если они соответствуют автору коммита. Действительные подписи будут помечены как «недоверенные», если подпись соответствует автору коммита, и «не совпадающие» впротивном случае. Это заставит Forgejo быть отмеченным в качестве автора подписанного коммита, а фактический автор будет указан в трейлерах Co-Authored-By: и Co-Committed-By: коммита. Ключ Forgejo по умолчанию должен соответствовать пользователю в базе данных. settings.wiki_delete=Стереть данные вики settings.wiki_delete_desc=Будьте внимательны! Как только вы удалите вики — пути назад не будет. @@ -2551,7 +2551,7 @@ mirror_address_protocol_invalid = Эта ссылка недействитель fork_no_valid_owners = Этот репозиторий не может быть форкнут, т.к. здесь нет действующих владельцев. new_repo_helper = Репозиторий содержит все файлы проекта и историю изменений. Уже где-то есть репозиторий? Выполните миграцию. mirror_address_url_invalid = Эта ссылка недействительна. Необходимо правильно указать все части адреса. -issues.comment.blocked_by_user = Вы не можете оставить комментарий под этой задачей, т.к. вы заблокированы владельцем репозитория или автором задачи. +issues.comment.blocked_by_user = Вы не можете комментировать под этой задачей, т.к. вы заблокированы владельцем репозитория или автором задачи. pulls.blocked_by_user = Невозможно создать запрос на слияние в этом репозитории, т.к. вы заблокированы его владельцем. settings.add_collaborator_blocked_our = Невозможно добавить соавтора, т.к. он заблокирован в этом репозитории. admin.enabled_flags = Включенные флаги репозитория: @@ -2561,7 +2561,7 @@ rss.must_be_on_branch = Перейдите на ветку, чтобы сдел admin.manage_flags = Управление флагами admin.update_flags = Обновить флаги object_format = Формат объекта -clone_in_vscodium = Клонировать в VS Codium +clone_in_vscodium = Клонировать в VSCodium mirror_sync = синхронизирован blame.ignore_revs = Правки в .git-blame-ignore-revs проигнорированы. Нажмите здесь, чтобы обойти этот файл и просмотреть авторов полноценно. issues.blocked_by_user = Невозможно создать задачу в этом репозитории, т.к. вы заблокированы его владельцем. @@ -2590,7 +2590,7 @@ issues.due_date_not_writer = "Для обновления даты выполн issues.review.outdated_description = С момента добавления этого комментария содержимое изменилось pulls.nothing_to_compare_have_tag = Выбранные ветки/теги идентичны. pulls.select_commit_hold_shift_for_range = Выберите коммит. Зажмите Shift, чтобы выбрать диапазон -pulls.blocked_by_official_review_requests = "У этого запроса на слияние есть запросы на официальную рецензию." +pulls.blocked_by_official_review_requests = "Этот запрос на слияние заблокирован, т.к. у него не хватает одобрений от одного или нескольких официальных рецензентов." pulls.recently_pushed_new_branches = Вы отправили коммиты в ветку %[1]s %[1]s milestones.new_subheader = Этапы полезны для систематизации задач и отслеживания их выполнения. wiki.cancel = Отмена @@ -2602,11 +2602,16 @@ settings.wiki_rename_branch_main_notices_2 = Внутренняя ветка в settings.wiki_branch_rename_failure = Не удалось нормализовать название ветки вики репозитория. settings.confirm_wiki_branch_rename = Переименовать ветку вики settings.wiki_rename_branch_main_notices_1 = Эта операция НЕОБРАТИМА. -settings.wiki_rename_branch_main_desc = Переименовать внутреннюю ветку, используемую вики, в "%s". Это изменение перманентно и необратимо. +settings.wiki_rename_branch_main_desc = Переименовать внутреннюю ветку, используемую вики, в "%s". Это изменение является перманентным и необратимым. settings.wiki_branch_rename_success = Название ветки вики репозитория успешно нормализовано. ambiguous_runes_description = `Этот файл содержит символы Юникода, которые легко спутать с похожими. Если так и должно быть, можете спокойно игнорировать это предупреждение. Отобразить символы можно кнопкой Экранирования.` editor.invalid_commit_mail = Неправильная почта для создания коммита. pulls.has_merged = Слияние не удалось: запрос уже был слит, изменение целевой ветки или повторное слияние невозможно. +settings.enter_repo_name = Для подтверждения введите название репозитория: +signing.wont_sign.error = Не удалось проверить возможность подписать коммит. +signing.wont_sign.nokey = Нет ключей для подписи этого коммита. +settings.wiki_globally_editable = Разрешить редактирование Вики всем пользователям +settings.webhook.test_delivery_desc_disabled = Активируйте этот веб-хук для проверки тестовым событием. [org] org_name_holder=Название организации @@ -3249,6 +3254,8 @@ notices.delete_success=Уведомления системы были удале self_check.no_problem_found = Пока проблем не обнаружено. auths.tip.gitea = Зарегистрируйте новое приложение OAuth2. Доступна инструкция: https://docs.gitea.com/development/oauth2-provider auths.tips.oauth2.general.tip = При регистрации нового приложения OAuth2 ссылка обратного перенаправления должна быть: +self_check.database_fix_mssql = В настоящий момент пользователи MSSQL могут исправить проблемы с сопоставлением только ручным прописыванием "ALTER ... COLLATE ..." в SQL. +self_check.database_fix_mysql = Пользователи MySQL и MariaDB могут исправить проблемы с сопоставлением командой "gitea doctor convert". Также можно вручную вписать "ALTER ... COLLATE ..." в SQL. [action] create_repo=создал(а) репозиторий %s @@ -3486,6 +3493,8 @@ owner.settings.chef.title=Реестр Chef owner.settings.chef.keypair=Создать пару ключей owner.settings.cleanuprules.none = Нет доступных правил очистки. Ознакомьтесь с документацией. owner.settings.cargo.rebuild.description = Пересборка может быть полезной в случае, если индекс не синхронизирован с сохранёнными пакетами Cargo. +rpm.repository = О репозитории +rpm.repository.architectures = Архитектуры [secrets] secrets=Секреты diff --git a/options/locale/locale_sl.ini b/options/locale/locale_sl.ini index 1967f4d9bd..ca95470379 100644 --- a/options/locale/locale_sl.ini +++ b/options/locale/locale_sl.ini @@ -99,12 +99,232 @@ twofa_scratch = Dvofaktorska koda Scratch home = Domov powered_by = Poganja ga %s retry = Ponovite +preview = Predogled +mirrors = Ogledala +loading = Nalaganje… +show_full_screen = Prikaži celoten zaslon +webauthn_error_insecure = WebAuthn podpira samo varne povezave. Za testiranje prek protokola HTTP lahko uporabite izvor "localhost" ali "127.0.0.1" +username = Usmerjevalno ime +tracked_time_summary = Povzetek spremljanega časa na podlagi filtrov seznama zadev +email = Elektronski naslov +captcha = CAPTCHA +sources = Viri +issues = Vprašanja +milestones = Mejniki +ok = OK +copy_branch = Kopiranje imena veje +artifacts = Artefakti [install] reinstall_confirm_check_3 = Potrjujete, da ste popolnoma prepričani, da se ta program Forgejo izvaja s pravilno lokacijo app.ini, in da ste prepričani, da ga morate znova namestiti. Potrjujete, da se zavedate zgoraj navedenih tveganj. +require_db_desc = Forgejo zahteva MySQL, PostgreSQL, MSSQL, 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 +disable_gravatar_popup = 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 konfiguracija +db_title = Nastavitve zbirke podatkov +db_type = Vrsta zbirke podatkov +host = Gostitelj +db_schema_helper = Pustite prazno za privzeto zbirko podatkov ("public"). +db_schema = Shema +ssl_mode = SSL +db_name = Ime zbirke podatkov +password = Geslo +path = Pot +sqlite_helper = Pot do datoteke za podatkovno zbirko SQLite3.
    Vnesite absolutno pot, če zaženete Forgejo kot storitev. +err_empty_db_path = Pot do zbirke podatkov SQLite3 ne sme biti prazna. +no_admin_and_disable_registration = Samodejne registracije uporabnikov ne morete onemogočiti, če ne ustvarite skrbniškega računa. +err_empty_admin_password = Administratorsko geslo ne more biti prazno. +err_empty_admin_email = Elektronsko sporočilo skrbnika ne sme biti prazno. +reinstall_error = Poskušate namestiti v obstoječo zbirko podatkov Forgejo +err_admin_name_pattern_not_allowed = Administratorjevo uporabniško ime je neveljavno, uporabniško ime se ujema z rezerviranim vzorcem +err_admin_name_is_invalid = Uporabniško ime skrbnika je neveljavno +general_title = Splošne nastavitve +app_name = Naslov mesta +app_name_helper = Tukaj lahko vnesete ime podjetja. +repo_path_helper = Oddaljene shrambe Git bodo shranjene v ta imenik. +lfs_path_helper = Datoteke, ki jim sledi sistem Git LFS, bodo shranjene v tem imeniku. Če želite onemogočiti, pustite prazno. +run_user = Zaženi kot uporabniško ime +domain = Domena strežnika +domain_helper = Domenski ali gostiteljski naslov strežnika. +ssh_port_helper = Številka vrat, na katerih posluša vaš strežnik SSH. Če želite onemogočiti, pustite prazno. +http_port = Forgejo HTTP Poslušaj vrata +http_port_helper = Številka vrat, na katerih bo poslušal spletni strežnik Forgejos. +app_url = Osnovni URL Forgejo +log_root_path = Pot dnevnika +log_root_path_helper = V ta imenik bodo zapisane dnevniške datoteke. +optional_title = Neobvezne nastavitve +mailer_user = Uporabniško ime SMTP +mailer_password = Geslo SMTP +server_service_title = Nastavitve strežnika in storitve tretje osebe +offline_mode = Omogoči lokalni način +offline_mode_popup = Onemogočite omrežja za dostavo vsebine tretjih oseb in vse vire ponudite lokalno. +disable_gravatar = Onemogočite Gravatar +allow_only_external_registration_popup = Dovolite registracijo samo prek zunanjih storitev +federated_avatar_lookup_popup = Omogočite združeno iskanje avatarja z uporabo Libravatar. +enable_captcha = Omogoči registracijo CAPTCHA +enable_captcha_popup = Zahtevajte CAPTCHA za samoprijavo uporabnika. +admin_setting_desc = Ustvarjanje skrbniškega računa ni obvezno. Prvi registrirani uporabnik bo samodejno postal skrbnik. +allow_dots_in_usernames = Uporabnikom dovolite uporabo pik v uporabniških imenih. Ne vpliva na obstoječe račune. +default_keep_email_private = Privzeto skrivanje e-poštnih naslovov +no_reply_address_helper = Ime domene za uporabnike s skritim e-poštnim naslovom. Na primer, uporabniško ime 'joe' bo prijavljeno v Git kot 'joe@noreply.example.org', če je skrita e-poštna domena nastavljena na 'noreply.example.org'. +password_algorithm = Algoritem šifriranja gesel +no_reply_address = Skrita e-poštna domena +default_allow_create_organization_popup = Novim uporabniškim računom privzeto dovolite ustvarjanje organizacij. +reinstall_confirm_check_1 = Podatki, šifrirani s SECRET_KEY v app.ini, se lahko izgubijo: uporabniki se morda ne bodo mogli prijaviti z 2FA/OTP in ogledala morda ne bodo delovala pravilno. S potrditvijo tega polja potrdite, da trenutna datoteka app.ini vsebuje pravilen SECRET_KEY. +reinstall_confirm_check_2 = Morda bo treba ponovno sinhronizirati skladišča in nastavitve. S potrditvijo tega polja potrdite, da boste kljuke za skladišča in datoteko authorized_keys ponovno sinhronizirali ročno. Potrjujete, da boste zagotovili, da so nastavitve skladišča in zrcala pravilne. +repo_path = Korenska pot repozitorija +lfs_path = Korenska pot Git LFS +run_user_helper = Uporabniško ime operacijskega sistema, v katerem se zažene Forgejo. Upoštevajte, da mora imeti ta uporabnik dostop do korenske poti skladišča. +ssh_port = Pristanišče strežnika SSH +app_url_helper = Osnovni naslov za URL-je klonov HTTP(S) in e-poštna obvestila. +email_title = Nastavitve e-pošte +smtp_addr = Gostitelj SMTP +smtp_port = Vrata SMTP +register_confirm = Zahtevajte e-poštno potrditev za registracijo +mail_notify = Omogočite e-poštna obvestila +federated_avatar_lookup = Omogočanje združenih avatarjev +disable_registration = Onemogočite samoprijavo +disable_registration_popup = Onemogočite samoprijavo uporabnika. Nove uporabniške račune bodo lahko ustvarjali samo skrbniki. +openid_signin = Omogočanje prijave OpenID +openid_signin_popup = Omogočite prijavo uporabnika prek OpenID. +openid_signup = Omogočanje samoprijave OpenID +openid_signup_popup = Omogočite samoprijavo uporabnikov na podlagi OpenID. +require_sign_in_view = Zahtevajte prijavo za ogled strani +require_sign_in_view_popup = Omejite dostop do strani na prijavljene uporabnike. Obiskovalci bodo videli samo strani za prijavo in registracijo. +admin_title = Nastavitve administratorskega računa +admin_name = Uporabniško ime administratorja +admin_password = Geslo +confirm_password = Potrditev gesla +install_btn_confirm = Namestitev programa Forgejo +admin_email = e-poštni naslov +test_git_failed = Ukaza 'git' ni bilo mogoče preizkusiti: %v +sqlite3_not_available = Ta različica programa Forgejo ne podpira SQLite3. Prosimo, prenesite uradno binarno različico s %s (ne različice 'gobuild'). +invalid_db_setting = Nastavitve podatkovne zbirke so neveljavne: %v +invalid_db_table = Tabela zbirke podatkov "%s" je neveljavna: %v +invalid_repo_path = Korenska pot skladišča je neveljavna: %v +invalid_app_data_path = Podatkovna pot aplikacije je neveljavna: %v +run_user_not_match = Uporabniško ime 'zagnati kot' ni trenutno uporabniško ime: %s -> %s +internal_token_failed = Ni uspelo ustvariti notranjega žetona: %v +secret_key_failed = Ni uspelo ustvariti tajnega ključa: %v +save_config_failed = Ni uspelo shraniti konfiguracije: %v +enable_update_checker_helper_forgejo = Redno preverja nove različice programa Forgejo s preverjanjem zapisa DNS TXT na naslovu release.forgejo.org. +invalid_admin_setting = Nastavitev skrbniškega računa je neveljavna: %v +invalid_log_root_path = Pot do dnevnika je neveljavna: %v +default_keep_email_private_popup = Privzeto skrijte e-poštne naslove novih uporabniških računov. +default_allow_create_organization = Privzeto omogočanje ustvarjanja organizacij +default_enable_timetracking = Privzeto omogočite sledenje času +default_enable_timetracking_popup = Privzeto omogočite sledenje času za nove shrambe. +invalid_password_algorithm = Nepravilen algoritem za stiskanje gesla +enable_update_checker = Omogočite preverjanje posodobitev +enable_update_checker_helper = Redno preverja izdaje novih različic tako, da se poveže s spletnim mestom gitea.io. +env_config_keys = Konfiguracija okolja +env_config_keys_prompt = V konfiguracijski datoteki bodo uporabljene tudi naslednje okoljske spremenljivke: [admin] users.allow_git_hook_tooltip = Kljuke Git se izvajajo kot uporabnik operacijskega sistema, v katerem je nameščen program Forgejo, in imajo enako raven dostopa do gostitelja. Uporabniki s tem posebnim privilegijem Git Hook lahko dostopajo do vseh skladišč Forgejo in spreminjajo vse zbirke Forgejo ter podatkovno bazo, ki jo uporablja Forgejo. Posledično lahko pridobijo tudi skrbniške privilegije Forgejo. [repo] -migrate.github_token_desc = Tu lahko vstavite enega ali več žetonov, ločenih z vejico, da bo selitev hitrejša zaradi omejitve hitrosti GitHub API. OPOZORILO: Zloraba te funkcije lahko krši pravila ponudnika storitev in povzroči blokado računa. \ No newline at end of file +migrate.github_token_desc = Tu lahko vstavite enega ali več žetonov, ločenih z vejico, da bo selitev hitrejša zaradi omejitve hitrosti GitHub API. OPOZORILO: Zloraba te funkcije lahko krši pravila ponudnika storitev in povzroči blokado računa. +ambiguous_runes_description = `Ta datoteka vsebuje znake Unicode, ki bi jih lahko zamenjali z drugimi znaki. Če menite, da je to namerno, lahko to opozorilo mirno prezrete. Za njihovo razkritje uporabite gumb Escape.` +invisible_runes_description = `Ta datoteka vsebuje nevidne znake Unicode, ki jih ljudje ne razlikujejo, vendar jih lahko računalnik obdela drugače. Če menite, da je to namerno, lahko to opozorilo mirno prezrete. Za njihovo razkritje uporabite gumb Escape.` + +[editor] +buttons.list.ordered.tooltip = Dodajte oštevilčen seznam +buttons.bold.tooltip = Dodajte krepko besedilo +buttons.quote.tooltip = Besedilo citata +buttons.code.tooltip = Dodajanje kode +buttons.list.unordered.tooltip = Dodajanje seznama z alinejami +buttons.heading.tooltip = Dodajte naslov +buttons.switch_to_legacy.tooltip = Namesto tega uporabite starejši urejevalnik +buttons.list.task.tooltip = Dodajanje seznama opravil +buttons.enable_monospace_font = Omogočite enobarvno pisavo +buttons.mention.tooltip = Omenite uporabnika ali ekipo +buttons.italic.tooltip = Dodajanje poševnega besedila +buttons.link.tooltip = Dodajanje povezave +buttons.disable_monospace_font = Onemogoči enobarvno pisavo + +[aria] +navbar = Navigacijska vrstica +footer.software = O programski opremi +footer.links = Povezave +footer = Stopka + +[settings] +oauth2_application_locked = Forgejo ob zagonu predhodno registrira nekatere aplikacije OAuth2, če je to omogočeno v konfiguraciji. Da bi preprečili nepričakovano obnašanje, jih ni mogoče niti urejati niti odstraniti. Za več informacij glejte dokumentacijo OAuth2. + +[heatmap] +less = Manj +number_of_contributions_in_the_last_12_months = %s prispevkov v zadnjih 12 mesecih +no_contributions = Ni prispevkov +more = Več + +[filter] +string.asc = A - Z +string.desc = Z - A + +[error] +occurred = Zgodila se je napaka +missing_csrf = Slaba zahteva: žeton CSRF ni prisoten +invalid_csrf = Slaba zahteva: neveljaven žeton CSRF +not_found = Tarče ni bilo mogoče najti. +network_error = Napaka omrežja + +[startpage] +app_desc = Brezskrbna, samostojno gostovana storitev Git +install = Enostaven za namestitev +lightweight = Lahka stran +lightweight_desc = Forgejo ima majhne minimalne zahteve in ga je mogoče zagnati na poceni računalniku Raspberry Pi. Prihranite energijo svojega stroja! +license = Odprta koda + +[explore] +code_search_unavailable = Iskanje kode trenutno ni na voljo. Obrnite se na skrbnika spletnega mesta. +go_to = Pojdi na +organizations = Organizacije +search.match.tooltip = Vključite samo rezultate, ki se natančno ujemajo z iskalno besedo +relevant_repositories_tooltip = Skladišča, ki so vilice ali nimajo teme, ikone in opisa, so skrita. +org_no_results = Ni bilo najdenih ustreznih organizacij. +search.fuzzy.tooltip = Vključite rezultate, ki prav tako natančno ustrezajo iskalni besedi +code = Koda +repos = Repozitoriji +users = Uporabniki +search = Iskanje +search.type.tooltip = Tip iskanja +search.fuzzy = Razpršeni +search.match = Tekma +code_no_results = Nisem našel izvorne kode, ki bi ustrezala iskalnemu izrazu. +repo_no_results = Ni bilo najdenih ustreznih skladišč. +user_no_results = Ni bilo najdenih ustreznih uporabnikov. +code_search_results = Rezultati iskanja za "%s" + +[auth] +disable_register_prompt = Registracija je onemogočena. Obrnite se na skrbnika spletnega mesta. +register_helper_msg = Že imate račun? Prijavite se zdaj! +manual_activation_only = Za dokončanje aktivacije se obrnite na skrbnika spletnega mesta. +forgot_password = Pozabljeno geslo? + +[home] +show_both_archived_unarchived = Prikazovanje arhiviranih in nearhiviranih +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štni naslov +password_holder = Geslo +my_repos = Repozitoriji +show_more_repos = Prikaži več skladišč… +my_orgs = Moje organizacije +view_home = Ogled %s +filter = Ostali filtri +feed_of = Podajanje "%s" +show_only_archived = Prikaz samo arhiviranih +show_only_unarchived = Prikaz samo nearhiviranih +show_private = Zasebno +show_both_private_public = Prikaz javnih in zasebnih +show_only_private = Prikaz samo zasebno +issues.in_your_repos = V vašem repozitorijev \ No newline at end of file diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 6586664eef..07bb39324a 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -1026,7 +1026,7 @@ editor.commit_signed_changes=Внести підписані зміни editor.commit_changes=Закомітити зміни editor.add_tmpl=Додати '' editor.commit_message_desc=Додати необов'язковий розширений опис… -editor.signoff_desc=Додатиь Signed-off-by комітом в конці повідомлення журналу комітів. +editor.signoff_desc=Додати повідомленню в журналі комітів рядок Signed-off-by від свого імені. editor.commit_directly_to_this_branch=Зробіть коміт прямо в гілку %s. editor.create_new_branch=Створити нову гілку для цього коміту та відкрити запит на злиття. editor.create_new_branch_np=Створити нову гілку для цього коміту. @@ -2051,6 +2051,22 @@ issues.close = Закрити задачу issues.role.owner_helper = Цей користувач є власником цього репозиторію. settings.mirror_settings.docs.more_information_if_disabled = Докладніше про push та pull дзеркала можна дізнатися тут: issues.comment.blocked_by_user = Ви не можете створити коментар до цієї задачі, оскільки вас заблокував власник репозиторію або автор цієї задачі. +editor.add_file = Додати файл +from_comment = (коментар) +editor.add = Додати %s +editor.update = Оновити %s +editor.patch = Застосувати латку +editor.fail_to_apply_patch = Не вдається застосувати латку «%s» +editor.patching = Латання: +editor.delete = Видалити %s +editor.new_patch = Нова латка +commit.operations = Дії +editor.cherry_pick = Висмикнути %s на: +commit.cherry-pick = Висмикнути +commit.cherry-pick-header = Висмикнути: %s +commit.cherry-pick-content = Оберіть гілку, на яку висмикнути: +pulls.expand_files = Розгорнути всі файли +pulls.collapse_files = Згорнути всі файли [org] org_name_holder=Назва організації @@ -2608,6 +2624,7 @@ packages.creator = Творець packages.version = Версія packages.package_manage_panel = Менеджмент Пакунків packages.published = Опубліковано +notices.operations = Дії [action] create_repo=створив(ла) репозиторій %s diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index 74498be9a4..2662306f06 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -655,7 +655,7 @@ webauthn=安全密钥 public_profile=公开信息 biography_placeholder=告诉我们一点您自己! (您可以使用Markdown) location_placeholder=与他人分享你的大概位置 -profile_desc=控制您的个人资料对其他用户的显示方式。您的主要电子邮件地址将用于通知、密码恢复和基于网页界面的 Git 操作 +profile_desc=控制您的个人资料对其他用户的显示方式。您的主要电子邮件地址将用于通知、密码恢复和基于网页界面的 Git 操作。 password_username_disabled=不允许非本地用户更改他们的用户名。更多详情请联系您的系统管理员。 full_name=自定义名称 website=个人网站 @@ -1764,7 +1764,7 @@ pulls.required_status_check_missing=缺少一些必要的检查。 pulls.required_status_check_administrator=作为管理员,您仍可合并此合并请求 pulls.blocked_by_approvals=此合并请求没有通过审批。已获取审批数%d个,共需要审批数%d个。 pulls.blocked_by_rejection=此合并请求有官方审核员请求的更改。 -pulls.blocked_by_official_review_requests=此合并请求需要官方评审。 +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=此合并请求被阻止是因为修改了被保护的文件: @@ -2596,10 +2596,39 @@ admin.update_flags = 更新标志 rss.must_be_on_branch = 您必须处于一个分支上才能拥有一个RSS订阅源。 admin.manage_flags = 管理标志 admin.failed_to_replace_flags = 替换仓库标志失败 -clone_in_vscodium = 在 VS Codium 中克隆 +clone_in_vscodium = 在 VSCodium 中克隆 object_format_helper = 仓库的对象格式,一旦设置无法更改。SHA1的兼容性最强。 object_format = 对象格式 mirror_sync = 已同步 +vendored = 提供者 +issues.blocked_by_user = 你无法在此仓库创建工单,因为你已被仓库所有者屏蔽。 +issues.comment.blocked_by_user = 你无法对此工单进行评论,因为你已被仓库所有者或此工单的发布者屏蔽。 +settings.wiki_rename_branch_main_desc = 将 Wiki 内部使用的分支重命名为“%s”。 此操作是永久性的且不可撤消。 +generated = 已生成 +editor.invalid_commit_mail = 用于创建提交的邮件地址无效。 +pulls.blocked_by_user = 你无法在此存储库上创建合并请求,因为您已被仓库所有者屏蔽。 +migrate.forgejo.description = 从 codeberg.org 或其他 Forgejo 实例迁移数据。 +commits.browse_further = 浏览更多 +commits.renamed_from = 重命名自 %s +pulls.nothing_to_compare_have_tag = 所选分支/标签相同。 +wiki.cancel = 取消 +settings.wiki_globally_editable = 允许任何人编辑 Wiki +settings.mirror_settings.pushed_repository = 已推送的仓库 +settings.new_owner_blocked_doer = 新所有者屏蔽了你。 +settings.enter_repo_name = 输入仓库名称以确认: +settings.wiki_rename_branch_main = 标准化 Wiki 分支名称 +settings.wiki_rename_branch_main_notices_1 = 此操作无法撤消。 +settings.wiki_branch_rename_success = wiki 仓库的分支名称已成功规范化。 +settings.confirm_wiki_branch_rename = 重命名 wiki 分支 +pulls.commit_ref_at = `提交 %[2]s 引用了此合并请求` +desc.sha256 = SHA256 +settings.ignore_stale_approvals = 忽略过时的批准 +settings.ignore_stale_approvals_desc = 不对旧的提交(过时的审查)计入已批准的合并请求数量。 +settings.archive.mirrors_unavailable = 如果仓库已存档,则仓库镜像不再可用。 +settings.wiki_rename_branch_main_notices_2 = 这将预先重命名 %s 的存储库 wiki 的内部分支。 现存的检出方式需要更新。 +settings.wiki_branch_rename_failure = 无法标准化存储库 wiki 的分支名称。 +settings.add_collaborator_blocked_our = 无法添加协作者,因为仓库所有者已屏蔽他们。 +settings.add_collaborator_blocked_them = 无法添加协作者,因为已屏蔽仓库所有者。 [org] org_name_holder=组织名称 @@ -2724,6 +2753,7 @@ teams.all_repositories_admin_permission_desc=该团队拥有 管理%s 中的团队 %s。 teams.invite.by=邀请人 %s teams.invite.description=请点击下面的按钮加入团队。 +follow_blocked_user = 你无法关注此组织,因为此组织已屏蔽你。 [admin] dashboard=管理面板 @@ -2868,7 +2898,7 @@ users.cannot_delete_self=你不能删除自己 users.still_own_repo=此用户仍然拥有一个或多个仓库。必须首先删除或转让这些仓库。 users.still_has_org=此用户是组织的成员。必须先从组织中删除用户。 users.purge=清理用户 -users.purge_help=强制删除用户和用户拥有的任何仓库、组织和软件包。所有评论也将被删除。 +users.purge_help=强制删除用户和用户拥有的任何仓库、组织和软件包。所有评论和工单也将被删除。 users.still_own_packages=此用户仍然拥有一个或多个软件包,请先删除这些软件包。 users.deletion_success=用户帐户已被删除。 users.reset_2fa=重置两步验证 @@ -3251,6 +3281,15 @@ notices.type_2=任务 notices.desc=提示描述 notices.op=操作 notices.delete_success=系统通知已被删除。 +dashboard.sync_repo_tags = 将 git 数据中的标签同步到数据库 +dashboard.sync_tag.started = 标签同步开始 +self_check = 自检 +self_check.no_problem_found = 未找到问题。 +self_check.database_collation_mismatch = 期望数据库使用排序规则:%s +self_check.database_collation_case_insensitive = 数据库正在使用 %s 排序规则,这是一种不敏感的排序规则。 尽管 Forgejo 可以使用它,但在极少数情况下可能无法按照预期工作。 +self_check.database_inconsistent_collation_columns = 数据库正在使用 %s 排序规则,但与这些列使用的排序规则不匹配。 这可能会导致一些意想不到的问题。 +self_check.database_fix_mysql = 对于 MySQL/MariaDB 用户,您可以使用 "gitea doctor convert" 命令来修复排序规则问题,也可以通过SQL命令 "ALTER ... COLLATE ..." 来手动修复问题。 +self_check.database_fix_mssql = 对于 MSSQL 用户,目前您只能通过SQL命令 "ALTER ... COLLATE ..." 来手动修复问题。 [action] create_repo=创建了仓库 %s @@ -3489,6 +3528,9 @@ owner.settings.cleanuprules.success.delete=清理规则已删除。 owner.settings.chef.title=Chef 注册中心 owner.settings.chef.keypair=生成密钥对 owner.settings.chef.keypair.description=需要密钥对才能向 Chef 注册中心进行身份验证。如果您之前已经生成过密钥对,生成新的密钥对将丢弃旧的密钥对。 +rpm.repository = 仓库信息 +rpm.repository.architectures = 架构 +rpm.repository.multiple_groups = 该软件包可在多个组中使用。 [secrets] secrets=密钥 From 83123b493f3ae25d07d81c86b1a78afe1c17db53 Mon Sep 17 00:00:00 2001 From: Chris Copeland Date: Mon, 12 Feb 2024 14:37:23 -0800 Subject: [PATCH 034/271] Add merge style `fast-forward-only` (#28954) With this option, it is possible to require a linear commit history with the following benefits over the next best option `Rebase+fast-forward`: The original commits continue existing, with the original signatures continuing to stay valid instead of being rewritten, there is no merge commit, and reverting commits becomes easier. Closes #24906 --- custom/conf/app.example.ini | 2 +- .../config-cheat-sheet.en-us.md | 2 +- .../config-cheat-sheet.zh-cn.md | 2 +- models/error.go | 17 ++++ models/repo/git.go | 2 + models/repo/repo_unit.go | 2 + modules/git/error.go | 2 +- modules/repository/create.go | 6 +- modules/structs/repo.go | 5 +- options/locale/locale_en-US.ini | 1 + routers/api/v1/repo/repo.go | 4 + routers/api/v1/repo/repo_test.go | 2 + routers/web/repo/issue.go | 2 + routers/web/repo/setting/setting.go | 1 + services/convert/repository.go | 3 + services/forms/repo_form.go | 5 +- services/pull/merge.go | 11 +++ services/pull/merge_ff_only.go | 21 +++++ services/pull/merge_merge.go | 2 +- templates/repo/issue/view_content/pull.tmpl | 9 +- .../view_content/pull_merge_instruction.tmpl | 4 + templates/repo/settings/units/pulls.tmpl | 11 +++ templates/swagger/v1_json.tmpl | 12 ++- tests/integration/api_repo_edit_test.go | 3 + tests/integration/pull_merge_test.go | 84 +++++++++++++++++++ 25 files changed, 204 insertions(+), 11 deletions(-) create mode 100644 services/pull/merge_ff_only.go diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index 6d7b3bedf6..1b53732b1b 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1061,7 +1061,7 @@ LEVEL = Info ;; List of keywords used in Pull Request comments to automatically reopen a related issue ;REOPEN_KEYWORDS = reopen,reopens,reopened ;; -;; Set default merge style for repository creating, valid options: merge, rebase, rebase-merge, squash +;; Set default merge style for repository creating, valid options: merge, rebase, rebase-merge, squash, fast-forward-only ;DEFAULT_MERGE_STYLE = merge ;; ;; In the default merge message for squash commits include at most this many commits diff --git a/docs/content/administration/config-cheat-sheet.en-us.md b/docs/content/administration/config-cheat-sheet.en-us.md index b7c6ceb431..aa2cbcee5d 100644 --- a/docs/content/administration/config-cheat-sheet.en-us.md +++ b/docs/content/administration/config-cheat-sheet.en-us.md @@ -126,7 +126,7 @@ In addition, there is _`StaticRootPath`_ which can be set as a built-in at build keywords used in Pull Request comments to automatically close a related issue - `REOPEN_KEYWORDS`: **reopen**, **reopens**, **reopened**: List of keywords used in Pull Request comments to automatically reopen a related issue -- `DEFAULT_MERGE_STYLE`: **merge**: Set default merge style for repository creating, valid options: `merge`, `rebase`, `rebase-merge`, `squash` +- `DEFAULT_MERGE_STYLE`: **merge**: Set default merge style for repository creating, valid options: `merge`, `rebase`, `rebase-merge`, `squash`, `fast-forward-only` - `DEFAULT_MERGE_MESSAGE_COMMITS_LIMIT`: **50**: In the default merge message for squash commits include at most this many commits. Set to `-1` to include all commits - `DEFAULT_MERGE_MESSAGE_SIZE`: **5120**: In the default merge message for squash commits limit the size of the commit messages. Set to `-1` to have no limit. Only used if `POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES` is `true`. - `DEFAULT_MERGE_MESSAGE_ALL_AUTHORS`: **false**: In the default merge message for squash commits walk all commits to include all authors in the Co-authored-by otherwise just use those in the limited list diff --git a/docs/content/administration/config-cheat-sheet.zh-cn.md b/docs/content/administration/config-cheat-sheet.zh-cn.md index 8236852ad3..01906930cb 100644 --- a/docs/content/administration/config-cheat-sheet.zh-cn.md +++ b/docs/content/administration/config-cheat-sheet.zh-cn.md @@ -125,7 +125,7 @@ menu: - `CLOSE_KEYWORDS`: **close**, **closes**, **closed**, **fix**, **fixes**, **fixed**, **resolve**, **resolves**, **resolved**: 在拉取请求评论中用于自动关闭相关问题的关键词列表。 - `REOPEN_KEYWORDS`: **reopen**, **reopens**, **reopened**: 在拉取请求评论中用于自动重新打开相关问题的 关键词列表。 -- `DEFAULT_MERGE_STYLE`: **merge**: 设置创建仓库的默认合并方式,可选: `merge`, `rebase`, `rebase-merge`, `squash` +- `DEFAULT_MERGE_STYLE`: **merge**: 设置创建仓库的默认合并方式,可选: `merge`, `rebase`, `rebase-merge`, `squash`, `fast-forward-only` - `DEFAULT_MERGE_MESSAGE_COMMITS_LIMIT`: **50**: 在默认合并消息中,对于`squash`提交,最多包括此数量的提交。设置为 -1 以包括所有提交。 - `DEFAULT_MERGE_MESSAGE_SIZE`: **5120**: 在默认的合并消息中,对于`squash`提交,限制提交消息的大小。设置为 `-1`以取消限制。仅在`POPULATE_SQUASH_COMMENT_WITH_COMMIT_MESSAGES`为`true`时使用。 - `DEFAULT_MERGE_MESSAGE_ALL_AUTHORS`: **false**: 在默认合并消息中,对于`squash`提交,遍历所有提交以包括所有作者的`Co-authored-by`,否则仅使用限定列表中的作者。 diff --git a/models/error.go b/models/error.go index 83dfe29805..75c53245de 100644 --- a/models/error.go +++ b/models/error.go @@ -493,6 +493,23 @@ func (err ErrMergeUnrelatedHistories) Error() string { return fmt.Sprintf("Merge UnrelatedHistories Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut) } +// ErrMergeDivergingFastForwardOnly represents an error if a fast-forward-only merge fails because the branches diverge +type ErrMergeDivergingFastForwardOnly struct { + StdOut string + StdErr string + Err error +} + +// IsErrMergeDivergingFastForwardOnly checks if an error is a ErrMergeDivergingFastForwardOnly. +func IsErrMergeDivergingFastForwardOnly(err error) bool { + _, ok := err.(ErrMergeDivergingFastForwardOnly) + return ok +} + +func (err ErrMergeDivergingFastForwardOnly) Error() string { + return fmt.Sprintf("Merge DivergingFastForwardOnly Error: %v: %s\n%s", err.Err, err.StdErr, err.StdOut) +} + // ErrRebaseConflicts represents an error if rebase fails with a conflict type ErrRebaseConflicts struct { Style repo_model.MergeStyle diff --git a/models/repo/git.go b/models/repo/git.go index 610c554296..388bf86522 100644 --- a/models/repo/git.go +++ b/models/repo/git.go @@ -21,6 +21,8 @@ const ( MergeStyleRebaseMerge MergeStyle = "rebase-merge" // MergeStyleSquash squash commits into single commit before merging MergeStyleSquash MergeStyle = "squash" + // MergeStyleFastForwardOnly fast-forward merge if possible, otherwise fail + MergeStyleFastForwardOnly MergeStyle = "fast-forward-only" // MergeStyleManuallyMerged pr has been merged manually, just mark it as merged directly MergeStyleManuallyMerged MergeStyle = "manually-merged" // MergeStyleRebaseUpdate not a merge style, used to update pull head by rebase diff --git a/models/repo/repo_unit.go b/models/repo/repo_unit.go index 3df5236ea7..08058b0d45 100644 --- a/models/repo/repo_unit.go +++ b/models/repo/repo_unit.go @@ -153,6 +153,7 @@ type PullRequestsConfig struct { AllowRebase bool AllowRebaseMerge bool AllowSquash bool + AllowFastForwardOnly bool AllowManualMerge bool AutodetectManualMerge bool AllowRebaseUpdate bool @@ -179,6 +180,7 @@ func (cfg *PullRequestsConfig) IsMergeStyleAllowed(mergeStyle MergeStyle) bool { mergeStyle == MergeStyleRebase && cfg.AllowRebase || mergeStyle == MergeStyleRebaseMerge && cfg.AllowRebaseMerge || mergeStyle == MergeStyleSquash && cfg.AllowSquash || + mergeStyle == MergeStyleFastForwardOnly && cfg.AllowFastForwardOnly || mergeStyle == MergeStyleManuallyMerged && cfg.AllowManualMerge } diff --git a/modules/git/error.go b/modules/git/error.go index dc10d451b3..91d25eca69 100644 --- a/modules/git/error.go +++ b/modules/git/error.go @@ -96,7 +96,7 @@ func (err ErrBranchNotExist) Unwrap() error { return util.ErrNotExist } -// ErrPushOutOfDate represents an error if merging fails due to unrelated histories +// ErrPushOutOfDate represents an error if merging fails due to the base branch being updated type ErrPushOutOfDate struct { StdOut string StdErr string diff --git a/modules/repository/create.go b/modules/repository/create.go index 7c954a1412..ca2150b972 100644 --- a/modules/repository/create.go +++ b/modules/repository/create.go @@ -87,7 +87,11 @@ func CreateRepositoryByExample(ctx context.Context, doer, u *user_model.User, re units = append(units, repo_model.RepoUnit{ RepoID: repo.ID, Type: tp, - Config: &repo_model.PullRequestsConfig{AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), AllowRebaseUpdate: true}, + Config: &repo_model.PullRequestsConfig{ + AllowMerge: true, AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, AllowFastForwardOnly: true, + DefaultMergeStyle: repo_model.MergeStyle(setting.Repository.PullRequest.DefaultMergeStyle), + AllowRebaseUpdate: true, + }, }) } else { units = append(units, repo_model.RepoUnit{ diff --git a/modules/structs/repo.go b/modules/structs/repo.go index e20b6bc26e..a50cddaf7e 100644 --- a/modules/structs/repo.go +++ b/modules/structs/repo.go @@ -99,6 +99,7 @@ type Repository struct { AllowRebase bool `json:"allow_rebase"` AllowRebaseMerge bool `json:"allow_rebase_explicit"` AllowSquash bool `json:"allow_squash_merge"` + AllowFastForwardOnly bool `json:"allow_fast_forward_only_merge"` AllowRebaseUpdate bool `json:"allow_rebase_update"` DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"` DefaultMergeStyle string `json:"default_merge_style"` @@ -198,6 +199,8 @@ type EditRepoOption struct { AllowRebaseMerge *bool `json:"allow_rebase_explicit,omitempty"` // either `true` to allow squash-merging pull requests, or `false` to prevent squash-merging. AllowSquash *bool `json:"allow_squash_merge,omitempty"` + // either `true` to allow fast-forward-only merging pull requests, or `false` to prevent fast-forward-only merging. + AllowFastForwardOnly *bool `json:"allow_fast_forward_only_merge,omitempty"` // either `true` to allow mark pr as merged manually, or `false` to prevent it. AllowManualMerge *bool `json:"allow_manual_merge,omitempty"` // either `true` to enable AutodetectManualMerge, or `false` to prevent it. Note: In some special cases, misjudgments can occur. @@ -206,7 +209,7 @@ type EditRepoOption struct { AllowRebaseUpdate *bool `json:"allow_rebase_update,omitempty"` // set to `true` to delete pr branch after merge by default DefaultDeleteBranchAfterMerge *bool `json:"default_delete_branch_after_merge,omitempty"` - // set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", or "squash". + // set to a merge style to be used by this repository: "merge", "rebase", "rebase-merge", "squash", or "fast-forward-only". DefaultMergeStyle *string `json:"default_merge_style,omitempty"` // set to `true` to allow edits from maintainers by default DefaultAllowMaintainerEdit *bool `json:"default_allow_maintainer_edit,omitempty"` diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 9c8b3fc541..e81d493b19 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1816,6 +1816,7 @@ pulls.merge_pull_request = Create merge commit pulls.rebase_merge_pull_request = Rebase then fast-forward pulls.rebase_merge_commit_pull_request = Rebase then create merge commit pulls.squash_merge_pull_request = Create squash commit +pulls.fast_forward_only_merge_pull_request = Fast-forward only pulls.merge_manually = Manually merged pulls.merge_commit_id = The merge commit ID pulls.require_signed_wont_sign = The branch requires signed commits but this merge will not be signed diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 08e79e544a..de105f474f 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -897,6 +897,7 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { AllowRebase: true, AllowRebaseMerge: true, AllowSquash: true, + AllowFastForwardOnly: true, AllowManualMerge: true, AutodetectManualMerge: false, AllowRebaseUpdate: true, @@ -923,6 +924,9 @@ func updateRepoUnits(ctx *context.APIContext, opts api.EditRepoOption) error { if opts.AllowSquash != nil { config.AllowSquash = *opts.AllowSquash } + if opts.AllowFastForwardOnly != nil { + config.AllowFastForwardOnly = *opts.AllowFastForwardOnly + } if opts.AllowManualMerge != nil { config.AllowManualMerge = *opts.AllowManualMerge } diff --git a/routers/api/v1/repo/repo_test.go b/routers/api/v1/repo/repo_test.go index 29e2d1f21d..08ba7fabac 100644 --- a/routers/api/v1/repo/repo_test.go +++ b/routers/api/v1/repo/repo_test.go @@ -35,6 +35,7 @@ func TestRepoEdit(t *testing.T) { allowRebase := false allowRebaseMerge := false allowSquashMerge := false + allowFastForwardOnlyMerge := false archived := true opts := api.EditRepoOption{ Name: &ctx.Repo.Repository.Name, @@ -50,6 +51,7 @@ func TestRepoEdit(t *testing.T) { AllowRebase: &allowRebase, AllowRebaseMerge: &allowRebaseMerge, AllowSquash: &allowSquashMerge, + AllowFastForwardOnly: &allowFastForwardOnlyMerge, Archived: &archived, } diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index 3e7b099bba..fb4f2bad98 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -1871,6 +1871,8 @@ func ViewIssue(ctx *context.Context) { mergeStyle = repo_model.MergeStyleRebaseMerge } else if prConfig.AllowSquash { mergeStyle = repo_model.MergeStyleSquash + } else if prConfig.AllowFastForwardOnly { + mergeStyle = repo_model.MergeStyleFastForwardOnly } else if prConfig.AllowManualMerge { mergeStyle = repo_model.MergeStyleManuallyMerged } diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index 8a429c359c..dcb4be7ef8 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -251,6 +251,7 @@ func UnitsPost(ctx *context.Context) { AllowRebase: form.PullsAllowRebase, AllowRebaseMerge: form.PullsAllowRebaseMerge, AllowSquash: form.PullsAllowSquash, + AllowFastForwardOnly: form.PullsAllowFastForwardOnly, AllowManualMerge: form.PullsAllowManualMerge, AutodetectManualMerge: form.EnableAutodetectManualMerge, AllowRebaseUpdate: form.PullsAllowRebaseUpdate, diff --git a/services/convert/repository.go b/services/convert/repository.go index b032d97d73..466d19d563 100644 --- a/services/convert/repository.go +++ b/services/convert/repository.go @@ -93,6 +93,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR allowRebase := false allowRebaseMerge := false allowSquash := false + allowFastForwardOnly := false allowRebaseUpdate := false defaultDeleteBranchAfterMerge := false defaultMergeStyle := repo_model.MergeStyleMerge @@ -105,6 +106,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR allowRebase = config.AllowRebase allowRebaseMerge = config.AllowRebaseMerge allowSquash = config.AllowSquash + allowFastForwardOnly = config.AllowFastForwardOnly allowRebaseUpdate = config.AllowRebaseUpdate defaultDeleteBranchAfterMerge = config.DefaultDeleteBranchAfterMerge defaultMergeStyle = config.GetDefaultMergeStyle() @@ -220,6 +222,7 @@ func innerToRepo(ctx context.Context, repo *repo_model.Repository, permissionInR AllowRebase: allowRebase, AllowRebaseMerge: allowRebaseMerge, AllowSquash: allowSquash, + AllowFastForwardOnly: allowFastForwardOnly, AllowRebaseUpdate: allowRebaseUpdate, DefaultDeleteBranchAfterMerge: defaultDeleteBranchAfterMerge, DefaultMergeStyle: string(defaultMergeStyle), diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 9527916ae0..6ecb4ea768 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -170,6 +170,7 @@ type RepoUnitSettingForm struct { PullsAllowRebase bool PullsAllowRebaseMerge bool PullsAllowSquash bool + PullsAllowFastForwardOnly bool PullsAllowManualMerge bool PullsDefaultMergeStyle string EnableAutodetectManualMerge bool @@ -609,8 +610,8 @@ func (f *InitializeLabelsForm) Validate(req *http.Request, errs binding.Errors) // swagger:model MergePullRequestOption type MergePullRequestForm struct { // required: true - // enum: merge,rebase,rebase-merge,squash,manually-merged - Do string `binding:"Required;In(merge,rebase,rebase-merge,squash,manually-merged)"` + // enum: merge,rebase,rebase-merge,squash,fast-forward-only,manually-merged + Do string `binding:"Required;In(merge,rebase,rebase-merge,squash,fast-forward-only,manually-merged)"` MergeTitleField string MergeMessageField string MergeCommitID string // only used for manually-merged diff --git a/services/pull/merge.go b/services/pull/merge.go index 718e964014..f09067996c 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -273,6 +273,10 @@ func doMergeAndPush(ctx context.Context, pr *issues_model.PullRequest, doer *use if err := doMergeStyleSquash(mergeCtx, message); err != nil { return "", err } + case repo_model.MergeStyleFastForwardOnly: + if err := doMergeStyleFastForwardOnly(mergeCtx); err != nil { + return "", err + } default: return "", models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle} } @@ -383,6 +387,13 @@ func runMergeCommand(ctx *mergeContext, mergeStyle repo_model.MergeStyle, cmd *g StdErr: ctx.errbuf.String(), Err: err, } + } else if mergeStyle == repo_model.MergeStyleFastForwardOnly && strings.Contains(ctx.errbuf.String(), "Not possible to fast-forward, aborting") { + log.Debug("MergeDivergingFastForwardOnly %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) + return models.ErrMergeDivergingFastForwardOnly{ + StdOut: ctx.outbuf.String(), + StdErr: ctx.errbuf.String(), + Err: err, + } } log.Error("git merge %-v: %v\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) return fmt.Errorf("git merge %v: %w\n%s\n%s", ctx.pr, err, ctx.outbuf.String(), ctx.errbuf.String()) diff --git a/services/pull/merge_ff_only.go b/services/pull/merge_ff_only.go new file mode 100644 index 0000000000..f57c732104 --- /dev/null +++ b/services/pull/merge_ff_only.go @@ -0,0 +1,21 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package pull + +import ( + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/log" +) + +// doMergeStyleFastForwardOnly merges the tracking into the current HEAD - which is assumed to be staging branch (equal to the pr.BaseBranch) +func doMergeStyleFastForwardOnly(ctx *mergeContext) error { + cmd := git.NewCommand(ctx, "merge", "--ff-only").AddDynamicArguments(trackingBranch) + if err := runMergeCommand(ctx, repo_model.MergeStyleFastForwardOnly, cmd); err != nil { + log.Error("%-v Unable to merge tracking into base: %v", ctx.pr, err) + return err + } + + return nil +} diff --git a/services/pull/merge_merge.go b/services/pull/merge_merge.go index 0f7664297a..bf56c071db 100644 --- a/services/pull/merge_merge.go +++ b/services/pull/merge_merge.go @@ -9,7 +9,7 @@ import ( "code.gitea.io/gitea/modules/log" ) -// doMergeStyleMerge merges the tracking into the current HEAD - which is assumed to tbe staging branch (equal to the pr.BaseBranch) +// doMergeStyleMerge merges the tracking branch into the current HEAD - which is assumed to be the staging branch (equal to the pr.BaseBranch) func doMergeStyleMerge(ctx *mergeContext, message string) error { cmd := git.NewCommand(ctx, "merge", "--no-ff", "--no-commit").AddDynamicArguments(trackingBranch) if err := runMergeCommand(ctx, repo_model.MergeStyleMerge, cmd); err != nil { diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index 2b5776ea03..f1ab53eb67 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -197,7 +197,7 @@ {{if .AllowMerge}} {{/* user is allowed to merge */}} {{$prUnit := .Repository.MustGetUnit $.Context $.UnitTypePullRequests}} {{$approvers := (.Issue.PullRequest.GetApprovers ctx)}} - {{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash}} + {{if or $prUnit.PullRequestsConfig.AllowMerge $prUnit.PullRequestsConfig.AllowRebase $prUnit.PullRequestsConfig.AllowRebaseMerge $prUnit.PullRequestsConfig.AllowSquash $prUnit.PullRequestsConfig.AllowFastForwardOnly}} {{$hasPendingPullRequestMergeTip := ""}} {{if .HasPendingPullRequestMerge}} {{$createdPRMergeStr := TimeSinceUnix .PendingPullRequestMerge.CreatedUnix ctx.Locale}} @@ -268,6 +268,13 @@ 'mergeMessageFieldText': {{.GetCommitMessages}} + defaultSquashMergeMessage, 'hideAutoMerge': generalHideAutoMerge, }, + { + 'name': 'fast-forward-only', + 'allowed': {{and $prUnit.PullRequestsConfig.AllowFastForwardOnly (eq .Issue.PullRequest.CommitsBehind 0)}}, + 'textDoMerge': {{ctx.Locale.Tr "repo.pulls.fast_forward_only_merge_pull_request"}}, + 'hideMergeMessageTexts': true, + 'hideAutoMerge': generalHideAutoMerge, + }, { 'name': 'manually-merged', 'allowed': {{$prUnit.PullRequestsConfig.AllowManualMerge}}, diff --git a/templates/repo/issue/view_content/pull_merge_instruction.tmpl b/templates/repo/issue/view_content/pull_merge_instruction.tmpl index 3dab44710e..a214f29786 100644 --- a/templates/repo/issue/view_content/pull_merge_instruction.tmpl +++ b/templates/repo/issue/view_content/pull_merge_instruction.tmpl @@ -35,6 +35,10 @@
    git checkout {{.PullRequest.BaseBranch}}
    git merge --squash {{$localBranch}}
    +
    +
    git checkout {{.PullRequest.BaseBranch}}
    +
    git merge --ff-only {{$localBranch}}
    +
    git checkout {{.PullRequest.BaseBranch}}
    git merge {{$localBranch}}
    diff --git a/templates/repo/settings/units/pulls.tmpl b/templates/repo/settings/units/pulls.tmpl index e735fe974c..4e9c53e0f4 100644 --- a/templates/repo/settings/units/pulls.tmpl +++ b/templates/repo/settings/units/pulls.tmpl @@ -42,6 +42,12 @@
    +
    +
    + + +
    +
    @@ -59,6 +65,7 @@ + {{svg "octicon-triangle-down" 14 "dropdown icon"}}
    {{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "merge")}} @@ -73,12 +80,16 @@ {{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "squash")}} {{ctx.Locale.Tr "repo.pulls.squash_merge_pull_request"}} {{end}} + {{if (eq $prUnit.PullRequestsConfig.DefaultMergeStyle "fast-forward-only")}} + {{ctx.Locale.Tr "repo.pulls.fast_forward_only_merge_pull_request"}} + {{end}}
    diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 67e949f4f2..8a40cf76d4 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -19930,6 +19930,11 @@ "description": "EditRepoOption options when editing a repository's properties", "type": "object", "properties": { + "allow_fast_forward_only_merge": { + "description": "either `true` to allow fast-forward-only merging pull requests, or `false` to prevent fast-forward-only merging.", + "type": "boolean", + "x-go-name": "AllowFastForwardOnly" + }, "allow_manual_merge": { "description": "either `true` to allow mark pr as merged manually, or `false` to prevent it.", "type": "boolean", @@ -19986,7 +19991,7 @@ "x-go-name": "DefaultDeleteBranchAfterMerge" }, "default_merge_style": { - "description": "set to a merge style to be used by this repository: \"merge\", \"rebase\", \"rebase-merge\", or \"squash\".", + "description": "set to a merge style to be used by this repository: \"merge\", \"rebase\", \"rebase-merge\", \"squash\", or \"fast-forward-only\".", "type": "string", "x-go-name": "DefaultMergeStyle" }, @@ -21395,6 +21400,7 @@ "rebase", "rebase-merge", "squash", + "fast-forward-only", "manually-merged" ] }, @@ -22795,6 +22801,10 @@ "description": "Repository represents a repository", "type": "object", "properties": { + "allow_fast_forward_only_merge": { + "type": "boolean", + "x-go-name": "AllowFastForwardOnly" + }, "allow_merge_commits": { "type": "boolean", "x-go-name": "AllowMerge" diff --git a/tests/integration/api_repo_edit_test.go b/tests/integration/api_repo_edit_test.go index c4fc2177b4..7de8910ee0 100644 --- a/tests/integration/api_repo_edit_test.go +++ b/tests/integration/api_repo_edit_test.go @@ -65,6 +65,7 @@ func getRepoEditOptionFromRepo(repo *repo_model.Repository) *api.EditRepoOption allowRebase := false allowRebaseMerge := false allowSquash := false + allowFastForwardOnly := false if unit, err := repo.GetUnit(db.DefaultContext, unit_model.TypePullRequests); err == nil { config := unit.PullRequestsConfig() hasPullRequests = true @@ -73,6 +74,7 @@ func getRepoEditOptionFromRepo(repo *repo_model.Repository) *api.EditRepoOption allowRebase = config.AllowRebase allowRebaseMerge = config.AllowRebaseMerge allowSquash = config.AllowSquash + allowFastForwardOnly = config.AllowFastForwardOnly } archived := repo.IsArchived return &api.EditRepoOption{ @@ -92,6 +94,7 @@ func getRepoEditOptionFromRepo(repo *repo_model.Repository) *api.EditRepoOption AllowRebase: &allowRebase, AllowRebaseMerge: &allowRebaseMerge, AllowSquash: &allowSquash, + AllowFastForwardOnly: &allowFastForwardOnly, Archived: &archived, } } diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index 1f6695ec50..1c5c8f6d45 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -364,6 +364,90 @@ func TestCantMergeUnrelated(t *testing.T) { }) } +func TestFastForwardOnlyMerge(t *testing.T) { + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFileToNewBranch(t, session, "user1", "repo1", "master", "update", "README.md", "Hello, World 2\n") + + // Use API to create a pr from update to master + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", "user1", "repo1"), &api.CreatePullRequestOption{ + Head: "update", + Base: "master", + Title: "create a pr that can be fast-forward-only merged", + }).AddTokenAuth(token) + session.MakeRequest(t, req, http.StatusCreated) + + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ + Name: "user1", + }) + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ + OwnerID: user1.ID, + Name: "repo1", + }) + + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ + HeadRepoID: repo1.ID, + BaseRepoID: repo1.ID, + HeadBranch: "update", + BaseBranch: "master", + }) + + gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name)) + assert.NoError(t, err) + + err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "FAST-FORWARD-ONLY", false) + + assert.NoError(t, err) + + gitRepo.Close() + }) +} + +func TestCantFastForwardOnlyMergeDiverging(t *testing.T) { + onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { + session := loginUser(t, "user1") + testRepoFork(t, session, "user2", "repo1", "user1", "repo1") + testEditFileToNewBranch(t, session, "user1", "repo1", "master", "diverging", "README.md", "Hello, World diverged\n") + testEditFile(t, session, "user1", "repo1", "master", "README.md", "Hello, World 2\n") + + // Use API to create a pr from diverging to update + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + req := NewRequestWithJSON(t, http.MethodPost, fmt.Sprintf("/api/v1/repos/%s/%s/pulls", "user1", "repo1"), &api.CreatePullRequestOption{ + Head: "diverging", + Base: "master", + Title: "create a pr from a diverging branch", + }).AddTokenAuth(token) + session.MakeRequest(t, req, http.StatusCreated) + + user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ + Name: "user1", + }) + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ + OwnerID: user1.ID, + Name: "repo1", + }) + + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ + HeadRepoID: repo1.ID, + BaseRepoID: repo1.ID, + HeadBranch: "diverging", + BaseBranch: "master", + }) + + gitRepo, err := git.OpenRepository(git.DefaultContext, repo_model.RepoPath(user1.Name, repo1.Name)) + assert.NoError(t, err) + + err = pull.Merge(context.Background(), pr, user1, gitRepo, repo_model.MergeStyleFastForwardOnly, "", "DIVERGING", false) + + assert.Error(t, err, "Merge should return an error due to being for a diverging branch") + assert.True(t, models.IsErrMergeDivergingFastForwardOnly(err), "Merge error is not a diverging fast-forward-only error") + + gitRepo.Close() + }) +} + func TestConflictChecking(t *testing.T) { onGiteaRun(t, func(t *testing.T, giteaURL *url.URL) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) From a3a7af5a2bd3177cff634c37b0ee55f254ae4984 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Wed, 14 Feb 2024 17:31:06 +0100 Subject: [PATCH 035/271] Update .deadcode-out Signed-off-by: Gergely Nagy --- .deadcode-out | 1 + 1 file changed, 1 insertion(+) diff --git a/.deadcode-out b/.deadcode-out index 7efe6f8406..9d1d7336e4 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -14,6 +14,7 @@ package "code.gitea.io/gitea/models" func (ErrUpdateTaskNotExist).Error func (ErrUpdateTaskNotExist).Unwrap func IsErrSHANotFound + func IsErrMergeDivergingFastForwardOnly func GetYamlFixturesAccess package "code.gitea.io/gitea/models/actions" From 259ad5d61493590732cc9fcc16158fc00ebdcdae Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Wed, 14 Feb 2024 00:35:38 +0100 Subject: [PATCH 036/271] agit: Automatically fill in the description If no `-o description=` is provided, fill it in automatically from the first commit, just like title. Also allow filling in either, and specifying them independently. This means that `git push origin HEAD:refs/for/main/my-local-branch` will fill in the PR title, *and* the description, without having to specify additional parameters. The description is the first commit's message without the first two lines (the title and a newline, as customary). Signed-off-by: Gergely Nagy --- services/agit/agit.go | 28 +++++++------ tests/integration/git_test.go | 74 ++++++++++++++++++++++++++++++++++- 2 files changed, 90 insertions(+), 12 deletions(-) diff --git a/services/agit/agit.go b/services/agit/agit.go index bc68372570..98367ab973 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -38,6 +38,9 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. _, forcePush = opts.GitPushOptions["force-push"] objectFormat, _ := gitRepo.GetObjectFormat() + title, hasTitle := opts.GitPushOptions["title"] + description, hasDesc := opts.GitPushOptions["description"] + for i := range opts.OldCommitIDs { if opts.NewCommitIDs[i] == objectFormat.EmptyObjectID().String() { results = append(results, private.HookProcReceiveRefResult{ @@ -102,18 +105,21 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. return nil, fmt.Errorf("Failed to get unmerged agit flow pull request in repository: %s/%s Error: %w", ownerName, repoName, err) } - // create a new pull request - if len(title) == 0 { - var has bool - title, has = opts.GitPushOptions["title"] - if !has || len(title) == 0 { - commit, err := gitRepo.GetCommit(opts.NewCommitIDs[i]) - if err != nil { - return nil, fmt.Errorf("Failed to get commit %s in repository: %s/%s Error: %w", opts.NewCommitIDs[i], ownerName, repoName, err) - } - title = strings.Split(commit.CommitMessage, "\n")[0] + // automatically fill out the title and the description from the first commit. + shouldGetCommit := len(title) == 0 || len(description) == 0 + + var commit *git.Commit + if shouldGetCommit { + commit, err = gitRepo.GetCommit(opts.NewCommitIDs[i]) + if err != nil { + return nil, fmt.Errorf("Failed to get commit %s in repository: %s/%s Error: %w", opts.NewCommitIDs[i], ownerName, repoName, err) } - description = opts.GitPushOptions["description"] + } + if !hasTitle || len(title) == 0 { + title = strings.Split(commit.CommitMessage, "\n")[0] + } + if !hasDesc || len(description) == 0 { + _, description, _ = strings.Cut(commit.CommitMessage, "\n\n") } pusher, err := user_model.GetUserByID(ctx, opts.UserID) diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index 0afe9fa580..3de83f2151 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -31,6 +31,7 @@ import ( "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const ( @@ -832,7 +833,7 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB Name: "user2", When: time.Now(), }, - Message: "Testing commit 2", + Message: "Testing commit 2\n\nLonger description.", }) assert.NoError(t, err) commit, err = gitRepo.GetRefCommitID("HEAD") @@ -864,6 +865,77 @@ func doCreateAgitFlowPull(dstPath string, ctx *APITestContext, baseBranch, headB assert.False(t, prMsg.HasMerged) assert.Equal(t, commit, prMsg.Head.Sha) }) + t.Run("PushParams", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + t.Run("NoParams", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + _, _, gitErr := git.NewCommand(git.DefaultContext, "push", "origin").AddDynamicArguments("HEAD:refs/for/master/" + headBranch + "-implicit").RunStdString(&git.RunOpts{Dir: dstPath}) + assert.NoError(t, gitErr) + + unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+3) + pr3 := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ + HeadRepoID: repo.ID, + Flow: issues_model.PullRequestFlowAGit, + Index: pr1.Index + 2, + }) + assert.NotEmpty(t, pr3) + err := pr3.LoadIssue(db.DefaultContext) + assert.NoError(t, err) + + _, err2 := doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr3.Index)(t) + require.NoError(t, err2) + + assert.Equal(t, "Testing commit 2", pr3.Issue.Title) + assert.Contains(t, pr3.Issue.Content, "Longer description.") + }) + t.Run("TitleOverride", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + _, _, gitErr := git.NewCommand(git.DefaultContext, "push", "origin", "-o", "title=my-shiny-title").AddDynamicArguments("HEAD:refs/for/master/" + headBranch + "-implicit-2").RunStdString(&git.RunOpts{Dir: dstPath}) + assert.NoError(t, gitErr) + + unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+4) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ + HeadRepoID: repo.ID, + Flow: issues_model.PullRequestFlowAGit, + Index: pr1.Index + 3, + }) + assert.NotEmpty(t, pr) + err := pr.LoadIssue(db.DefaultContext) + assert.NoError(t, err) + + _, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr.Index)(t) + require.NoError(t, err) + + assert.Equal(t, "my-shiny-title", pr.Issue.Title) + assert.Contains(t, pr.Issue.Content, "Longer description.") + }) + + t.Run("DescriptionOverride", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + _, _, gitErr := git.NewCommand(git.DefaultContext, "push", "origin", "-o", "description=custom").AddDynamicArguments("HEAD:refs/for/master/" + headBranch + "-implicit-3").RunStdString(&git.RunOpts{Dir: dstPath}) + assert.NoError(t, gitErr) + + unittest.AssertCount(t, &issues_model.PullRequest{}, pullNum+5) + pr := unittest.AssertExistsAndLoadBean(t, &issues_model.PullRequest{ + HeadRepoID: repo.ID, + Flow: issues_model.PullRequestFlowAGit, + Index: pr1.Index + 4, + }) + assert.NotEmpty(t, pr) + err := pr.LoadIssue(db.DefaultContext) + assert.NoError(t, err) + + _, err = doAPIGetPullRequest(*ctx, ctx.Username, ctx.Reponame, pr.Index)(t) + require.NoError(t, err) + + assert.Equal(t, "Testing commit 2", pr.Issue.Title) + assert.Contains(t, pr.Issue.Content, "custom") + }) + }) t.Run("Merge", doAPIMergePullRequest(*ctx, ctx.Username, ctx.Reponame, pr1.Index)) t.Run("CheckoutMasterAgain", doGitCheckoutBranch(dstPath, "master")) } From f7491db1c0d539d0dea63cf6778853e1c389b9fa Mon Sep 17 00:00:00 2001 From: silverwind Date: Sun, 11 Feb 2024 13:55:11 +0100 Subject: [PATCH 037/271] Update JS and PY dependencies (#29127) - Update all excluding `@mcaptcha/vanilla-glue` and `eslint-plugin-array-func` - Remove deprecated and duplicate eslint rule - Tested Monaco, Mermaid and Swagger (cherry picked from commit 4fe37124e9ad5395b734662a7e8ab7b0025c38a3) --- .eslintrc.yaml | 1 - package-lock.json | 243 +++++++++++++++++++++------------------------- package.json | 22 ++--- poetry.lock | 8 +- pyproject.toml | 2 +- 5 files changed, 125 insertions(+), 151 deletions(-) diff --git a/.eslintrc.yaml b/.eslintrc.yaml index fc6f38ec53..ed0309dbea 100644 --- a/.eslintrc.yaml +++ b/.eslintrc.yaml @@ -558,7 +558,6 @@ rules: prefer-rest-params: [2] prefer-spread: [2] prefer-template: [2] - quotes: [2, single, {avoidEscape: true, allowTemplateLiterals: true}] radix: [2, as-needed] regexp/confusing-quantifier: [2] regexp/control-character-escape: [2] diff --git a/package-lock.json b/package-lock.json index 6918dc64b7..62bf36e7b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,22 +30,22 @@ "jquery": "3.7.1", "katex": "0.16.9", "license-checker-webpack-plugin": "0.2.1", - "mermaid": "10.7.0", + "mermaid": "10.8.0", "mini-css-extract-plugin": "2.8.0", "minimatch": "9.0.3", - "monaco-editor": "0.45.0", + "monaco-editor": "0.46.0", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.2.12", "pretty-ms": "9.0.0", "sortablejs": "1.15.2", - "swagger-ui-dist": "5.11.2", + "swagger-ui-dist": "5.11.3", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", "toastify-js": "1.12.0", "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", - "vue": "3.4.15", + "vue": "3.4.18", "vue-bar-graph": "2.0.0", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", @@ -55,11 +55,11 @@ }, "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", - "@playwright/test": "1.41.1", + "@playwright/test": "1.41.2", "@stoplight/spectral-cli": "6.11.0", - "@stylistic/eslint-plugin-js": "1.5.4", + "@stylistic/eslint-plugin-js": "1.6.1", "@stylistic/stylelint-plugin": "2.0.0", - "@vitejs/plugin-vue": "5.0.3", + "@vitejs/plugin-vue": "5.0.4", "eslint": "8.56.0", "eslint-plugin-array-func": "4.0.0", "eslint-plugin-i": "2.29.1", @@ -68,8 +68,8 @@ "eslint-plugin-no-use-extend-native": "0.5.0", "eslint-plugin-regexp": "2.2.0", "eslint-plugin-sonarjs": "0.23.0", - "eslint-plugin-unicorn": "50.0.1", - "eslint-plugin-vitest": "0.3.21", + "eslint-plugin-unicorn": "51.0.1", + "eslint-plugin-vitest": "0.3.22", "eslint-plugin-vitest-globals": "1.4.0", "eslint-plugin-vue": "9.21.1", "eslint-plugin-vue-scoped-css": "2.7.2", @@ -81,8 +81,8 @@ "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.4", "svgo": "3.2.0", - "updates": "15.1.1", - "vite-string-plugin": "1.1.3", + "updates": "15.1.2", + "vite-string-plugin": "1.1.5", "vitest": "1.2.2" }, "engines": { @@ -1370,12 +1370,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.41.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.1.tgz", - "integrity": "sha512-9g8EWTjiQ9yFBXc6HjCWe41msLpxEX0KhmfmPl9RPLJdfzL4F0lg2BdJ91O9azFdl11y1pmpwdjBiSxvqc+btw==", + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.41.2.tgz", + "integrity": "sha512-qQB9h7KbibJzrDpkXkYvsmiDJK14FULCCZgEcoe2AvFAS64oCirWTwzTlAYEbKaRxWs5TFesE1Na6izMv3HfGg==", "dev": true, "dependencies": { - "playwright": "1.41.1" + "playwright": "1.41.2" }, "bin": { "playwright": "cli.js" @@ -2072,9 +2072,9 @@ "dev": true }, "node_modules/@stylistic/eslint-plugin-js": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.5.4.tgz", - "integrity": "sha512-3ctWb3NvJNV1MsrZN91cYp2EGInLPSoZKphXIbIRx/zjZxKwLDr9z4LMOWtqjq14li/OgqUUcMq5pj8fgbLoTw==", + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-js/-/eslint-plugin-js-1.6.1.tgz", + "integrity": "sha512-gHRxkbA5p8S1fnChE7Yf5NFltRZCzbCuQOcoTe93PSKBC4GqVjZmlWUSLz9pJKHvDAUTjWkfttWHIOaFYPEhRQ==", "dev": true, "dependencies": { "acorn": "^8.11.3", @@ -2366,9 +2366,9 @@ "dev": true }, "node_modules/@vitejs/plugin-vue": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.3.tgz", - "integrity": "sha512-b8S5dVS40rgHdDrw+DQi/xOM9ed+kSRZzfm1T74bMmBDCd8XO87NKlFYInzCtwvtWwXZvo1QxE2OSspTATWrbA==", + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-5.0.4.tgz", + "integrity": "sha512-WS3hevEszI6CEVEx28F8RjTX97k3KsrcY6kvTg7+Whm5y3oYvcqzVeGCU3hxSAn4uY2CLCkeokkGKpoctccilQ==", "dev": true, "engines": { "node": "^18.0.0 || >=20.0.0" @@ -2502,46 +2502,46 @@ } }, "node_modules/@vue/compiler-core": { - "version": "3.4.15", - "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.15.tgz", - "integrity": "sha512-XcJQVOaxTKCnth1vCxEChteGuwG6wqnUHxAm1DO3gCz0+uXKaJNx8/digSz4dLALCy8n2lKq24jSUs8segoqIw==", + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.4.18.tgz", + "integrity": "sha512-F7YK8lMK0iv6b9/Gdk15A67wM0KKZvxDxed0RR60C1z9tIJTKta+urs4j0RTN5XqHISzI3etN3mX0uHhjmoqjQ==", "dependencies": { - "@babel/parser": "^7.23.6", - "@vue/shared": "3.4.15", + "@babel/parser": "^7.23.9", + "@vue/shared": "3.4.18", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-dom": { - "version": "3.4.15", - "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.15.tgz", - "integrity": "sha512-wox0aasVV74zoXyblarOM3AZQz/Z+OunYcIHe1OsGclCHt8RsRm04DObjefaI82u6XDzv+qGWZ24tIsRAIi5MQ==", + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.4.18.tgz", + "integrity": "sha512-24Eb8lcMfInefvQ6YlEVS18w5Q66f4+uXWVA+yb7praKbyjHRNuKVWGuinfSSjM0ZIiPi++QWukhkgznBaqpEA==", "dependencies": { - "@vue/compiler-core": "3.4.15", - "@vue/shared": "3.4.15" + "@vue/compiler-core": "3.4.18", + "@vue/shared": "3.4.18" } }, "node_modules/@vue/compiler-sfc": { - "version": "3.4.15", - "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.15.tgz", - "integrity": "sha512-LCn5M6QpkpFsh3GQvs2mJUOAlBQcCco8D60Bcqmf3O3w5a+KWS5GvYbrrJBkgvL1BDnTp+e8q0lXCLgHhKguBA==", + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.4.18.tgz", + "integrity": "sha512-rG5tqtnzwrVpMqAQ7FHtvHaV70G6LLfJIWLYZB/jZ9m/hrnZmIQh+H3ewnC5onwe/ibljm9+ZupxeElzqCkTAw==", "dependencies": { - "@babel/parser": "^7.23.6", - "@vue/compiler-core": "3.4.15", - "@vue/compiler-dom": "3.4.15", - "@vue/compiler-ssr": "3.4.15", - "@vue/shared": "3.4.15", + "@babel/parser": "^7.23.9", + "@vue/compiler-core": "3.4.18", + "@vue/compiler-dom": "3.4.18", + "@vue/compiler-ssr": "3.4.18", + "@vue/shared": "3.4.18", "estree-walker": "^2.0.2", - "magic-string": "^0.30.5", + "magic-string": "^0.30.6", "postcss": "^8.4.33", "source-map-js": "^1.0.2" } }, "node_modules/@vue/compiler-sfc/node_modules/magic-string": { - "version": "0.30.6", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.6.tgz", - "integrity": "sha512-n62qCLbPjNjyo+owKtveQxZFZTBm+Ms6YoGD23Wew6Vw337PElFNifQpknPruVRQV57kVShPnLGo9vWxVhpPvA==", + "version": "0.30.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.7.tgz", + "integrity": "sha512-8vBuFF/I/+OSLRmdf2wwFCJCz+nSn0m6DPvGH1fS/KiQoSaR+sETbov0eIk9KhEKy8CYqIkIAnbohxT/4H0kuA==", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" }, @@ -2550,57 +2550,57 @@ } }, "node_modules/@vue/compiler-ssr": { - "version": "3.4.15", - "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.15.tgz", - "integrity": "sha512-1jdeQyiGznr8gjFDadVmOJqZiLNSsMa5ZgqavkPZ8O2wjHv0tVuAEsw5hTdUoUW4232vpBbL/wJhzVW/JwY1Uw==", + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.4.18.tgz", + "integrity": "sha512-hSlv20oUhPxo2UYUacHgGaxtqP0tvFo6ixxxD6JlXIkwzwoZ9eKK6PFQN4hNK/R13JlNyldwWt/fqGBKgWJ6nQ==", "dependencies": { - "@vue/compiler-dom": "3.4.15", - "@vue/shared": "3.4.15" + "@vue/compiler-dom": "3.4.18", + "@vue/shared": "3.4.18" } }, "node_modules/@vue/reactivity": { - "version": "3.4.15", - "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.15.tgz", - "integrity": "sha512-55yJh2bsff20K5O84MxSvXKPHHt17I2EomHznvFiJCAZpJTNW8IuLj1xZWMLELRhBK3kkFV/1ErZGHJfah7i7w==", + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.4.18.tgz", + "integrity": "sha512-7uda2/I0jpLiRygprDo5Jxs2HJkOVXcOMlyVlY54yRLxoycBpwGJRwJT9EdGB4adnoqJDXVT2BilUAYwI7qvmg==", "dependencies": { - "@vue/shared": "3.4.15" + "@vue/shared": "3.4.18" } }, "node_modules/@vue/runtime-core": { - "version": "3.4.15", - "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.15.tgz", - "integrity": "sha512-6E3by5m6v1AkW0McCeAyhHTw+3y17YCOKG0U0HDKDscV4Hs0kgNT5G+GCHak16jKgcCDHpI9xe5NKb8sdLCLdw==", + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.4.18.tgz", + "integrity": "sha512-7mU9diCa+4e+8/wZ7Udw5pwTH10A11sZ1nldmHOUKJnzCwvZxfJqAtw31mIf4T5H2FsLCSBQT3xgioA9vIjyDQ==", "dependencies": { - "@vue/reactivity": "3.4.15", - "@vue/shared": "3.4.15" + "@vue/reactivity": "3.4.18", + "@vue/shared": "3.4.18" } }, "node_modules/@vue/runtime-dom": { - "version": "3.4.15", - "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.15.tgz", - "integrity": "sha512-EVW8D6vfFVq3V/yDKNPBFkZKGMFSvZrUQmx196o/v2tHKdwWdiZjYUBS+0Ez3+ohRyF8Njwy/6FH5gYJ75liUw==", + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.4.18.tgz", + "integrity": "sha512-2y1Mkzcw1niSfG7z3Qx+2ir9Gb4hdTkZe5p/I8x1aTIKQE0vY0tPAEUPhZm5tx6183gG3D/KwHG728UR0sIufA==", "dependencies": { - "@vue/runtime-core": "3.4.15", - "@vue/shared": "3.4.15", + "@vue/runtime-core": "3.4.18", + "@vue/shared": "3.4.18", "csstype": "^3.1.3" } }, "node_modules/@vue/server-renderer": { - "version": "3.4.15", - "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.15.tgz", - "integrity": "sha512-3HYzaidu9cHjrT+qGUuDhFYvF/j643bHC6uUN9BgM11DVy+pM6ATsG6uPBLnkwOgs7BpJABReLmpL3ZPAsUaqw==", + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.4.18.tgz", + "integrity": "sha512-YJd1wa7mzUN3NRqLEsrwEYWyO+PUBSROIGlCc3J/cvn7Zu6CxhNLgXa8Z4zZ5ja5/nviYO79J1InoPeXgwBTZA==", "dependencies": { - "@vue/compiler-ssr": "3.4.15", - "@vue/shared": "3.4.15" + "@vue/compiler-ssr": "3.4.18", + "@vue/shared": "3.4.18" }, "peerDependencies": { - "vue": "3.4.15" + "vue": "3.4.18" } }, "node_modules/@vue/shared": { - "version": "3.4.15", - "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.15.tgz", - "integrity": "sha512-KzfPTxVaWfB+eGcGdbSf4CWdaXcGDqckoeXUh7SB3fZdEtzPCK2Vq9B/lRRL3yutax/LWITz+SwvgyOxz5V75g==" + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.4.18.tgz", + "integrity": "sha512-CxouGFxxaW5r1WbrSmWwck3No58rApXgRSBxrqgnY1K+jk20F6DrXJkHdH9n4HVT+/B6G2CAn213Uq3npWiy8Q==" }, "node_modules/@webassemblyjs/ast": { "version": "1.11.6", @@ -3766,30 +3766,6 @@ "cytoscape": "^3.2.0" } }, - "node_modules/cytoscape-fcose": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", - "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", - "dependencies": { - "cose-base": "^2.2.0" - }, - "peerDependencies": { - "cytoscape": "^3.2.0" - } - }, - "node_modules/cytoscape-fcose/node_modules/cose-base": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", - "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", - "dependencies": { - "layout-base": "^2.0.0" - } - }, - "node_modules/cytoscape-fcose/node_modules/layout-base": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", - "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==" - }, "node_modules/d3": { "version": "7.8.5", "resolved": "https://registry.npmjs.org/d3/-/d3-7.8.5.tgz", @@ -5012,9 +4988,9 @@ } }, "node_modules/eslint-plugin-unicorn": { - "version": "50.0.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-50.0.1.tgz", - "integrity": "sha512-KxenCZxqSYW0GWHH18okDlOQcpezcitm5aOSz6EnobyJ6BIByiPDviQRjJIUAjG/tMN11958MxaQ+qCoU6lfDA==", + "version": "51.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-51.0.1.tgz", + "integrity": "sha512-MuR/+9VuB0fydoI0nIn2RDA5WISRn4AsJyNSaNKLVwie9/ONvQhxOBbkfSICBPnzKrB77Fh6CZZXjgTt/4Latw==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.22.20", @@ -5045,12 +5021,12 @@ } }, "node_modules/eslint-plugin-vitest": { - "version": "0.3.21", - "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.21.tgz", - "integrity": "sha512-oYwR1MrwaBw/OG6CKU+SJYleAc442w6CWL1RTQl5WLwy8X3sh0bgHIQk5iEtmTak3Q+XAvZglr0bIoDOjFdkcw==", + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/eslint-plugin-vitest/-/eslint-plugin-vitest-0.3.22.tgz", + "integrity": "sha512-atkFGQ7aVgcuSeSMDqnyevIyUpfBPMnosksgEPrKE7Y8xQlqG/5z2IQ6UDau05zXaaFv7Iz8uzqvIuKshjZ0Zw==", "dev": true, "dependencies": { - "@typescript-eslint/utils": "^6.20.0" + "@typescript-eslint/utils": "^6.21.0" }, "engines": { "node": "^18.0.0 || >= 20.0.0" @@ -7341,16 +7317,15 @@ } }, "node_modules/mermaid": { - "version": "10.7.0", - "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.7.0.tgz", - "integrity": "sha512-PsvGupPCkN1vemAAjScyw4pw34p4/0dZkSrqvAB26hUvJulOWGIwt35FZWmT9wPIi4r0QLa5X0PB4YLIGn0/YQ==", + "version": "10.8.0", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.8.0.tgz", + "integrity": "sha512-9CzfSreRjdDJxX796+jW4zjEq0DVw5xVF0nWsqff8OTbrt+ml0TZ5PyYUjjUZJa2NYxYJZZXewEquxGiM8qZEA==", "dependencies": { "@braintree/sanitize-url": "^6.0.1", "@types/d3-scale": "^4.0.3", "@types/d3-scale-chromatic": "^3.0.0", - "cytoscape": "^3.23.0", + "cytoscape": "^3.28.1", "cytoscape-cose-bilkent": "^4.1.0", - "cytoscape-fcose": "^2.1.0", "d3": "^7.4.0", "d3-sankey": "^0.12.3", "dagre-d3-es": "7.0.10", @@ -7904,9 +7879,9 @@ } }, "node_modules/monaco-editor": { - "version": "0.45.0", - "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.45.0.tgz", - "integrity": "sha512-mjv1G1ZzfEE3k9HZN0dQ2olMdwIfaeAAjFiwNprLfYNRSz7ctv9XuCT7gPtBGrMUeV1/iZzYKj17Khu1hxoHOA==" + "version": "0.46.0", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.46.0.tgz", + "integrity": "sha512-ADwtLIIww+9FKybWscd7OCfm9odsFYHImBRI1v9AviGce55QY8raT+9ihH8jX/E/e6QVSGM+pKj4jSUSRmALNQ==" }, "node_modules/monaco-editor-webpack-plugin": { "version": "7.1.0", @@ -8491,12 +8466,12 @@ "dev": true }, "node_modules/playwright": { - "version": "1.41.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.1.tgz", - "integrity": "sha512-gdZAWG97oUnbBdRL3GuBvX3nDDmUOuqzV/D24dytqlKt+eI5KbwusluZRGljx1YoJKZ2NRPaeWiFTeGZO7SosQ==", + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.41.2.tgz", + "integrity": "sha512-v0bOa6H2GJChDL8pAeLa/LZC4feoAMbSQm1/jF/ySsWWoaNItvrMP7GEkvEEFyCTUYKMxjQKaTSg5up7nR6/8A==", "dev": true, "dependencies": { - "playwright-core": "1.41.1" + "playwright-core": "1.41.2" }, "bin": { "playwright": "cli.js" @@ -8509,9 +8484,9 @@ } }, "node_modules/playwright-core": { - "version": "1.41.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.1.tgz", - "integrity": "sha512-/KPO5DzXSMlxSX77wy+HihKGOunh3hqndhqeo/nMxfigiKzogn8kfL0ZBDu0L1RKgan5XHCPmn6zXd2NUJgjhg==", + "version": "1.41.2", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.41.2.tgz", + "integrity": "sha512-VaTvwCA4Y8kxEe+kfm2+uUUw5Lubf38RxF7FpBxLPmGe5sdNkSg5e3ChEigaGrX7qdqT3pt2m/98LiyvU2x6CA==", "dev": true, "bin": { "playwright-core": "cli.js" @@ -10135,9 +10110,9 @@ } }, "node_modules/swagger-ui-dist": { - "version": "5.11.2", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.2.tgz", - "integrity": "sha512-jQG0cRgJNMZ7aCoiFofnoojeSaa/+KgWaDlfgs8QN+BXoGMpxeMVY5OEnjq4OlNvF3yjftO8c9GRAgcHlO+u7A==" + "version": "5.11.3", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.3.tgz", + "integrity": "sha512-vQ+Pe73xt7vMVbX40L6nHu4sDmNCM6A+eMVJPGvKrifHQ4LO3smH0jCiiefKzsVl7OlOcVEnrZ9IFzYwElfMkA==" }, "node_modules/symbol-tree": { "version": "3.2.4", @@ -10620,9 +10595,9 @@ } }, "node_modules/updates": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/updates/-/updates-15.1.1.tgz", - "integrity": "sha512-dMz/4251b0lV7yR58tuydCKaiWxOa18YM8fnRgtiDVzQ5ALopTZhMckv00w0nSMj6OFMFKLshTZGkX4dAebaaw==", + "version": "15.1.2", + "resolved": "https://registry.npmjs.org/updates/-/updates-15.1.2.tgz", + "integrity": "sha512-+/JT4NChl82iexV9G80TY5HF3ubQ5O9UTOk3LlCo4Y4aRCYvo1h4bJE8YkP0PE7KiFRWIQq/rPmUYrY2QF8wVA==", "dev": true, "bin": { "updates": "bin/updates.js" @@ -10795,9 +10770,9 @@ } }, "node_modules/vite-string-plugin": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.1.3.tgz", - "integrity": "sha512-uHL8BV2tBf32T2slYpS0vRzGVrAS3iuivtGknjzyecvpSq2AiBSkyLAjEvvIZuZGDDGFHyGX+5+yc3OBPjWDlA==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/vite-string-plugin/-/vite-string-plugin-1.1.5.tgz", + "integrity": "sha512-KRCIFX3PWVUuEjpi9O7EKLT9E27OqOA3RimIvVx6cziLAUxvnk2VvHQfMrP+mKkqyqqSmnnYyTig3OyDnK/zlA==", "dev": true }, "node_modules/vite/node_modules/@types/estree": { @@ -10931,15 +10906,15 @@ } }, "node_modules/vue": { - "version": "3.4.15", - "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.15.tgz", - "integrity": "sha512-jC0GH4KkWLWJOEQjOpkqU1bQsBwf4R1rsFtw5GQJbjHVKWDzO6P0nWWBTmjp1xSemAioDFj1jdaK1qa3DnMQoQ==", + "version": "3.4.18", + "resolved": "https://registry.npmjs.org/vue/-/vue-3.4.18.tgz", + "integrity": "sha512-0zLRYamFRe0wF4q2L3O24KQzLyLpL64ye1RUToOgOxuWZsb/FhaNRdGmeozdtVYLz6tl94OXLaK7/WQIrVCw1A==", "dependencies": { - "@vue/compiler-dom": "3.4.15", - "@vue/compiler-sfc": "3.4.15", - "@vue/runtime-dom": "3.4.15", - "@vue/server-renderer": "3.4.15", - "@vue/shared": "3.4.15" + "@vue/compiler-dom": "3.4.18", + "@vue/compiler-sfc": "3.4.18", + "@vue/runtime-dom": "3.4.18", + "@vue/server-renderer": "3.4.18", + "@vue/shared": "3.4.18" }, "peerDependencies": { "typescript": "*" diff --git a/package.json b/package.json index ef1fcca545..46dfdd1055 100644 --- a/package.json +++ b/package.json @@ -29,22 +29,22 @@ "jquery": "3.7.1", "katex": "0.16.9", "license-checker-webpack-plugin": "0.2.1", - "mermaid": "10.7.0", + "mermaid": "10.8.0", "mini-css-extract-plugin": "2.8.0", "minimatch": "9.0.3", - "monaco-editor": "0.45.0", + "monaco-editor": "0.46.0", "monaco-editor-webpack-plugin": "7.1.0", "pdfobject": "2.2.12", "pretty-ms": "9.0.0", "sortablejs": "1.15.2", - "swagger-ui-dist": "5.11.2", + "swagger-ui-dist": "5.11.3", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tippy.js": "6.3.7", "toastify-js": "1.12.0", "tributejs": "5.1.3", "uint8-to-base64": "0.2.0", - "vue": "3.4.15", + "vue": "3.4.18", "vue-bar-graph": "2.0.0", "vue-loader": "17.4.2", "vue3-calendar-heatmap": "2.0.5", @@ -54,11 +54,11 @@ }, "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "4.1.0", - "@playwright/test": "1.41.1", + "@playwright/test": "1.41.2", "@stoplight/spectral-cli": "6.11.0", - "@stylistic/eslint-plugin-js": "1.5.4", + "@stylistic/eslint-plugin-js": "1.6.1", "@stylistic/stylelint-plugin": "2.0.0", - "@vitejs/plugin-vue": "5.0.3", + "@vitejs/plugin-vue": "5.0.4", "eslint": "8.56.0", "eslint-plugin-array-func": "4.0.0", "eslint-plugin-i": "2.29.1", @@ -67,8 +67,8 @@ "eslint-plugin-no-use-extend-native": "0.5.0", "eslint-plugin-regexp": "2.2.0", "eslint-plugin-sonarjs": "0.23.0", - "eslint-plugin-unicorn": "50.0.1", - "eslint-plugin-vitest": "0.3.21", + "eslint-plugin-unicorn": "51.0.1", + "eslint-plugin-vitest": "0.3.22", "eslint-plugin-vitest-globals": "1.4.0", "eslint-plugin-vue": "9.21.1", "eslint-plugin-vue-scoped-css": "2.7.2", @@ -80,8 +80,8 @@ "stylelint-declaration-block-no-ignored-properties": "2.8.0", "stylelint-declaration-strict-value": "1.10.4", "svgo": "3.2.0", - "updates": "15.1.1", - "vite-string-plugin": "1.1.3", + "updates": "15.1.2", + "vite-string-plugin": "1.1.5", "vitest": "1.2.2" }, "browserslist": [ diff --git a/poetry.lock b/poetry.lock index 74d202c919..4897496a40 100644 --- a/poetry.lock +++ b/poetry.lock @@ -342,13 +342,13 @@ telegram = ["requests"] [[package]] name = "yamllint" -version = "1.33.0" +version = "1.34.0" description = "A linter for YAML files." optional = false python-versions = ">=3.8" files = [ - {file = "yamllint-1.33.0-py3-none-any.whl", hash = "sha256:28a19f5d68d28d8fec538a1db21bb2d84c7dc2e2ea36266da8d4d1c5a683814d"}, - {file = "yamllint-1.33.0.tar.gz", hash = "sha256:2dceab9ef2d99518a2fcf4ffc964d44250ac4459be1ba3ca315118e4a1a81f7d"}, + {file = "yamllint-1.34.0-py3-none-any.whl", hash = "sha256:33b813f6ff2ffad2e57a288281098392b85f7463ce1f3d5cd45aa848b916a806"}, + {file = "yamllint-1.34.0.tar.gz", hash = "sha256:7f0a6a41e8aab3904878da4ae34b6248b6bc74634e0d3a90f0fb2d7e723a3d4f"}, ] [package.dependencies] @@ -361,4 +361,4 @@ dev = ["doc8", "flake8", "flake8-import-order", "rstcheck[sphinx]", "sphinx"] [metadata] lock-version = "2.0" python-versions = "^3.8" -content-hash = "175c87d138a47ba190a2c3f16b801f694915cc6f2367a358585df9cd1b17ff96" +content-hash = "e4ea4301a70487379fce7008493d15c005af3aada7d88fbf0bd3167147ec6502" diff --git a/pyproject.toml b/pyproject.toml index cf65f0ddfa..8d8d22088e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ python = "^3.8" [tool.poetry.group.dev.dependencies] djlint = "1.34.1" -yamllint = "1.33.0" +yamllint = "1.34.0" [tool.djlint] profile="golang" From b03af9efb275f935bb265c7f031225caaafefaff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Nicas=20Oelschl=C3=A4ger?= <72873130+zokkis@users.noreply.github.com> Date: Sun, 11 Feb 2024 15:10:04 +0100 Subject: [PATCH 038/271] Show more settings for empty repositories (#29130) Shows more settings for empty repositories (Fixes #29060) (cherry picked from commit 28db539d9c0fa0b7c9411724d8b4bf6f371651a0) --- templates/repo/settings/navbar.tmpl | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/templates/repo/settings/navbar.tmpl b/templates/repo/settings/navbar.tmpl index 62f81a901e..df5ba3aba6 100644 --- a/templates/repo/settings/navbar.tmpl +++ b/templates/repo/settings/navbar.tmpl @@ -29,10 +29,12 @@ {{ctx.Locale.Tr "repo.settings.hooks"}} {{end}} - {{if and (.Repository.UnitEnabled $.Context $.UnitTypeCode) (not .Repository.IsEmpty)}} - - {{ctx.Locale.Tr "repo.settings.branches"}} - + {{if .Repository.UnitEnabled $.Context $.UnitTypeCode}} + {{if not .Repository.IsEmpty}} + + {{ctx.Locale.Tr "repo.settings.branches"}} + + {{end}} {{ctx.Locale.Tr "repo.settings.tags"}} From 9a6709869e31128f7e34adcdfd52b4c7b72b2ecf Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Mon, 12 Feb 2024 00:24:21 +0000 Subject: [PATCH 039/271] [skip ci] Updated translations via Crowdin (cherry picked from commit d75708736a2189e7fdbed60444e3bbeef1c5270a) --- options/locale/locale_el-GR.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 1d2efb8427..f71a8ca673 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -844,6 +844,8 @@ oauth2_regenerate_secret=Αναδημιουργία Μυστικού oauth2_regenerate_secret_hint=Χάσατε το μυστικό σας; oauth2_application_edit=Επεξεργασία oauth2_application_create_description=Οι εφαρμογές OAuth2 δίνει πρόσβαση στην εξωτερική εφαρμογή σας σε λογαριασμούς χρηστών σε αυτή την υπηρεσία. +oauth2_application_remove_description=Αφαιρώντας μια εφαρμογή OAuth2 θα αποτραπεί η πρόσβαση αυτής, σε εξουσιοδοτημένους λογαριασμούς χρηστών σε αυτή την υπηρεσία. Συνέχεια; +oauth2_application_locked=Το Gitea κάνει προεγγραφή σε μερικές εφαρμογές OAuth2 κατά την εκκίνηση αν είναι ενεργοποιημένες στις ρυθμίσεις. Για την αποφυγή απροσδόκητης συμπεριφοράς, αυτές δεν μπορούν ούτε να επεξεργαστούν ούτε να καταργηθούν. Παρακαλούμε ανατρέξτε στην τεκμηρίωση OAuth2 για περισσότερες πληροφορίες. authorized_oauth2_applications=Εξουσιοδοτημένες Εφαρμογές OAuth2 revoke_key=Ανάκληση From 1662f50464c36ce2aa18466de21237903d37e4b6 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Tue, 13 Feb 2024 00:24:22 +0000 Subject: [PATCH 040/271] [skip ci] Updated translations via Crowdin (cherry picked from commit 33d939096d93a1014d4961374939376260740cbc) --- options/locale/locale_el-GR.ini | 36 +++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index f71a8ca673..70df36a4a6 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -421,6 +421,7 @@ authorize_title=Εξουσιοδότηση του "%s" για έχει πρόσ authorization_failed=Αποτυχία εξουσιοδότησης authorization_failed_desc=Η εξουσιοδότηση απέτυχε επειδή εντοπίστηκε μια μη έγκυρη αίτηση. Παρακαλούμε επικοινωνήστε με το συντηρητή της εφαρμογής που προσπαθήσατε να εξουσιοδοτήσετε. sspi_auth_failed=Αποτυχία ταυτοποίησης SSPI +password_pwned=Ο κωδικός πρόσβασης που επιλέξατε είναι σε μια λίστα κλεμμένων κωδικών πρόσβασης που προηγουμένως εκτέθηκαν σε παραβίαση δημόσιων δεδομένων. Παρακαλώ δοκιμάστε ξανά με διαφορετικό κωδικό πρόσβασης και σκεφτείτε να αλλάξετε αυτόν τον κωδικό πρόσβασης όπου αλλού χρησιμοποιείται. password_pwned_err=Δεν ήταν δυνατή η ολοκλήρωση του αιτήματος προς το HaveIBeenPwned [mail] @@ -634,6 +635,7 @@ webauthn=Κλειδιά Ασφαλείας public_profile=Δημόσιο Προφίλ biography_placeholder=Πείτε μας λίγο για τον εαυτό σας! (Μπορείτε να γράψετε με Markdown) location_placeholder=Μοιραστείτε την κατά προσέγγιση τοποθεσία σας με άλλους +profile_desc=Ελέγξτε πώς εμφανίζεται το προφίλ σας σε άλλους χρήστες. Η κύρια διεύθυνση email σας θα χρησιμοποιηθεί για ειδοποιήσεις, ανάκτηση κωδικού πρόσβασης και λειτουργίες Git που βασίζονται στο web. password_username_disabled=Οι μη τοπικοί χρήστες δεν επιτρέπεται να αλλάξουν το όνομα χρήστη τους. Επικοινωνήστε με το διαχειριστή σας για περισσότερες λεπτομέρειες. full_name=Πλήρες Όνομα website=Ιστοσελίδα @@ -645,6 +647,7 @@ update_language_not_found=Η γλώσσα "%s" δεν είναι διαθέσι update_language_success=Η γλώσσα ενημερώθηκε. update_profile_success=Το προφίλ σας έχει ενημερωθεί. change_username=Το όνομα χρήστη σας έχει αλλάξει. +change_username_prompt=Σημείωση: Αλλάζοντας το όνομα χρήστη σας αλλάζει επίσης το URL του λογαριασμού σας. change_username_redirect_prompt=Το παλιό όνομα χρήστη θα ανακατευθύνει μέχρι να ζητηθεί ξανά. continue=Συνέχεια cancel=Ακύρωση @@ -723,6 +726,7 @@ add_email_success=Η νέα διεύθυνση email έχει προστεθεί email_preference_set_success=Οι προτιμήσεις email έχουν οριστεί επιτυχώς. add_openid_success=Προστέθηκε η νέα διεύθυνση OpenID. keep_email_private=Απόκρυψη Διεύθυνσης Email +keep_email_private_popup=Αυτό θα κρύψει τη διεύθυνση ηλεκτρονικού ταχυδρομείου σας από το προφίλ σας, καθώς και όταν κάνετε ένα pull request ή επεξεργαστείτε ένα αρχείο χρησιμοποιώντας τη διεπαφή ιστού. Οι ωθούμενες υποβολές δεν θα τροποποιηθούν. Χρησιμοποιήστε το %s στις υποβολές για να τις συσχετίσετε με το λογαριασμό σας. openid_desc=Το OpenID σας επιτρέπει να αναθέσετε τον έλεγχο ταυτότητας σε έναν εξωτερικό πάροχο. manage_ssh_keys=Διαχείριση SSH Κλειδιών @@ -801,7 +805,9 @@ ssh_disabled=SSH Απενεργοποιημένο ssh_signonly=Το SSH είναι απενεργοποιημένο αυτή τη στιγμή, έτσι αυτά τα κλειδιά είναι μόνο για την επαλήθευση υπογραφής των υποβολών. ssh_externally_managed=Αυτό το κλειδί SSH διαχειρίζεται εξωτερικά για αυτόν το χρήστη manage_social=Διαχείριση Συσχετιζόμενων Λογαριασμών Κοινωνικών Δικτύων +social_desc=Αυτοί οι κοινωνικοί λογαριασμοί μπορούν να χρησιμοποιηθούν για να συνδεθείτε στο λογαριασμό σας. Βεβαιωθείτε ότι τους αναγνωρίζετε όλους. unbind=Αποσύνδεση +unbind_success=Ο κοινωνικός λογαριασμός έχει διαγραφεί επιτυχώς. manage_access_token=Διαχείριση Διακριτικών Πρόσβασης generate_new_token=Δημιουργία Νέου Διακριτικού @@ -823,6 +829,7 @@ select_permissions=Επιλέξτε δικαιώματα permission_no_access=Καμία Πρόσβαση permission_read=Αναγνωσμένες permission_write=Ανάγνωση και Εγγραφή +access_token_desc=Τα επιλεγμένα δικαιώματα διακριτικών περιορίζουν την άδεια μόνο στις αντίστοιχες διαδρομές API. Διαβάστε την τεκμηρίωση για περισσότερες πληροφορίες. at_least_one_permission=Πρέπει να επιλέξετε τουλάχιστον ένα δικαίωμα για να δημιουργήσετε ένα διακριτικό permissions_list=Δικαιώματα: @@ -834,6 +841,8 @@ remove_oauth2_application_desc=Η αφαίρεση μιας εφαρμογής O remove_oauth2_application_success=Η εφαρμογή έχει διαγραφεί. create_oauth2_application=Δημιουργία νέας εφαρμογής OAuth2 create_oauth2_application_button=Δημιουργία Εφαρμογής +create_oauth2_application_success=Έχετε δημιουργήσει με επιτυχία μια νέα εφαρμογή OAuth2. +update_oauth2_application_success=Έχετε ενημερώσει με επιτυχία την εφαρμογή OAuth2. oauth2_application_name=Όνομα Εφαρμογής oauth2_confidential_client=Εμπιστευτικός Πελάτης. Επιλέξτε το για εφαρμογές που διατηρούν το μυστικό κωδικό κρυφό, όπως πχ οι εφαρμογές ιστού. Μην επιλέγετε για εγγενείς εφαρμογές, συμπεριλαμβανομένων εφαρμογών επιφάνειας εργασίας και εφαρμογών για κινητά. oauth2_redirect_uris=URI Ανακατεύθυνσης. Χρησιμοποιήστε μια νέα γραμμή για κάθε URI. @@ -842,12 +851,14 @@ oauth2_client_id=Ταυτότητα Πελάτη oauth2_client_secret=Μυστικό Πελάτη oauth2_regenerate_secret=Αναδημιουργία Μυστικού oauth2_regenerate_secret_hint=Χάσατε το μυστικό σας; +oauth2_client_secret_hint=Το μυστικό δε θα εμφανιστεί ξανά αν κλείσετε ή ανανεώσετε αυτή τη σελίδα. Παρακαλώ βεβαιωθείτε ότι το έχετε αποθηκεύσει. oauth2_application_edit=Επεξεργασία oauth2_application_create_description=Οι εφαρμογές OAuth2 δίνει πρόσβαση στην εξωτερική εφαρμογή σας σε λογαριασμούς χρηστών σε αυτή την υπηρεσία. oauth2_application_remove_description=Αφαιρώντας μια εφαρμογή OAuth2 θα αποτραπεί η πρόσβαση αυτής, σε εξουσιοδοτημένους λογαριασμούς χρηστών σε αυτή την υπηρεσία. Συνέχεια; oauth2_application_locked=Το Gitea κάνει προεγγραφή σε μερικές εφαρμογές OAuth2 κατά την εκκίνηση αν είναι ενεργοποιημένες στις ρυθμίσεις. Για την αποφυγή απροσδόκητης συμπεριφοράς, αυτές δεν μπορούν ούτε να επεξεργαστούν ούτε να καταργηθούν. Παρακαλούμε ανατρέξτε στην τεκμηρίωση OAuth2 για περισσότερες πληροφορίες. authorized_oauth2_applications=Εξουσιοδοτημένες Εφαρμογές OAuth2 +authorized_oauth2_applications_description=Έχετε χορηγήσει πρόσβαση στον προσωπικό σας λογαριασμό σε αυτές τις εφαρμογές τρίτων. Ανακαλέστε την πρόσβαση για εφαρμογές που δεν χρειάζεστε πλέον. revoke_key=Ανάκληση revoke_oauth2_grant=Ανάκληση Πρόσβασης revoke_oauth2_grant_description=Η ανάκληση πρόσβασης για αυτή την εξωτερική εφαρμογή θα αποτρέψει αυτή την εφαρμογή από την πρόσβαση στα δεδομένα σας. Σίγουρα; @@ -967,6 +978,8 @@ mirror_interval_invalid=Το χρονικό διάστημα του ειδώλο mirror_sync_on_commit=Συγχρονισμός κατά την ώθηση mirror_address=Κλωνοποίηση Από Το URL mirror_address_desc=Τοποθετήστε όλα τα απαιτούμενα διαπιστευτήρια στην ενότητα Εξουσιοδότηση. +mirror_address_url_invalid=Η διεύθυνση URL που δόθηκε δεν είναι έγκυρη. Πρέπει να κάνετε escape όλα τα στοιχεία του url σωστά. +mirror_address_protocol_invalid=Η παρεχόμενη διεύθυνση URL δεν είναι έγκυρη. Μόνο οι τοποθεσίες http(s):// ή git:// μπορούν να χρησιμοποιηθούν για τη δημιουργία ειδώλου. mirror_lfs=Large File Storage (LFS) mirror_lfs_desc=Ενεργοποίηση αντικατοπτρισμού δεδομένων LFS. mirror_lfs_endpoint=Άκρο LFS @@ -1739,6 +1752,7 @@ pulls.rebase_conflict_summary=Μήνυμα Σφάλματος pulls.unrelated_histories=H Συγχώνευση Απέτυχε: Η κεφαλή και η βάση της συγχώνευσης δεν μοιράζονται μια κοινή ιστορία. Συμβουλή: Δοκιμάστε μια διαφορετική στρατηγική pulls.merge_out_of_date=Η συγχώνευση απέτυχε: Κατά τη δημιουργία της συγχώνευσης, η βάση ενημερώθηκε. Συμβουλή: Δοκιμάστε ξανά. pulls.head_out_of_date=Η συγχώνευση απέτυχε: Κατά τη δημιουργία της συγχώνευσης, το HEAD ενημερώθηκε. Συμβουλή: Δοκιμάστε ξανά. +pulls.has_merged=Αποτυχία: Το pull request έχει συγχωνευθεί, δεν είναι δυνατή η συγχώνευση ξανά ή να αλλάξει ο κλάδος προορισμού. pulls.push_rejected=Η συγχώνευση απέτυχε: Η ώθηση απορρίφθηκε. Ελέγξτε τα Άγκιστρα Git για αυτό το αποθετήριο. pulls.push_rejected_summary=Μήνυμα Πλήρους Απόρριψης pulls.push_rejected_no_message=H Συγχώνευση Aπέτυχε: Η ώθηση απορρίφθηκε, αλλά δεν υπήρχε απομακρυσμένο μήνυμα.
    Ελέγξτε τα Άγκιστρα Git για αυτό το αποθετήριο @@ -1760,7 +1774,11 @@ pulls.outdated_with_base_branch=Αυτός ο κλάδος δεν είναι ε pulls.close=Κλείσιμο Pull Request pulls.closed_at=`έκλεισε αυτό το pull request %[2]s` pulls.reopened_at=`άνοιξε ξανά αυτό το pull request %[2]s` +pulls.cmd_instruction_hint=`Δείτε τις οδηγίες γραμμής εντολών.` +pulls.cmd_instruction_checkout_title=Έλεγχος +pulls.cmd_instruction_checkout_desc=Από το αποθετήριο του έργου σας, ελέγξτε έναν νέο κλάδο και δοκιμάστε τις αλλαγές. pulls.cmd_instruction_merge_title=Συγχώνευση +pulls.cmd_instruction_merge_desc=Συγχώνευση των αλλαγών και ενημέρωση στο Gitea. pulls.clear_merge_message=Εκκαθάριση μηνύματος συγχώνευσης pulls.clear_merge_message_hint=Η εκκαθάριση του μηνύματος συγχώνευσης θα αφαιρέσει μόνο το περιεχόμενο του μηνύματος υποβολής και θα διατηρήσει τα παραγόμενα git trailers όπως "Co-Authored-By …". @@ -1779,6 +1797,7 @@ pulls.auto_merge_canceled_schedule_comment=`ακύρωσε την αυτόματ pulls.delete.title=Διαγραφή αυτού του pull request; pulls.delete.text=Θέλετε πραγματικά να διαγράψετε αυτό το pull request; (Αυτό θα σβήσει οριστικά όλο το περιεχόμενο του. Εξετάστε αν θέλετε να το κλείσετε, αν σκοπεύεται να το αρχειοθετήσετε) +pulls.recently_pushed_new_branches=Ωθήσατε στο κλάδο %[1]s %[2]s pull.deleted_branch=(διαγράφηκε):%s @@ -1812,8 +1831,13 @@ milestones.filter_sort.most_complete=Περισσότερο πλήρη milestones.filter_sort.most_issues=Περισσότερα ζητήματα milestones.filter_sort.least_issues=Λιγότερα ζητήματα +signing.will_sign=Αυτή η υποβολή θα υπογραφεί με το κλειδί "%s". +signing.wont_sign.error=Παρουσιάστηκε σφάλμα κατά τον έλεγχο για το αν η υποβολή μπορεί να υπογραφεί. signing.wont_sign.never=Οι υποβολές δεν υπογράφονται ποτέ. signing.wont_sign.always=Οι υποβολές υπογράφονται πάντα. +signing.wont_sign.parentsigned=Η υποβολή δε θα υπογραφεί καθώς η γονική υποβολή δεν έχει υπογραφεί. +signing.wont_sign.basesigned=Η συγχώνευση δε θα υπογραφεί καθώς η βασική υποβολή δεν έχει υπογραφή της βάσης. +signing.wont_sign.headsigned=Η συγχώνευση δε θα υπογραφεί καθώς δεν έχει υπογραφή η υποβολή της κεφαλής. signing.wont_sign.not_signed_in=Δεν είστε συνδεδεμένοι. ext_wiki=Πρόσβαση στο Εξωτερικό Wiki @@ -1954,6 +1978,7 @@ settings.mirror_settings.last_update=Τελευταία ενημέρωση settings.mirror_settings.push_mirror.none=Δεν έχουν ρυθμιστεί είδωλα ώθησης settings.mirror_settings.push_mirror.remote_url=URL Απομακρυσμένου Αποθετηρίου Git settings.mirror_settings.push_mirror.add=Προσθήκη Είδωλου Push +settings.mirror_settings.push_mirror.edit_sync_time=Επεξεργασία διαστήματος συγχρονισμού ειδώλου settings.sync_mirror=Συγχρονισμός Τώρα settings.site=Ιστοσελίδα @@ -2088,12 +2113,14 @@ settings.webhook_deletion_desc=Η αφαίρεση ενός webhook διαγρά settings.webhook_deletion_success=Το webhook έχει αφαιρεθεί. settings.webhook.test_delivery=Δοκιμή Παράδοσης settings.webhook.test_delivery_desc=Δοκιμάστε αυτό το webhook με ένα ψεύτικο συμβάν. +settings.webhook.test_delivery_desc_disabled=Για να δοκιμάσετε αυτό το webhook με μια ψεύτικη κλήση, ενεργοποιήστε το. settings.webhook.request=Αίτημα settings.webhook.response=Απάντηση settings.webhook.headers=Κεφαλίδες settings.webhook.payload=Περιεχόμενο settings.webhook.body=Σώμα settings.webhook.replay.description=Επανάληψη αυτού του webhook. +settings.webhook.replay.description_disabled=Για να επαναλάβετε αυτό το webhook, ενεργοποιήστε το. settings.webhook.delivery.success=Ένα γεγονός έχει προστεθεί στην ουρά παράδοσης. Μπορεί να χρειαστούν λίγα δευτερόλεπτα μέχρι να εμφανιστεί στο ιστορικό. settings.githooks_desc=Τα Άγκιστρα Git παρέχονται από το ίδιο το Git. Μπορείτε να επεξεργαστείτε τα αρχεία αγκίστρων παρακάτω για να ρυθμίσετε προσαρμοσμένες λειτουργίες. settings.githook_edit_desc=Αν το hook είναι ανενεργό, θα παρουσιαστεί ένα παράδειγμα. Αφήνοντας το περιεχόμενο του hook κενό θα το απενεργοποιήσετε. @@ -2254,6 +2281,7 @@ settings.dismiss_stale_approvals_desc=Όταν οι νέες υποβολές π settings.require_signed_commits=Απαιτούνται Υπογεγραμμένες Υποβολές settings.require_signed_commits_desc=Απόρριψη νέων υποβολών σε αυτόν τον κλάδο εάν είναι μη υπογεγραμμένες ή μη επαληθεύσιμες. settings.protect_branch_name_pattern=Μοτίβο Προστατευμένου Ονόματος Κλάδου +settings.protect_branch_name_pattern_desc=Μοτίβα ονόματος προστατευμένων κλάδων. Δείτε την τεκμηρίωση για σύνταξη μοτίβου. Παραδείγματα: main, release/** settings.protect_patterns=Μοτίβα settings.protect_protected_file_patterns=Μοτίβα προστατευμένων αρχείων (διαχωρισμένα με ερωτηματικό ';'): settings.protect_protected_file_patterns_desc=Τα προστατευόμενα αρχεία δεν επιτρέπεται να αλλάξουν άμεσα, ακόμη και αν ο χρήστης έχει δικαιώματα να προσθέσει, να επεξεργαστεί ή να διαγράψει αρχεία σε αυτόν τον κλάδο. Επιπλέων μοτίβα μπορούν να διαχωριστούν με ερωτηματικό (';'). Δείτε την τεκμηρίωση github.com/gobwas/glob για τη σύνταξη του μοτίβου. Πχ: .drone.yml, /docs/**/*.txt. @@ -2290,6 +2318,7 @@ settings.tags.protection.allowed.teams=Επιτρεπόμενες ομάδες settings.tags.protection.allowed.noone=Καμία settings.tags.protection.create=Προστασία Ετικέτας settings.tags.protection.none=Δεν υπάρχουν προστατευμένες ετικέτες. +settings.tags.protection.pattern.description=Μπορείτε να χρησιμοποιήσετε ένα μόνο όνομα ή ένα μοτίβο τύπου glob ή κανονική έκφραση για να ταιριάξετε πολλαπλές ετικέτες. Διαβάστε περισσότερα στον οδηγό προστατευμένων ετικετών. settings.bot_token=Διακριτικό Bot settings.chat_id=ID Συνομιλίας settings.thread_id=ID Νήματος @@ -2306,6 +2335,8 @@ settings.archive.branchsettings_unavailable=Οι ρυθμίσεις του κλ settings.archive.tagsettings_unavailable=Οι ρυθμίσεις της ετικέτας δεν είναι διαθέσιμες αν το αποθετήριο είναι αρχειοθετημένο. settings.unarchive.button=Απο-Αρχειοθέτηση αποθετηρίου settings.unarchive.header=Απο-Αρχειοθέτηση του αποθετηρίου +settings.unarchive.text=Η απο-αρχειοθέτηση του αποθετηρίου θα αποκαταστήσει την ικανότητά του να λαμβάνει υποβολές και ωθήσεις, καθώς και νέα ζητήματα και pull-requests. +settings.unarchive.success=Το αποθετήριο απο-αρχειοθετήθηκε με επιτυχία. settings.update_avatar_success=Η εικόνα του αποθετηρίου έχει ενημερωθεί. settings.lfs=LFS settings.lfs_filelist=Αρχεία LFS σε αυτό το αποθετήριο @@ -2488,6 +2519,7 @@ tag.create_success=Η ετικέτα "%s" δημιουργήθηκε. topic.manage_topics=Διαχείριση Θεμάτων topic.done=Ολοκληρώθηκε topic.count_prompt=Δεν μπορείτε να επιλέξετε περισσότερα από 25 θέματα +topic.format_prompt=Τα θέματα πρέπει να ξεκινούν με γράμμα ή αριθμό, μπορούν να περιλαμβάνουν παύλες ('-') και τελείες ('.'), μπορεί να είναι μέχρι 35 χαρακτήρες. Τα γράμματα πρέπει να είναι πεζά. find_file.go_to_file=Αναζήτηση αρχείου find_file.no_matching=Δεν ταιριάζει κανένα αρχείο @@ -2650,11 +2682,13 @@ dashboard.clean_unbind_oauth=Εκκαθάριση μη δεσμευμένων σ dashboard.clean_unbind_oauth_success=Όλες οι μη δεσμευμένες συνδέσεις OAuth διαγράφηκαν. dashboard.task.started=Εκκίνηση Εργασίας: %[1]s dashboard.task.process=Εργασία: %[1]s +dashboard.task.cancelled=Εργασία: %[1]ακυρώθηκε: %[3]s dashboard.task.error=Σφάλμα στην Εργασία: %[1]s: %[3]s dashboard.task.finished=Εργασία: %[1]s που εκκινήθηκε από %[2]s τελείωσε dashboard.task.unknown=Άγνωστη εργασία: %[1]s dashboard.cron.started=Εκκίνηση Προγραμματισμένης Εργασίας: %[1]s dashboard.cron.process=Προγραμματισμένη Εργασία: %[1]s +dashboard.cron.cancelled=Προγραμματισμένη εργασία: %[1]s ακυρώθηκε: %[3]s dashboard.cron.error=Σφάλμα στη Προγραμματισμένη Εργασία: %s: %[3]s dashboard.cron.finished=Προγραμματισμένη Εργασία: %[1]s τελείωσε dashboard.delete_inactive_accounts=Διαγραφή όλων των μη ενεργοποιημένων λογαριασμών @@ -2664,6 +2698,7 @@ dashboard.delete_repo_archives.started=Η διαγραφή όλων των αρ dashboard.delete_missing_repos=Διαγραφή όλων των αποθετηρίων που δεν έχουν τα αρχεία Git τους dashboard.delete_missing_repos.started=Η διαγραφή όλων των αποθετηρίων που δεν έχουν αρχεία Git τους, ξεκίνησε. dashboard.delete_generated_repository_avatars=Διαγραφή δημιουργημένων εικόνων αποθετηρίων +dashboard.sync_repo_branches=Συγχρονισμός κλάδων που λείπουν, από τα δεδομένα git στις βάσεις δεδομένων dashboard.update_mirrors=Ενημέρωση Ειδώλων dashboard.repo_health_check=Έλεγχος υγείας σε όλα τα αποθετήρια dashboard.check_repo_stats=Έλεγχος όλων των στατιστικών αποθετηρίων @@ -2716,6 +2751,7 @@ dashboard.stop_zombie_tasks=Διακοπή εργασιών ζόμπι dashboard.stop_endless_tasks=Διακοπή ατελείωτων εργασιών dashboard.cancel_abandoned_jobs=Ακύρωση εγκαταλελειμμένων εργασιών dashboard.start_schedule_tasks=Έναρξη προγραμματισμένων εργασιών +dashboard.sync_branch.started=Ο Συγχρονισμός των Κλάδων ξεκίνησε dashboard.rebuild_issue_indexer=Αναδόμηση ευρετηρίου ζητημάτων users.user_manage_panel=Διαχείριση Λογαριασμών Χρηστών From 27bc2b9d9597de89d2c6b68581c6729bb16a4572 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Nicas=20Oelschl=C3=A4ger?= <72873130+zokkis@users.noreply.github.com> Date: Tue, 13 Feb 2024 09:07:59 +0100 Subject: [PATCH 041/271] Show `View at this point in history` for every commit (#29122) Shows the 'View at this point in history'-link (from #27354) for every commit before: ![image](https://github.com/go-gitea/gitea/assets/72873130/0e5cd763-e099-4bb4-9519-653fe21f85a6) after: ![image](https://github.com/go-gitea/gitea/assets/72873130/2b57346f-51e3-4901-b85e-63a690878939) (cherry picked from commit b85e4a64fa26e1f20321c3a7cedf9fa05640ca48) --- templates/repo/commits_list.tmpl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/templates/repo/commits_list.tmpl b/templates/repo/commits_list.tmpl index 7702770c40..4eb31e0e8e 100644 --- a/templates/repo/commits_list.tmpl +++ b/templates/repo/commits_list.tmpl @@ -78,9 +78,12 @@ {{end}} - {{if $.FileName}} - {{svg "octicon-file-code"}} - {{end}} + + {{svg "octicon-file-code"}} + {{end}} From e9e6c6152e8480001119ef69d0ec13b8fdeae7f4 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Tue, 13 Feb 2024 09:45:31 +0100 Subject: [PATCH 042/271] Dont load Review if Comment is CommentTypeReviewRequest (#28551) RequestReview get deleted on review. So we don't have to try to load them on comments. broken out #28544 (cherry picked from commit 6fad2c874438275d3f69bb1cc223708bd2d27ff6) --- models/issues/comment.go | 7 +++++++ models/issues/comment_list.go | 3 ++- models/issues/review.go | 3 +++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/models/issues/comment.go b/models/issues/comment.go index d9acca7e48..5631b94a50 100644 --- a/models/issues/comment.go +++ b/models/issues/comment.go @@ -695,8 +695,15 @@ func (c *Comment) LoadReactions(ctx context.Context, repo *repo_model.Repository } func (c *Comment) loadReview(ctx context.Context) (err error) { + if c.ReviewID == 0 { + return nil + } if c.Review == nil { if c.Review, err = GetReviewByID(ctx, c.ReviewID); err != nil { + // review request which has been replaced by actual reviews doesn't exist in database anymore, so ignorem them. + if c.Type == CommentTypeReviewRequest { + return nil + } return err } } diff --git a/models/issues/comment_list.go b/models/issues/comment_list.go index 93af45870e..cb7df3270d 100644 --- a/models/issues/comment_list.go +++ b/models/issues/comment_list.go @@ -430,7 +430,8 @@ func (comments CommentList) loadReviews(ctx context.Context) error { for _, comment := range comments { comment.Review = reviews[comment.ReviewID] if comment.Review == nil { - if comment.ReviewID > 0 { + // review request which has been replaced by actual reviews doesn't exist in database anymore, so don't log errors for them. + if comment.ReviewID > 0 && comment.Type != CommentTypeReviewRequest { log.Error("comment with review id [%d] but has no review record", comment.ReviewID) } continue diff --git a/models/issues/review.go b/models/issues/review.go index f2022ae0aa..ba4e02f765 100644 --- a/models/issues/review.go +++ b/models/issues/review.go @@ -621,6 +621,9 @@ func AddReviewRequest(ctx context.Context, issue *Issue, reviewer, doer *user_mo return nil, err } + // func caller use the created comment to retrieve created review too. + comment.Review = review + return comment, committer.Commit() } From b1aabbf174704d8ca27664a4ff133060f8c71ea9 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 14 Feb 2024 23:01:35 +0100 Subject: [PATCH 043/271] Preview: set font-size on preview content - When previewing the content in a review, no font size was set. This resulted in the previewed content being bigger than other text and therefor creating an noticable inconsistency. - Set the font size of the previewed content, 14px, this is consistent with how the content would be rendered. - `comment-code-cloud` is the class used for the review boxes. `.ui.tab.markup` means it only applies to the preview tab. --- web_src/css/review.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/web_src/css/review.css b/web_src/css/review.css index 5336775547..62da25b209 100644 --- a/web_src/css/review.css +++ b/web_src/css/review.css @@ -153,6 +153,10 @@ min-height: 168px; } +.comment-code-cloud .ui.tab.markup { + font-size: 14px; +} + .comment-code-cloud .ui.tabular.menu { margin: 0.5em; } From 761641cf4aab3d721d2a96bca41fc228b1379899 Mon Sep 17 00:00:00 2001 From: Argo Date: Thu, 15 Feb 2024 11:19:08 +0000 Subject: [PATCH 044/271] Add Zig gitignore --- options/gitignore/Zig | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 options/gitignore/Zig diff --git a/options/gitignore/Zig b/options/gitignore/Zig new file mode 100644 index 0000000000..236ae6be8c --- /dev/null +++ b/options/gitignore/Zig @@ -0,0 +1,5 @@ +zig-cache/ +zig-out/ +build/ +build-*/ +docgen_tmp/ \ No newline at end of file From 79df4092567ac599e7b34b8aea93de830df00144 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Thu, 15 Feb 2024 16:19:28 +0100 Subject: [PATCH 045/271] [CI] upgrade to golangci-lint@v1.56.1 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index ce854272bc..5b1ea0da40 100644 --- a/Makefile +++ b/Makefile @@ -29,7 +29,7 @@ XGO_VERSION := go-1.21.x AIR_PACKAGE ?= github.com/cosmtrek/air@v1.49.0 EDITORCONFIG_CHECKER_PACKAGE ?= github.com/editorconfig-checker/editorconfig-checker/cmd/editorconfig-checker@2.7.0 GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0 -GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.55.2 +GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.1 GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1 SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.5 From c8b177713a44d14825b9e7f15b5333264ddbb5c5 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Thu, 15 Feb 2024 16:19:36 +0100 Subject: [PATCH 046/271] [CLEANUP] make golangci-lint@v1.56.1 happy --- models/issues/issue_xref.go | 6 +++--- models/issues/review_list.go | 12 +++++------ modules/git/log_name_status.go | 14 ++++++------- modules/repository/generate.go | 2 +- routers/web/org/projects.go | 8 ++++---- routers/web/repo/projects.go | 8 ++++---- routers/web/user/home.go | 8 ++++---- services/repository/create_test.go | 32 +++++++++++++++--------------- 8 files changed, 45 insertions(+), 45 deletions(-) diff --git a/models/issues/issue_xref.go b/models/issues/issue_xref.go index 9b1a447471..9c9d5d66cd 100644 --- a/models/issues/issue_xref.go +++ b/models/issues/issue_xref.go @@ -46,10 +46,10 @@ func neuterCrossReferences(ctx context.Context, issueID, commentID int64) error for i, c := range active { ids[i] = c.ID } - return neuterCrossReferencesIds(ctx, nil, ids) + return neuterCrossReferencesIDs(ctx, nil, ids) } -func neuterCrossReferencesIds(stdCtx context.Context, ctx *crossReferencesContext, ids []int64) error { +func neuterCrossReferencesIDs(stdCtx context.Context, ctx *crossReferencesContext, ids []int64) error { sess := db.GetEngine(stdCtx).In("id", ids).Cols("`ref_action`") if ctx != nil && ctx.OrigIssue.NoAutoTime { sess.SetExpr("updated_unix", ctx.OrigIssue.UpdatedUnix).NoAutoTime() @@ -104,7 +104,7 @@ func (issue *Issue) createCrossReferences(stdCtx context.Context, ctx *crossRefe } } if len(ids) > 0 { - if err = neuterCrossReferencesIds(stdCtx, ctx, ids); err != nil { + if err = neuterCrossReferencesIDs(stdCtx, ctx, ids); err != nil { return err } } diff --git a/models/issues/review_list.go b/models/issues/review_list.go index ed3d0bd028..282f18b4f7 100644 --- a/models/issues/review_list.go +++ b/models/issues/review_list.go @@ -18,11 +18,11 @@ type ReviewList []*Review // LoadReviewers loads reviewers func (reviews ReviewList) LoadReviewers(ctx context.Context) error { - reviewerIds := make([]int64, len(reviews)) + reviewerIDs := make([]int64, len(reviews)) for i := 0; i < len(reviews); i++ { - reviewerIds[i] = reviews[i].ReviewerID + reviewerIDs[i] = reviews[i].ReviewerID } - reviewers, err := user_model.GetPossibleUserByIDs(ctx, reviewerIds) + reviewers, err := user_model.GetPossibleUserByIDs(ctx, reviewerIDs) if err != nil { return err } @@ -38,12 +38,12 @@ func (reviews ReviewList) LoadReviewers(ctx context.Context) error { } func (reviews ReviewList) LoadIssues(ctx context.Context) error { - issueIds := container.Set[int64]{} + issueIDs := container.Set[int64]{} for i := 0; i < len(reviews); i++ { - issueIds.Add(reviews[i].IssueID) + issueIDs.Add(reviews[i].IssueID) } - issues, err := GetIssuesByIDs(ctx, issueIds.Values()) + issues, err := GetIssuesByIDs(ctx, issueIDs.Values()) if err != nil { return err } diff --git a/modules/git/log_name_status.go b/modules/git/log_name_status.go index 26a0d28098..9e345f3ee0 100644 --- a/modules/git/log_name_status.go +++ b/modules/git/log_name_status.go @@ -143,19 +143,19 @@ func (g *LogNameStatusRepoParser) Next(treepath string, paths2ids map[string]int } // Our "line" must look like: SP ( SP) * NUL - commitIds := string(g.next) + commitIDs := string(g.next) if g.buffull { more, err := g.rd.ReadString('\x00') if err != nil { return nil, err } - commitIds += more + commitIDs += more } - commitIds = commitIds[:len(commitIds)-1] - splitIds := strings.Split(commitIds, " ") - ret.CommitID = splitIds[0] - if len(splitIds) > 1 { - ret.ParentIDs = splitIds[1:] + commitIDs = commitIDs[:len(commitIDs)-1] + splitIDs := strings.Split(commitIDs, " ") + ret.CommitID = splitIDs[0] + if len(splitIDs) > 1 { + ret.ParentIDs = splitIDs[1:] } // now read the next "line" diff --git a/modules/repository/generate.go b/modules/repository/generate.go index 013dd8f76f..f622383bb5 100644 --- a/modules/repository/generate.go +++ b/modules/repository/generate.go @@ -94,7 +94,7 @@ type GiteaTemplate struct { } // Globs parses the .gitea/template globs or returns them if they were already parsed -func (gt GiteaTemplate) Globs() []glob.Glob { +func (gt *GiteaTemplate) Globs() []glob.Glob { if gt.globs != nil { return gt.globs } diff --git a/routers/web/org/projects.go b/routers/web/org/projects.go index 03798a712c..f65cc6e679 100644 --- a/routers/web/org/projects.go +++ b/routers/web/org/projects.go @@ -377,16 +377,16 @@ func ViewProject(ctx *context.Context) { linkedPrsMap := make(map[int64][]*issues_model.Issue) for _, issuesList := range issuesMap { for _, issue := range issuesList { - var referencedIds []int64 + var referencedIDs []int64 for _, comment := range issue.Comments { if comment.RefIssueID != 0 && comment.RefIsPull { - referencedIds = append(referencedIds, comment.RefIssueID) + referencedIDs = append(referencedIDs, comment.RefIssueID) } } - if len(referencedIds) > 0 { + if len(referencedIDs) > 0 { if linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{ - IssueIDs: referencedIds, + IssueIDs: referencedIDs, IsPull: util.OptionalBoolTrue, }); err == nil { linkedPrsMap[issue.ID] = linkedPrs diff --git a/routers/web/repo/projects.go b/routers/web/repo/projects.go index 4908bb796d..001f0752c3 100644 --- a/routers/web/repo/projects.go +++ b/routers/web/repo/projects.go @@ -339,16 +339,16 @@ func ViewProject(ctx *context.Context) { linkedPrsMap := make(map[int64][]*issues_model.Issue) for _, issuesList := range issuesMap { for _, issue := range issuesList { - var referencedIds []int64 + var referencedIDs []int64 for _, comment := range issue.Comments { if comment.RefIssueID != 0 && comment.RefIsPull { - referencedIds = append(referencedIds, comment.RefIssueID) + referencedIDs = append(referencedIDs, comment.RefIssueID) } } - if len(referencedIds) > 0 { + if len(referencedIDs) > 0 { if linkedPrs, err := issues_model.Issues(ctx, &issues_model.IssuesOptions{ - IssueIDs: referencedIds, + IssueIDs: referencedIDs, IsPull: util.OptionalBoolTrue, }); err == nil { linkedPrsMap[issue.ID] = linkedPrs diff --git a/routers/web/user/home.go b/routers/web/user/home.go index 79af74eb02..115c995e21 100644 --- a/routers/web/user/home.go +++ b/routers/web/user/home.go @@ -296,17 +296,17 @@ func Milestones(ctx *context.Context) { } } - showRepoIds := make(container.Set[int64], len(showRepos)) + showRepoIDs := make(container.Set[int64], len(showRepos)) for _, repo := range showRepos { if repo.ID > 0 { - showRepoIds.Add(repo.ID) + showRepoIDs.Add(repo.ID) } } if len(repoIDs) == 0 { - repoIDs = showRepoIds.Values() + repoIDs = showRepoIDs.Values() } repoIDs = slices.DeleteFunc(repoIDs, func(v int64) bool { - return !showRepoIds.Contains(v) + return !showRepoIDs.Contains(v) }) var pagerCount int diff --git a/services/repository/create_test.go b/services/repository/create_test.go index b3e1f0550c..131249ad9c 100644 --- a/services/repository/create_test.go +++ b/services/repository/create_test.go @@ -52,12 +52,12 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { assert.True(t, ownerTeam.IncludesAllRepositories, "Owner team includes all repositories") // Create repos. - repoIds := make([]int64, 0) + repoIDs := make([]int64, 0) for i := 0; i < 3; i++ { r, err := CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), CreateRepoOptions{Name: fmt.Sprintf("repo-%d", i)}) assert.NoError(t, err, "CreateRepository %d", i) if r != nil { - repoIds = append(repoIds, r.ID) + repoIDs = append(repoIDs, r.ID) } } // Get fresh copy of Owner team after creating repos. @@ -93,10 +93,10 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { }, } teamRepos := [][]int64{ - repoIds, - repoIds, + repoIDs, + repoIDs, {}, - repoIds, + repoIDs, {}, } for i, team := range teams { @@ -109,7 +109,7 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { // Update teams and check repositories. teams[3].IncludesAllRepositories = false teams[4].IncludesAllRepositories = true - teamRepos[4] = repoIds + teamRepos[4] = repoIDs for i, team := range teams { assert.NoError(t, models.UpdateTeam(db.DefaultContext, team, false, true), "%s: UpdateTeam", team.Name) testTeamRepositories(team.ID, teamRepos[i]) @@ -119,27 +119,27 @@ func TestIncludesAllRepositoriesTeams(t *testing.T) { r, err := CreateRepositoryDirectly(db.DefaultContext, user, org.AsUser(), CreateRepoOptions{Name: "repo-last"}) assert.NoError(t, err, "CreateRepository last") if r != nil { - repoIds = append(repoIds, r.ID) + repoIDs = append(repoIDs, r.ID) } - teamRepos[0] = repoIds - teamRepos[1] = repoIds - teamRepos[4] = repoIds + teamRepos[0] = repoIDs + teamRepos[1] = repoIDs + teamRepos[4] = repoIDs for i, team := range teams { testTeamRepositories(team.ID, teamRepos[i]) } // Remove repo and check teams repositories. - assert.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, repoIds[0]), "DeleteRepository") - teamRepos[0] = repoIds[1:] - teamRepos[1] = repoIds[1:] - teamRepos[3] = repoIds[1:3] - teamRepos[4] = repoIds[1:] + assert.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, repoIDs[0]), "DeleteRepository") + teamRepos[0] = repoIDs[1:] + teamRepos[1] = repoIDs[1:] + teamRepos[3] = repoIDs[1:3] + teamRepos[4] = repoIDs[1:] for i, team := range teams { testTeamRepositories(team.ID, teamRepos[i]) } // Wipe created items. - for i, rid := range repoIds { + for i, rid := range repoIDs { if i > 0 { // first repo already deleted. assert.NoError(t, DeleteRepositoryDirectly(db.DefaultContext, user, rid), "DeleteRepository %d", i) } From 0fc61c88361c93a55150b999211e8b9e226e6b86 Mon Sep 17 00:00:00 2001 From: oliverpool Date: Fri, 16 Feb 2024 12:16:11 +0000 Subject: [PATCH 047/271] [BUG] split code conversations in diff tab (#2306) Follow-up of #2282 and #2296 (which tried to address #2278) One of the issue with the previous PR is that when a conversation on the Files tab was marked as "resolved", it would fetch all the comments for that line (even the outdated ones, which should not be shown on this page - except when explicitly activated). To properly fix this, I have changed `FetchCodeCommentsByLine` to `FetchCodeConversation`. Its role is to fetch all comments related to a given (review, path, line) and reverted my changes in the template (which were based on a misunderstanding). Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2306 Reviewed-by: Earl Warren Reviewed-by: Gusted Co-authored-by: oliverpool Co-committed-by: oliverpool --- models/issues/comment_code.go | 74 ++++- models/issues/comment_test.go | 8 +- routers/web/repo/pull_review.go | 2 +- services/gitdiff/gitdiff.go | 34 +-- services/gitdiff/gitdiff_test.go | 12 +- services/repository/files/diff_test.go | 66 ++--- templates/repo/diff/conversations.tmpl | 3 + templates/repo/diff/section_split.tmpl | 24 +- templates/repo/diff/section_unified.tmpl | 4 +- .../repo/issue/view_content/conversation.tmpl | 9 +- tests/integration/pull_review_test.go | 256 ++++++++++++++---- 11 files changed, 348 insertions(+), 144 deletions(-) create mode 100644 templates/repo/diff/conversations.tmpl diff --git a/models/issues/comment_code.go b/models/issues/comment_code.go index 384a595dd9..cef2ea281c 100644 --- a/models/issues/comment_code.go +++ b/models/issues/comment_code.go @@ -14,15 +14,58 @@ import ( "xorm.io/builder" ) +// CodeConversation contains the comment of a given review +type CodeConversation []*Comment + +// CodeConversationsAtLine contains the conversations for a given line +type CodeConversationsAtLine map[int64][]CodeConversation + +// CodeConversationsAtLineAndTreePath contains the conversations for a given TreePath and line +type CodeConversationsAtLineAndTreePath map[string]CodeConversationsAtLine + +func newCodeConversationsAtLineAndTreePath(comments []*Comment) CodeConversationsAtLineAndTreePath { + tree := make(CodeConversationsAtLineAndTreePath) + for _, comment := range comments { + tree.insertComment(comment) + } + return tree +} + +func (tree CodeConversationsAtLineAndTreePath) insertComment(comment *Comment) { + // attempt to append comment to existing conversations (i.e. list of comments belonging to the same review) + for i, conversation := range tree[comment.TreePath][comment.Line] { + if conversation[0].ReviewID == comment.ReviewID { + tree[comment.TreePath][comment.Line][i] = append(conversation, comment) + return + } + } + + // no previous conversation was found at this line, create it + if tree[comment.TreePath] == nil { + tree[comment.TreePath] = make(map[int64][]CodeConversation) + } + + tree[comment.TreePath][comment.Line] = append(tree[comment.TreePath][comment.Line], CodeConversation{comment}) +} + +// FetchCodeConversations will return a 2d-map: ["Path"]["Line"] = List of CodeConversation (one per review) for this line +func FetchCodeConversations(ctx context.Context, issue *Issue, doer *user_model.User, showOutdatedComments bool) (CodeConversationsAtLineAndTreePath, error) { + opts := FindCommentsOptions{ + Type: CommentTypeCode, + IssueID: issue.ID, + } + comments, err := findCodeComments(ctx, opts, issue, doer, nil, showOutdatedComments) + if err != nil { + return nil, err + } + + return newCodeConversationsAtLineAndTreePath(comments), nil +} + // CodeComments represents comments on code by using this structure: FILENAME -> LINE (+ == proposed; - == previous) -> COMMENTS type CodeComments map[string]map[int64][]*Comment -// FetchCodeComments will return a 2d-map: ["Path"]["Line"] = Comments at line -func FetchCodeComments(ctx context.Context, issue *Issue, currentUser *user_model.User, showOutdatedComments bool) (CodeComments, error) { - return fetchCodeCommentsByReview(ctx, issue, currentUser, nil, showOutdatedComments) -} - -func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *user_model.User, review *Review, showOutdatedComments bool) (CodeComments, error) { +func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, doer *user_model.User, review *Review, showOutdatedComments bool) (CodeComments, error) { pathToLineToComment := make(CodeComments) if review == nil { review = &Review{ID: 0} @@ -33,7 +76,7 @@ func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *u ReviewID: review.ID, } - comments, err := findCodeComments(ctx, opts, issue, currentUser, review, showOutdatedComments) + comments, err := findCodeComments(ctx, opts, issue, doer, review, showOutdatedComments) if err != nil { return nil, err } @@ -47,7 +90,7 @@ func fetchCodeCommentsByReview(ctx context.Context, issue *Issue, currentUser *u return pathToLineToComment, nil } -func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, currentUser *user_model.User, review *Review, showOutdatedComments bool) ([]*Comment, error) { +func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issue, doer *user_model.User, review *Review, showOutdatedComments bool) ([]*Comment, error) { var comments CommentList if review == nil { review = &Review{ID: 0} @@ -91,7 +134,7 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu if re, ok := reviews[comment.ReviewID]; ok && re != nil { // If the review is pending only the author can see the comments (except if the review is set) if review.ID == 0 && re.Type == ReviewTypePending && - (currentUser == nil || currentUser.ID != re.ReviewerID) { + (doer == nil || doer.ID != re.ReviewerID) { continue } comment.Review = re @@ -121,13 +164,14 @@ func findCodeComments(ctx context.Context, opts FindCommentsOptions, issue *Issu return comments[:n], nil } -// FetchCodeCommentsByLine fetches the code comments for a given treePath and line number -func FetchCodeCommentsByLine(ctx context.Context, issue *Issue, currentUser *user_model.User, treePath string, line int64, showOutdatedComments bool) ([]*Comment, error) { +// FetchCodeConversation fetches the code conversation of a given comment (same review, treePath and line number) +func FetchCodeConversation(ctx context.Context, comment *Comment, doer *user_model.User) ([]*Comment, error) { opts := FindCommentsOptions{ Type: CommentTypeCode, - IssueID: issue.ID, - TreePath: treePath, - Line: line, + IssueID: comment.IssueID, + ReviewID: comment.ReviewID, + TreePath: comment.TreePath, + Line: comment.Line, } - return findCodeComments(ctx, opts, issue, currentUser, nil, showOutdatedComments) + return findCodeComments(ctx, opts, comment.Issue, doer, nil, true) } diff --git a/models/issues/comment_test.go b/models/issues/comment_test.go index e08bd7fbf5..e7ceee4298 100644 --- a/models/issues/comment_test.go +++ b/models/issues/comment_test.go @@ -46,20 +46,20 @@ func TestCreateComment(t *testing.T) { unittest.AssertInt64InRange(t, now, then, int64(updatedIssue.UpdatedUnix)) } -func TestFetchCodeComments(t *testing.T) { +func TestFetchCodeConversations(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) issue := unittest.AssertExistsAndLoadBean(t, &issues_model.Issue{ID: 2}) user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) - res, err := issues_model.FetchCodeComments(db.DefaultContext, issue, user, false) + res, err := issues_model.FetchCodeConversations(db.DefaultContext, issue, user, false) assert.NoError(t, err) assert.Contains(t, res, "README.md") assert.Contains(t, res["README.md"], int64(4)) assert.Len(t, res["README.md"][4], 1) - assert.Equal(t, int64(4), res["README.md"][4][0].ID) + assert.Equal(t, int64(4), res["README.md"][4][0][0].ID) user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) - res, err = issues_model.FetchCodeComments(db.DefaultContext, issue, user2, false) + res, err = issues_model.FetchCodeConversations(db.DefaultContext, issue, user2, false) assert.NoError(t, err) assert.Len(t, res, 1) } diff --git a/routers/web/repo/pull_review.go b/routers/web/repo/pull_review.go index e399176a4a..a5e0c69471 100644 --- a/routers/web/repo/pull_review.go +++ b/routers/web/repo/pull_review.go @@ -153,7 +153,7 @@ func UpdateResolveConversation(ctx *context.Context) { } func renderConversation(ctx *context.Context, comment *issues_model.Comment, origin string) { - comments, err := issues_model.FetchCodeCommentsByLine(ctx, comment.Issue, ctx.Doer, comment.TreePath, comment.Line, true) + comments, err := issues_model.FetchCodeConversation(ctx, comment, ctx.Doer) if err != nil { ctx.ServerError("FetchCodeCommentsByLine", err) return diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 0f6e2b6c17..241d849c9f 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -13,7 +13,6 @@ import ( "html/template" "io" "net/url" - "sort" "strings" "time" @@ -75,13 +74,13 @@ const ( // DiffLine represents a line difference in a DiffSection. type DiffLine struct { - LeftIdx int - RightIdx int - Match int - Type DiffLineType - Content string - Comments []*issues_model.Comment - SectionInfo *DiffLineSectionInfo + LeftIdx int + RightIdx int + Match int + Type DiffLineType + Content string + Conversations []issues_model.CodeConversation + SectionInfo *DiffLineSectionInfo } // DiffLineSectionInfo represents diff line section meta data @@ -118,15 +117,15 @@ func (d *DiffLine) GetHTMLDiffLineType() string { // CanComment returns whether a line can get commented func (d *DiffLine) CanComment() bool { - return len(d.Comments) == 0 && d.Type != DiffLineSection + return len(d.Conversations) == 0 && d.Type != DiffLineSection } // GetCommentSide returns the comment side of the first comment, if not set returns empty string func (d *DiffLine) GetCommentSide() string { - if len(d.Comments) == 0 { + if len(d.Conversations) == 0 || len(d.Conversations[0]) == 0 { return "" } - return d.Comments[0].DiffSide() + return d.Conversations[0][0].DiffSide() } // GetLineTypeMarker returns the line type marker @@ -467,23 +466,20 @@ type Diff struct { // LoadComments loads comments into each line func (diff *Diff) LoadComments(ctx context.Context, issue *issues_model.Issue, currentUser *user_model.User, showOutdatedComments bool) error { - allComments, err := issues_model.FetchCodeComments(ctx, issue, currentUser, showOutdatedComments) + allConversations, err := issues_model.FetchCodeConversations(ctx, issue, currentUser, showOutdatedComments) if err != nil { return err } for _, file := range diff.Files { - if lineCommits, ok := allComments[file.Name]; ok { + if lineCommits, ok := allConversations[file.Name]; ok { for _, section := range file.Sections { for _, line := range section.Lines { - if comments, ok := lineCommits[int64(line.LeftIdx*-1)]; ok { - line.Comments = append(line.Comments, comments...) + if conversations, ok := lineCommits[int64(line.LeftIdx*-1)]; ok { + line.Conversations = append(line.Conversations, conversations...) } if comments, ok := lineCommits[int64(line.RightIdx)]; ok { - line.Comments = append(line.Comments, comments...) + line.Conversations = append(line.Conversations, comments...) } - sort.SliceStable(line.Comments, func(i, j int) bool { - return line.Comments[i].CreatedUnix < line.Comments[j].CreatedUnix - }) } } } diff --git a/services/gitdiff/gitdiff_test.go b/services/gitdiff/gitdiff_test.go index adcac355a7..8d6c376dce 100644 --- a/services/gitdiff/gitdiff_test.go +++ b/services/gitdiff/gitdiff_test.go @@ -601,7 +601,7 @@ func TestDiff_LoadCommentsNoOutdated(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) diff := setupDefaultDiff() assert.NoError(t, diff.LoadComments(db.DefaultContext, issue, user, false)) - assert.Len(t, diff.Files[0].Sections[0].Lines[0].Comments, 2) + assert.Len(t, diff.Files[0].Sections[0].Lines[0].Conversations, 2) } func TestDiff_LoadCommentsWithOutdated(t *testing.T) { @@ -611,20 +611,22 @@ func TestDiff_LoadCommentsWithOutdated(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) diff := setupDefaultDiff() assert.NoError(t, diff.LoadComments(db.DefaultContext, issue, user, true)) - assert.Len(t, diff.Files[0].Sections[0].Lines[0].Comments, 3) + assert.Len(t, diff.Files[0].Sections[0].Lines[0].Conversations, 2) + assert.Len(t, diff.Files[0].Sections[0].Lines[0].Conversations[0], 2) + assert.Len(t, diff.Files[0].Sections[0].Lines[0].Conversations[1], 1) } func TestDiffLine_CanComment(t *testing.T) { assert.False(t, (&DiffLine{Type: DiffLineSection}).CanComment()) - assert.False(t, (&DiffLine{Type: DiffLineAdd, Comments: []*issues_model.Comment{{Content: "bla"}}}).CanComment()) + assert.False(t, (&DiffLine{Type: DiffLineAdd, Conversations: []issues_model.CodeConversation{{{Content: "bla"}}}}).CanComment()) assert.True(t, (&DiffLine{Type: DiffLineAdd}).CanComment()) assert.True(t, (&DiffLine{Type: DiffLineDel}).CanComment()) assert.True(t, (&DiffLine{Type: DiffLinePlain}).CanComment()) } func TestDiffLine_GetCommentSide(t *testing.T) { - assert.Equal(t, "previous", (&DiffLine{Comments: []*issues_model.Comment{{Line: -3}}}).GetCommentSide()) - assert.Equal(t, "proposed", (&DiffLine{Comments: []*issues_model.Comment{{Line: 3}}}).GetCommentSide()) + assert.Equal(t, "previous", (&DiffLine{Conversations: []issues_model.CodeConversation{{{Line: -3}}}}).GetCommentSide()) + assert.Equal(t, "proposed", (&DiffLine{Conversations: []issues_model.CodeConversation{{{Line: 3}}}}).GetCommentSide()) } func TestGetDiffRangeWithWhitespaceBehavior(t *testing.T) { diff --git a/services/repository/files/diff_test.go b/services/repository/files/diff_test.go index 91c878e505..fbd2f3e70f 100644 --- a/services/repository/files/diff_test.go +++ b/services/repository/files/diff_test.go @@ -53,11 +53,11 @@ func TestGetDiffPreview(t *testing.T) { Name: "", Lines: []*gitdiff.DiffLine{ { - LeftIdx: 0, - RightIdx: 0, - Type: 4, - Content: "@@ -1,3 +1,4 @@", - Comments: nil, + LeftIdx: 0, + RightIdx: 0, + Type: 4, + Content: "@@ -1,3 +1,4 @@", + Conversations: nil, SectionInfo: &gitdiff.DiffLineSectionInfo{ Path: "README.md", LastLeftIdx: 0, @@ -69,42 +69,42 @@ func TestGetDiffPreview(t *testing.T) { }, }, { - LeftIdx: 1, - RightIdx: 1, - Type: 1, - Content: " # repo1", - Comments: nil, + LeftIdx: 1, + RightIdx: 1, + Type: 1, + Content: " # repo1", + Conversations: nil, }, { - LeftIdx: 2, - RightIdx: 2, - Type: 1, - Content: " ", - Comments: nil, + LeftIdx: 2, + RightIdx: 2, + Type: 1, + Content: " ", + Conversations: nil, }, { - LeftIdx: 3, - RightIdx: 0, - Match: 4, - Type: 3, - Content: "-Description for repo1", - Comments: nil, + LeftIdx: 3, + RightIdx: 0, + Match: 4, + Type: 3, + Content: "-Description for repo1", + Conversations: nil, }, { - LeftIdx: 0, - RightIdx: 3, - Match: 3, - Type: 2, - Content: "+Description for repo1", - Comments: nil, + LeftIdx: 0, + RightIdx: 3, + Match: 3, + Type: 2, + Content: "+Description for repo1", + Conversations: nil, }, { - LeftIdx: 0, - RightIdx: 4, - Match: -1, - Type: 2, - Content: "+this is a new line", - Comments: nil, + LeftIdx: 0, + RightIdx: 4, + Match: -1, + Type: 2, + Content: "+this is a new line", + Conversations: nil, }, }, }, diff --git a/templates/repo/diff/conversations.tmpl b/templates/repo/diff/conversations.tmpl new file mode 100644 index 0000000000..5945337cd3 --- /dev/null +++ b/templates/repo/diff/conversations.tmpl @@ -0,0 +1,3 @@ +{{range .conversations}} + {{template "repo/diff/conversation" dict "." $ "comments" .}} +{{end}} diff --git a/templates/repo/diff/section_split.tmpl b/templates/repo/diff/section_split.tmpl index 5b0d982e96..5137e0e838 100644 --- a/templates/repo/diff/section_split.tmpl +++ b/templates/repo/diff/section_split.tmpl @@ -108,44 +108,44 @@ {{if and (eq .GetType 3) $hasmatch}} {{$match := index $section.Lines $line.Match}} - {{if or $line.Comments $match.Comments}} + {{if or $line.Conversations $match.Conversations}} - {{if $line.Comments}} + {{if $line.Conversations}} {{if eq $line.GetCommentSide "previous"}} - {{template "repo/diff/conversation" dict "." $.root "comments" $line.Comments}} + {{template "repo/diff/conversations" dict "." $.root "conversations" $line.Conversations}} {{end}} {{end}} - {{if $match.Comments}} + {{if $match.Conversations}} {{if eq $match.GetCommentSide "previous"}} - {{template "repo/diff/conversation" dict "." $.root "comments" $match.Comments}} + {{template "repo/diff/conversations" dict "." $.root "conversations" $match.Conversations}} {{end}} {{end}} - {{if $line.Comments}} + {{if $line.Conversations}} {{if eq $line.GetCommentSide "proposed"}} - {{template "repo/diff/conversation" dict "." $.root "comments" $line.Comments}} + {{template "repo/diff/conversations" dict "." $.root "conversations" $line.Conversations}} {{end}} {{end}} - {{if $match.Comments}} + {{if $match.Conversations}} {{if eq $match.GetCommentSide "proposed"}} - {{template "repo/diff/conversation" dict "." $.root "comments" $match.Comments}} + {{template "repo/diff/conversations" dict "." $.root "conversations" $match.Conversations}} {{end}} {{end}} {{end}} - {{else if $line.Comments}} + {{else if $line.Conversations}} {{if eq $line.GetCommentSide "previous"}} - {{template "repo/diff/conversation" dict "." $.root "comments" $line.Comments}} + {{template "repo/diff/conversations" dict "." $.root "conversations" $line.Conversations}} {{end}} {{if eq $line.GetCommentSide "proposed"}} - {{template "repo/diff/conversation" dict "." $.root "comments" $line.Comments}} + {{template "repo/diff/conversations" dict "." $.root "conversations" $line.Conversations}} {{end}} diff --git a/templates/repo/diff/section_unified.tmpl b/templates/repo/diff/section_unified.tmpl index 2b901411e2..7e00677121 100644 --- a/templates/repo/diff/section_unified.tmpl +++ b/templates/repo/diff/section_unified.tmpl @@ -60,10 +60,10 @@ */}} {{end}} - {{if $line.Comments}} + {{if $line.Conversations}} - {{template "repo/diff/conversation" dict "." $.root "comments" $line.Comments}} + {{template "repo/diff/conversations" dict "." $.root "conversations" $line.Conversations}} {{end}} diff --git a/templates/repo/issue/view_content/conversation.tmpl b/templates/repo/issue/view_content/conversation.tmpl index 0e978dafcd..c9e5ee6275 100644 --- a/templates/repo/issue/view_content/conversation.tmpl +++ b/templates/repo/issue/view_content/conversation.tmpl @@ -1,7 +1,6 @@ {{$invalid := (index .comments 0).Invalidated}} {{$resolved := (index .comments 0).IsResolved}} {{$resolveDoer := (index .comments 0).ResolveDoer}} -{{$hideByDefault := or $resolved (and $invalid (not .ShowOutdatedComments))}} {{$isNotPending := (not (eq (index .comments 0).Review.Type 0))}}
    @@ -15,7 +14,7 @@
    {{if or $invalid $resolved}} - -
    diff --git a/templates/admin/repo/list.tmpl b/templates/admin/repo/list.tmpl index fdba0734a2..c7a6ec7e4e 100644 --- a/templates/admin/repo/list.tmpl +++ b/templates/admin/repo/list.tmpl @@ -101,7 +101,7 @@

    {{ctx.Locale.Tr "repo.settings.delete_desc"}}

    - {{ctx.Locale.Tr "repo.settings.delete_notices_2" `` | Safe}}
    + {{ctx.Locale.Tr "repo.settings.delete_notices_2" (``|Safe)}}
    {{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}}
    {{template "base/modal_actions_confirm" .}} diff --git a/templates/admin/stacktrace.tmpl b/templates/admin/stacktrace.tmpl index 894e41f8d7..aa5e810cd7 100644 --- a/templates/admin/stacktrace.tmpl +++ b/templates/admin/stacktrace.tmpl @@ -39,7 +39,7 @@ {{ctx.Locale.Tr "admin.monitor.process.cancel"}}
    -

    {{ctx.Locale.Tr "admin.monitor.process.cancel_notices" `` | Safe}}

    +

    {{ctx.Locale.Tr "admin.monitor.process.cancel_notices" (``|Safe)}}

    {{ctx.Locale.Tr "admin.monitor.process.cancel_desc"}}

    {{template "base/modal_actions_confirm" .}} diff --git a/templates/org/member/members.tmpl b/templates/org/member/members.tmpl index e4ddb69805..03509ec93e 100644 --- a/templates/org/member/members.tmpl +++ b/templates/org/member/members.tmpl @@ -73,7 +73,7 @@ {{ctx.Locale.Tr "org.members.leave"}}
    -

    {{ctx.Locale.Tr "org.members.leave.detail" `` | Safe}}

    +

    {{ctx.Locale.Tr "org.members.leave.detail" (``|Safe)}}

    {{template "base/modal_actions_confirm" .}} @@ -82,7 +82,7 @@ {{ctx.Locale.Tr "org.members.remove"}}
    -

    {{ctx.Locale.Tr "org.members.remove.detail" `` `` | Safe}}

    +

    {{ctx.Locale.Tr "org.members.remove.detail" (``|Safe) (``|Safe)}}

    {{template "base/modal_actions_confirm" .}} diff --git a/templates/org/team/members.tmpl b/templates/org/team/members.tmpl index da63d82967..dd4ece1433 100644 --- a/templates/org/team/members.tmpl +++ b/templates/org/team/members.tmpl @@ -81,7 +81,7 @@ {{ctx.Locale.Tr "org.members.remove"}}
    -

    {{ctx.Locale.Tr "org.members.remove.detail" `` `` | Safe}}

    +

    {{ctx.Locale.Tr "org.members.remove.detail" (``|Safe) (``|Safe)}}

    {{template "base/modal_actions_confirm" .}} diff --git a/templates/org/team/sidebar.tmpl b/templates/org/team/sidebar.tmpl index 29e7cf7cdd..37550ab71f 100644 --- a/templates/org/team/sidebar.tmpl +++ b/templates/org/team/sidebar.tmpl @@ -88,7 +88,7 @@ {{ctx.Locale.Tr "org.teams.leave"}}
    -

    {{ctx.Locale.Tr "org.teams.leave.detail" `` | Safe}}

    +

    {{ctx.Locale.Tr "org.teams.leave.detail" (``|Safe)}}

    {{template "base/modal_actions_confirm" .}} diff --git a/templates/org/team/teams.tmpl b/templates/org/team/teams.tmpl index f4ceada2a7..b518d7d9d7 100644 --- a/templates/org/team/teams.tmpl +++ b/templates/org/team/teams.tmpl @@ -49,7 +49,7 @@ {{ctx.Locale.Tr "org.teams.leave"}}
    -

    {{ctx.Locale.Tr "org.teams.leave.detail" `` | Safe}}

    +

    {{ctx.Locale.Tr "org.teams.leave.detail" (``|Safe)}}

    {{template "base/modal_actions_confirm" .}} diff --git a/templates/repo/commit_page.tmpl b/templates/repo/commit_page.tmpl index 01fa45babe..ce9fcecd8b 100644 --- a/templates/repo/commit_page.tmpl +++ b/templates/repo/commit_page.tmpl @@ -88,7 +88,7 @@ {{.CsrfTokenHtml}}
    @@ -113,7 +113,7 @@
    diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index f71ee2f11b..9e50ee4d94 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -112,9 +112,9 @@ {{template "shared/user/authorlink" .Poster}} {{$link := printf "%s/commit/%s" $.Repository.Link ($.Issue.PullRequest.MergedCommitID|PathEscape)}} {{if eq $.Issue.PullRequest.Status 3}} - {{ctx.Locale.Tr "repo.issues.comment_manually_pull_merged_at" (printf `%[2]s` ($link|Escape) (ShortSha $.Issue.PullRequest.MergedCommitID)) (printf "%[1]s" ($.BaseTarget|Escape)) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.comment_manually_pull_merged_at" (printf `%[2]s` ($link|Escape) (ShortSha $.Issue.PullRequest.MergedCommitID) | Safe) (printf "%[1]s" ($.BaseTarget|Escape) | Safe) $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.comment_pull_merged_at" (printf `%[2]s` ($link|Escape) (ShortSha $.Issue.PullRequest.MergedCommitID)) (printf "%[1]s" ($.BaseTarget|Escape)) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.comment_pull_merged_at" (printf `%[2]s` ($link|Escape) (ShortSha $.Issue.PullRequest.MergedCommitID) | Safe) (printf "%[1]s" ($.BaseTarget|Escape) | Safe) $createdStr}} {{end}}
    diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index f1ab53eb67..a28b849f98 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -38,7 +38,7 @@ {{ctx.Locale.Tr "repo.pulls.merged_success"}}
    diff --git a/templates/repo/settings/webhook/settings.tmpl b/templates/repo/settings/webhook/settings.tmpl index 3dfa094cf5..8e2387067e 100644 --- a/templates/repo/settings/webhook/settings.tmpl +++ b/templates/repo/settings/webhook/settings.tmpl @@ -263,7 +263,7 @@ {{if ne .HookType "matrix"}}{{/* Matrix doesn't make the authorization optional but it is implied by the help string, should be changed.*/}} - {{ctx.Locale.Tr "repo.settings.authorization_header_desc" "Bearer token123456, Basic YWxhZGRpbjpvcGVuc2VzYW1l" | Str2html}} + {{ctx.Locale.Tr "repo.settings.authorization_header_desc" ("Bearer token123456, Basic YWxhZGRpbjpvcGVuc2VzYW1l" | Safe)}} {{end}}
    diff --git a/templates/user/settings/organization.tmpl b/templates/user/settings/organization.tmpl index 8079521984..102ff2e95b 100644 --- a/templates/user/settings/organization.tmpl +++ b/templates/user/settings/organization.tmpl @@ -47,7 +47,7 @@ {{ctx.Locale.Tr "org.members.leave"}}
    -

    {{ctx.Locale.Tr "org.members.leave.detail" `` | Safe}}

    +

    {{ctx.Locale.Tr "org.members.leave.detail" (``|Safe)}}

    {{template "base/modal_actions_confirm" .}} From 81925ebb0cd2cdfc17fb1dfc0be20cfe8989f810 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Sun, 18 Feb 2024 17:52:02 +0800 Subject: [PATCH 110/271] Refactor more code in templates (#29236) Follow #29165. * Introduce JSONTemplate to help to render JSON templates * Introduce JSEscapeSafe for templates. Now only use `{{ ... | JSEscape}}` instead of `{{ ... | JSEscape | Safe}}` * Simplify "UserLocationMapURL" useage (cherry picked from commit 31bb9f3247388b993c61a10190cfd512408ce57e) --- Makefile | 4 ++-- modules/context/context_response.go | 14 ++++++++++++++ modules/templates/helper.go | 6 +++++- modules/templates/helper_test.go | 4 ++++ routers/api/v1/api.go | 2 +- routers/web/auth/oauth.go | 10 +--------- routers/web/shared/user/header.go | 4 +++- routers/web/swagger_json.go | 14 +------------- templates/shared/user/profile_big_avatar.tmpl | 5 ++--- templates/swagger/v1_json.tmpl | 4 ++-- templates/user/auth/oidc_wellknown.tmpl | 14 +++++++------- 11 files changed, 42 insertions(+), 39 deletions(-) diff --git a/Makefile b/Makefile index ffffb1faf0..f023608b1e 100644 --- a/Makefile +++ b/Makefile @@ -156,8 +156,8 @@ endif FORGEJO_API_SPEC := public/assets/forgejo/api.v1.yml SWAGGER_SPEC := templates/swagger/v1_json.tmpl -SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|g -SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape \| Safe}}/api/v1"|"basePath": "/api/v1"|g +SWAGGER_SPEC_S_TMPL := s|"basePath": *"/api/v1"|"basePath": "{{AppSubUrl \| JSEscape}}/api/v1"|g +SWAGGER_SPEC_S_JSON := s|"basePath": *"{{AppSubUrl \| JSEscape}}/api/v1"|"basePath": "/api/v1"|g SWAGGER_EXCLUDE := code.gitea.io/sdk SWAGGER_NEWLINE_COMMAND := -e '$$a\' SWAGGER_SPEC_BRANDING := s|Gitea API|Forgejo API|g diff --git a/modules/context/context_response.go b/modules/context/context_response.go index d9102b77bd..829bca1f59 100644 --- a/modules/context/context_response.go +++ b/modules/context/context_response.go @@ -90,6 +90,20 @@ func (ctx *Context) HTML(status int, name base.TplName) { } } +// JSONTemplate renders the template as JSON response +// keep in mind that the template is processed in HTML context, so JSON-things should be handled carefully, eg: by JSEscape +func (ctx *Context) JSONTemplate(tmpl base.TplName) { + t, err := ctx.Render.TemplateLookup(string(tmpl), nil) + if err != nil { + ctx.ServerError("unable to find template", err) + return + } + ctx.Resp.Header().Set("Content-Type", "application/json") + if err = t.Execute(ctx.Resp, ctx.Data); err != nil { + ctx.ServerError("unable to execute template", err) + } +} + // RenderToString renders the template content to a string func (ctx *Context) RenderToString(name base.TplName, data map[string]any) (string, error) { var buf strings.Builder diff --git a/modules/templates/helper.go b/modules/templates/helper.go index 90371fa8fe..3bf1919b4a 100644 --- a/modules/templates/helper.go +++ b/modules/templates/helper.go @@ -38,7 +38,7 @@ func NewFuncMap() template.FuncMap { "Safe": Safe, "Escape": Escape, "QueryEscape": url.QueryEscape, - "JSEscape": template.JSEscapeString, + "JSEscape": JSEscapeSafe, "Str2html": Str2html, // TODO: rename it to SanitizeHTML "URLJoin": util.URLJoin, "DotEscape": DotEscape, @@ -214,6 +214,10 @@ func Escape(s any) template.HTML { panic(fmt.Sprintf("unexpected type %T", s)) } +func JSEscapeSafe(s string) template.HTML { + return template.HTML(template.JSEscapeString(s)) +} + func RenderEmojiPlain(s any) any { switch v := s.(type) { case string: diff --git a/modules/templates/helper_test.go b/modules/templates/helper_test.go index ec83e9ac33..739a92f34f 100644 --- a/modules/templates/helper_test.go +++ b/modules/templates/helper_test.go @@ -52,3 +52,7 @@ func TestSubjectBodySeparator(t *testing.T) { "", "Insuficient\n--\nSeparators") } + +func TestJSEscapeSafe(t *testing.T) { + assert.EqualValues(t, `\u0026\u003C\u003E\'\"`, JSEscapeSafe(`&<>'"`)) +} diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 58814d3b2e..1babccb650 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -8,7 +8,7 @@ // // Schemes: https, http // BasePath: /api/v1 -// Version: {{AppVer | JSEscape | Safe}} +// Version: {{AppVer | JSEscape}} // License: MIT http://opensource.org/licenses/MIT // // Consumes: diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index 2adcb3aea0..e840e03bcf 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -579,16 +579,8 @@ func GrantApplicationOAuth(ctx *context.Context) { // OIDCWellKnown generates JSON so OIDC clients know Gitea's capabilities func OIDCWellKnown(ctx *context.Context) { - t, err := ctx.Render.TemplateLookup("user/auth/oidc_wellknown", nil) - if err != nil { - ctx.ServerError("unable to find template", err) - return - } - ctx.Resp.Header().Set("Content-Type", "application/json") ctx.Data["SigningKey"] = oauth2.DefaultSigningKey - if err = t.Execute(ctx.Resp, ctx.Data); err != nil { - ctx.ServerError("unable to execute template", err) - } + ctx.JSONTemplate("user/auth/oidc_wellknown") } // OIDCKeys generates the JSON Web Key Set diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 203e5fa5a5..6830bdb8a9 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -4,6 +4,8 @@ package user import ( + "net/url" + "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" access_model "code.gitea.io/gitea/models/perm/access" @@ -37,7 +39,7 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) { ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) ctx.Data["IsBlocked"] = ctx.Doer != nil && user_model.IsBlocked(ctx, ctx.Doer.ID, ctx.ContextUser.ID) ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate - ctx.Data["UserLocationMapURL"] = setting.Service.UserLocationMapURL + ctx.Data["ContextUserLocationMapURL"] = setting.Service.UserLocationMapURL + url.QueryEscape(ctx.ContextUser.Location) // Show OpenID URIs openIDs, err := user_model.GetUserOpenIDs(ctx, ctx.ContextUser.ID) diff --git a/routers/web/swagger_json.go b/routers/web/swagger_json.go index 493c97aa67..42e9dbe967 100644 --- a/routers/web/swagger_json.go +++ b/routers/web/swagger_json.go @@ -4,22 +4,10 @@ package web import ( - "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" ) -// tplSwaggerV1Json swagger v1 json template -const tplSwaggerV1Json base.TplName = "swagger/v1_json" - // SwaggerV1Json render swagger v1 json func SwaggerV1Json(ctx *context.Context) { - t, err := ctx.Render.TemplateLookup(string(tplSwaggerV1Json), nil) - if err != nil { - ctx.ServerError("unable to find template", err) - return - } - ctx.Resp.Header().Set("Content-Type", "application/json") - if err = t.Execute(ctx.Resp, ctx.Data); err != nil { - ctx.ServerError("unable to execute template", err) - } + ctx.JSONTemplate("swagger/v1_json") } diff --git a/templates/shared/user/profile_big_avatar.tmpl b/templates/shared/user/profile_big_avatar.tmpl index fefaa9dd17..e731cfe2e0 100644 --- a/templates/shared/user/profile_big_avatar.tmpl +++ b/templates/shared/user/profile_big_avatar.tmpl @@ -31,9 +31,8 @@
  • {{svg "octicon-location"}} {{.ContextUser.Location}} - {{if .UserLocationMapURL}} - {{/* We presume that the UserLocationMapURL is safe, as it is provided by the site administrator. */}} - + {{if .ContextUserLocationMapURL}} + {{svg "octicon-link-external"}} {{end}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 8a40cf76d4..0b330a89ee 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -19,9 +19,9 @@ "name": "MIT", "url": "http://opensource.org/licenses/MIT" }, - "version": "{{AppVer | JSEscape | Safe}}" + "version": "{{AppVer | JSEscape}}" }, - "basePath": "{{AppSubUrl | JSEscape | Safe}}/api/v1", + "basePath": "{{AppSubUrl | JSEscape}}/api/v1", "paths": { "/activitypub/user-id/{user-id}": { "get": { diff --git a/templates/user/auth/oidc_wellknown.tmpl b/templates/user/auth/oidc_wellknown.tmpl index 38e6900c38..54bb4a763d 100644 --- a/templates/user/auth/oidc_wellknown.tmpl +++ b/templates/user/auth/oidc_wellknown.tmpl @@ -1,16 +1,16 @@ { - "issuer": "{{AppUrl | JSEscape | Safe}}", - "authorization_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/authorize", - "token_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/access_token", - "jwks_uri": "{{AppUrl | JSEscape | Safe}}login/oauth/keys", - "userinfo_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/userinfo", - "introspection_endpoint": "{{AppUrl | JSEscape | Safe}}login/oauth/introspect", + "issuer": "{{AppUrl | JSEscape}}", + "authorization_endpoint": "{{AppUrl | JSEscape}}login/oauth/authorize", + "token_endpoint": "{{AppUrl | JSEscape}}login/oauth/access_token", + "jwks_uri": "{{AppUrl | JSEscape}}login/oauth/keys", + "userinfo_endpoint": "{{AppUrl | JSEscape}}login/oauth/userinfo", + "introspection_endpoint": "{{AppUrl | JSEscape}}login/oauth/introspect", "response_types_supported": [ "code", "id_token" ], "id_token_signing_alg_values_supported": [ - "{{.SigningKey.SigningMethod.Alg | JSEscape | Safe}}" + "{{.SigningKey.SigningMethod.Alg | JSEscape}}" ], "subject_types_supported": [ "public" From d93d963c3f9002a5235fdc3d1c88c79fad551f6a Mon Sep 17 00:00:00 2001 From: yp05327 <576951401@qq.com> Date: Sun, 18 Feb 2024 19:58:46 +0900 Subject: [PATCH 111/271] Implement some action notifier functions (#29173) Fix #29166 Add support for the following activity types of `pull_request` - assigned - unassigned - review_requested - review_request_removed - milestoned - demilestoned (cherry picked from commit 1a6e1cbada27db1e3327b0d7d331492c95e24759) --- modules/actions/github.go | 4 +- modules/actions/workflows.go | 8 ++-- services/actions/notifier.go | 76 +++++++++++++++++++++++++++++++----- 3 files changed, 75 insertions(+), 13 deletions(-) diff --git a/modules/actions/github.go b/modules/actions/github.go index a988b2a124..d4e559408b 100644 --- a/modules/actions/github.go +++ b/modules/actions/github.go @@ -55,7 +55,9 @@ func canGithubEventMatch(eventName string, triggedEvent webhook_module.HookEvent case webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestSync, webhook_module.HookEventPullRequestAssign, - webhook_module.HookEventPullRequestLabel: + webhook_module.HookEventPullRequestLabel, + webhook_module.HookEventPullRequestReviewRequest, + webhook_module.HookEventPullRequestMilestone: return true default: diff --git a/modules/actions/workflows.go b/modules/actions/workflows.go index 00d83e06d7..81ab26bc27 100644 --- a/modules/actions/workflows.go +++ b/modules/actions/workflows.go @@ -186,7 +186,9 @@ func detectMatched(gitRepo *git.Repository, commit *git.Commit, triggedEvent web webhook_module.HookEventPullRequest, webhook_module.HookEventPullRequestSync, webhook_module.HookEventPullRequestAssign, - webhook_module.HookEventPullRequestLabel: + webhook_module.HookEventPullRequestLabel, + webhook_module.HookEventPullRequestReviewRequest, + webhook_module.HookEventPullRequestMilestone: return matchPullRequestEvent(gitRepo, commit, payload.(*api.PullRequestPayload), evt) case // pull_request_review @@ -362,13 +364,13 @@ func matchPullRequestEvent(gitRepo *git.Repository, commit *git.Commit, prPayloa } else { // See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request // Actions with the same name: - // opened, edited, closed, reopened, assigned, unassigned + // opened, edited, closed, reopened, assigned, unassigned, review_requested, review_request_removed, milestoned, demilestoned // Actions need to be converted: // synchronized -> synchronize // label_updated -> labeled // label_cleared -> unlabeled // Unsupported activity types: - // converted_to_draft, ready_for_review, locked, unlocked, review_requested, review_request_removed, auto_merge_enabled, auto_merge_disabled + // converted_to_draft, ready_for_review, locked, unlocked, auto_merge_enabled, auto_merge_disabled, enqueued, dequeued action := prPayload.Action switch action { diff --git a/services/actions/notifier.go b/services/actions/notifier.go index 0b4fed5db1..093607f05c 100644 --- a/services/actions/notifier.go +++ b/services/actions/notifier.go @@ -101,11 +101,40 @@ func (n *actionsNotifier) IssueChangeStatus(ctx context.Context, doer *user_mode Notify(ctx) } +// IssueChangeAssignee notifies assigned or unassigned to notifiers +func (n *actionsNotifier) IssueChangeAssignee(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) { + ctx = withMethod(ctx, "IssueChangeAssignee") + + var action api.HookIssueAction + if removed { + action = api.HookIssueUnassigned + } else { + action = api.HookIssueAssigned + } + notifyIssueChange(ctx, doer, issue, webhook_module.HookEventPullRequestAssign, action) +} + +// IssueChangeMilestone notifies assignee to notifiers +func (n *actionsNotifier) IssueChangeMilestone(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldMilestoneID int64) { + ctx = withMethod(ctx, "IssueChangeMilestone") + + var action api.HookIssueAction + if issue.MilestoneID > 0 { + action = api.HookIssueMilestoned + } else { + action = api.HookIssueDemilestoned + } + notifyIssueChange(ctx, doer, issue, webhook_module.HookEventPullRequestMilestone, action) +} + func (n *actionsNotifier) IssueChangeLabels(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, _, _ []*issues_model.Label, ) { ctx = withMethod(ctx, "IssueChangeLabels") + notifyIssueChange(ctx, doer, issue, webhook_module.HookEventPullRequestLabel, api.HookIssueLabelUpdated) +} +func notifyIssueChange(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, event webhook_module.HookEventType, action api.HookIssueAction) { var err error if err = issue.LoadRepo(ctx); err != nil { log.Error("LoadRepo: %v", err) @@ -117,20 +146,15 @@ func (n *actionsNotifier) IssueChangeLabels(ctx context.Context, doer *user_mode return } - permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, issue.Poster) if issue.IsPull { if err = issue.LoadPullRequest(ctx); err != nil { log.Error("loadPullRequest: %v", err) return } - if err = issue.PullRequest.LoadIssue(ctx); err != nil { - log.Error("LoadIssue: %v", err) - return - } - newNotifyInputFromIssue(issue, webhook_module.HookEventPullRequestLabel). + newNotifyInputFromIssue(issue, event). WithDoer(doer). WithPayload(&api.PullRequestPayload{ - Action: api.HookIssueLabelUpdated, + Action: action, Index: issue.Index, PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), Repository: convert.ToRepo(ctx, issue.Repo, access_model.Permission{AccessMode: perm_model.AccessModeNone}), @@ -140,10 +164,11 @@ func (n *actionsNotifier) IssueChangeLabels(ctx context.Context, doer *user_mode Notify(ctx) return } - newNotifyInputFromIssue(issue, webhook_module.HookEventIssueLabel). + permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, issue.Poster) + newNotifyInputFromIssue(issue, event). WithDoer(doer). WithPayload(&api.IssuePayload{ - Action: api.HookIssueLabelUpdated, + Action: action, Index: issue.Index, Issue: convert.ToAPIIssue(ctx, issue), Repository: convert.ToRepo(ctx, issue.Repo, permission), @@ -305,6 +330,39 @@ func (n *actionsNotifier) PullRequestReview(ctx context.Context, pr *issues_mode }).Notify(ctx) } +func (n *actionsNotifier) PullRequestReviewRequest(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) { + if !issue.IsPull { + log.Warn("PullRequestReviewRequest: issue is not a pull request: %v", issue.ID) + return + } + + ctx = withMethod(ctx, "PullRequestReviewRequest") + + permission, _ := access_model.GetUserRepoPermission(ctx, issue.Repo, doer) + if err := issue.LoadPullRequest(ctx); err != nil { + log.Error("LoadPullRequest failed: %v", err) + return + } + var action api.HookIssueAction + if isRequest { + action = api.HookIssueReviewRequested + } else { + action = api.HookIssueReviewRequestRemoved + } + newNotifyInputFromIssue(issue, webhook_module.HookEventPullRequestReviewRequest). + WithDoer(doer). + WithPayload(&api.PullRequestPayload{ + Action: action, + Index: issue.Index, + PullRequest: convert.ToAPIPullRequest(ctx, issue.PullRequest, nil), + RequestedReviewer: convert.ToUser(ctx, reviewer, nil), + Repository: convert.ToRepo(ctx, issue.Repo, permission), + Sender: convert.ToUser(ctx, doer, nil), + }). + WithPullRequest(issue.PullRequest). + Notify(ctx) +} + func (*actionsNotifier) MergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) { ctx = withMethod(ctx, "MergePullRequest") From 559afdad731b2989737f043602e5d5b25edf4324 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Nicas=20Oelschl=C3=A4ger?= <72873130+zokkis@users.noreply.github.com> Date: Sun, 18 Feb 2024 12:47:50 +0100 Subject: [PATCH 112/271] Convert visibility to number (#29226) Don't throw error while creating user (Fixes #29218) (cherry picked from commit 6093f507fe6f2d4802de8ec1ff5b04820e81571c) --- templates/admin/user/new.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/admin/user/new.tmpl b/templates/admin/user/new.tmpl index 81f70511d0..bcb53d8131 100644 --- a/templates/admin/user/new.tmpl +++ b/templates/admin/user/new.tmpl @@ -26,7 +26,7 @@
    {{if .Commit.Signature}} -
    +
    {{if .Verification.Verified}} {{if ne .Verification.SigningUser.ID 0}} diff --git a/templates/repo/diff/box.tmpl b/templates/repo/diff/box.tmpl index 05559fc9a7..9b4a9ba983 100644 --- a/templates/repo/diff/box.tmpl +++ b/templates/repo/diff/box.tmpl @@ -1,7 +1,7 @@ {{$showFileTree := (and (not .DiffNotAvailable) (gt .Diff.NumFiles 1))}}
    -
    +
    {{if $showFileTree}}
    {{end}} From bb911b2d5f709ebdf927771a141c263ff9d10e41 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Mon, 19 Feb 2024 00:24:35 +0000 Subject: [PATCH 117/271] [skip ci] Updated licenses and gitignores (cherry picked from commit f04e71f9bc05d4930e1eff0b69ceb0e890528e30) --- options/license/Brian-Gladman-2-Clause | 17 ++++++++++ options/license/CMU-Mach-nodoc | 11 +++++++ options/license/GNOME-examples-exception | 1 + options/license/Gmsh-exception | 16 +++++++++ options/license/HPND-Fenneberg-Livingston | 13 ++++++++ options/license/HPND-INRIA-IMAG | 9 +++++ options/license/Mackerras-3-Clause | 25 ++++++++++++++ .../license/Mackerras-3-Clause-acknowledgment | 25 ++++++++++++++ options/license/OpenVision | 33 +++++++++++++++++++ options/license/Sun-PPP | 13 ++++++++ options/license/UMich-Merit | 19 +++++++++++ options/license/bcrypt-Solar-Designer | 11 +++++++ options/license/gtkbook | 6 ++++ options/license/softSurfer | 6 ++++ 14 files changed, 205 insertions(+) create mode 100644 options/license/Brian-Gladman-2-Clause create mode 100644 options/license/CMU-Mach-nodoc create mode 100644 options/license/GNOME-examples-exception create mode 100644 options/license/Gmsh-exception create mode 100644 options/license/HPND-Fenneberg-Livingston create mode 100644 options/license/HPND-INRIA-IMAG create mode 100644 options/license/Mackerras-3-Clause create mode 100644 options/license/Mackerras-3-Clause-acknowledgment create mode 100644 options/license/OpenVision create mode 100644 options/license/Sun-PPP create mode 100644 options/license/UMich-Merit create mode 100644 options/license/bcrypt-Solar-Designer create mode 100644 options/license/gtkbook create mode 100644 options/license/softSurfer diff --git a/options/license/Brian-Gladman-2-Clause b/options/license/Brian-Gladman-2-Clause new file mode 100644 index 0000000000..7276f63e9e --- /dev/null +++ b/options/license/Brian-Gladman-2-Clause @@ -0,0 +1,17 @@ +Copyright (C) 1998-2013, Brian Gladman, Worcester, UK. All + rights reserved. + +The redistribution and use of this software (with or without +changes) is allowed without the payment of fees or royalties +provided that: + + source code distributions include the above copyright notice, + this list of conditions and the following disclaimer; + + binary distributions include the above copyright notice, this + list of conditions and the following disclaimer in their + documentation. + +This software is provided 'as is' with no explicit or implied +warranties in respect of its operation, including, but not limited +to, correctness and fitness for purpose. diff --git a/options/license/CMU-Mach-nodoc b/options/license/CMU-Mach-nodoc new file mode 100644 index 0000000000..c81d74fee7 --- /dev/null +++ b/options/license/CMU-Mach-nodoc @@ -0,0 +1,11 @@ +Copyright (C) 2002 Naval Research Laboratory (NRL/CCS) + +Permission to use, copy, modify and distribute this software and +its documentation is hereby granted, provided that both the +copyright notice and this permission notice appear in all copies of +the software, derivative works or modified versions, and any +portions thereof. + +NRL ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" CONDITION AND +DISCLAIMS ANY LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER +RESULTING FROM THE USE OF THIS SOFTWARE. diff --git a/options/license/GNOME-examples-exception b/options/license/GNOME-examples-exception new file mode 100644 index 0000000000..0f0cd53b50 --- /dev/null +++ b/options/license/GNOME-examples-exception @@ -0,0 +1 @@ +As a special exception, the copyright holders give you permission to copy, modify, and distribute the example code contained in this document under the terms of your choosing, without restriction. diff --git a/options/license/Gmsh-exception b/options/license/Gmsh-exception new file mode 100644 index 0000000000..6d28f704e4 --- /dev/null +++ b/options/license/Gmsh-exception @@ -0,0 +1,16 @@ +The copyright holders of Gmsh give you permission to combine Gmsh + with code included in the standard release of Netgen (from Joachim + Sch"oberl), METIS (from George Karypis at the University of + Minnesota), OpenCASCADE (from Open CASCADE S.A.S) and ParaView + (from Kitware, Inc.) under their respective licenses. You may copy + and distribute such a system following the terms of the GNU GPL for + Gmsh and the licenses of the other code concerned, provided that + you include the source code of that other code when and as the GNU + GPL requires distribution of source code. + + Note that people who make modified versions of Gmsh are not + obligated to grant this special exception for their modified + versions; it is their choice whether to do so. The GNU General + Public License gives permission to release a modified version + without this exception; this exception also makes it possible to + release a modified version which carries forward this exception. diff --git a/options/license/HPND-Fenneberg-Livingston b/options/license/HPND-Fenneberg-Livingston new file mode 100644 index 0000000000..aaf524f3aa --- /dev/null +++ b/options/license/HPND-Fenneberg-Livingston @@ -0,0 +1,13 @@ +Copyright (C) 1995,1996,1997,1998 Lars Fenneberg + +Permission to use, copy, modify, and distribute this software for any +purpose and without fee is hereby granted, provided that this copyright and +permission notice appear on all copies and supporting documentation, the +name of Lars Fenneberg not be used in advertising or publicity pertaining to +distribution of the program without specific prior permission, and notice be +given in supporting documentation that copying and distribution is by +permission of Lars Fenneberg. + +Lars Fenneberg makes no representations about the suitability of this +software for any purpose. It is provided "as is" without express or implied +warranty. diff --git a/options/license/HPND-INRIA-IMAG b/options/license/HPND-INRIA-IMAG new file mode 100644 index 0000000000..87d09d92cb --- /dev/null +++ b/options/license/HPND-INRIA-IMAG @@ -0,0 +1,9 @@ +This software is available with usual "research" terms with +the aim of retain credits of the software. Permission to use, +copy, modify and distribute this software for any purpose and +without fee is hereby granted, provided that the above copyright +notice and this permission notice appear in all copies, and +the name of INRIA, IMAG, or any contributor not be used in +advertising or publicity pertaining to this material without +the prior explicit permission. The software is provided "as +is" without any warranties, support or liabilities of any kind. diff --git a/options/license/Mackerras-3-Clause b/options/license/Mackerras-3-Clause new file mode 100644 index 0000000000..6467f0c98e --- /dev/null +++ b/options/license/Mackerras-3-Clause @@ -0,0 +1,25 @@ +Copyright (c) 1995 Eric Rosenquist. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + + 3. The name(s) of the authors of this software must not be used to + endorse or promote products derived from this software without + prior written permission. + + THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO + THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY + SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN + AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING + OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/options/license/Mackerras-3-Clause-acknowledgment b/options/license/Mackerras-3-Clause-acknowledgment new file mode 100644 index 0000000000..5f0187add7 --- /dev/null +++ b/options/license/Mackerras-3-Clause-acknowledgment @@ -0,0 +1,25 @@ +Copyright (c) 1993-2002 Paul Mackerras. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. The name(s) of the authors of this software must not be used to + endorse or promote products derived from this software without + prior written permission. + +3. Redistributions of any form whatsoever must retain the following + acknowledgment: + "This product includes software developed by Paul Mackerras + ". + +THE AUTHORS OF THIS SOFTWARE DISCLAIM ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS, IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY +SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN +AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING +OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/options/license/OpenVision b/options/license/OpenVision new file mode 100644 index 0000000000..983505389e --- /dev/null +++ b/options/license/OpenVision @@ -0,0 +1,33 @@ +Copyright, OpenVision Technologies, Inc., 1993-1996, All Rights +Reserved + +WARNING: Retrieving the OpenVision Kerberos Administration system +source code, as described below, indicates your acceptance of the +following terms. If you do not agree to the following terms, do +not retrieve the OpenVision Kerberos administration system. + +You may freely use and distribute the Source Code and Object Code +compiled from it, with or without modification, but this Source +Code is provided to you "AS IS" EXCLUSIVE OF ANY WARRANTY, +INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY OR +FITNESS FOR A PARTICULAR PURPOSE, OR ANY OTHER WARRANTY, WHETHER +EXPRESS OR IMPLIED. IN NO EVENT WILL OPENVISION HAVE ANY LIABILITY +FOR ANY LOST PROFITS, LOSS OF DATA OR COSTS OF PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES, OR FOR ANY SPECIAL, INDIRECT, OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THIS AGREEMENT, INCLUDING, +WITHOUT LIMITATION, THOSE RESULTING FROM THE USE OF THE SOURCE +CODE, OR THE FAILURE OF THE SOURCE CODE TO PERFORM, OR FOR ANY +OTHER REASON. + +OpenVision retains all copyrights in the donated Source Code. +OpenVision also retains copyright to derivative works of the Source +Code, whether created by OpenVision or by a third party. The +OpenVision copyright notice must be preserved if derivative works +are made based on the donated Source Code. + +OpenVision Technologies, Inc. has donated this Kerberos +Administration system to MIT for inclusion in the standard Kerberos +5 distribution. This donation underscores our commitment to +continuing Kerberos technology development and our gratitude for +the valuable work which has been performed by MIT and the Kerberos +community. diff --git a/options/license/Sun-PPP b/options/license/Sun-PPP new file mode 100644 index 0000000000..5f94a13437 --- /dev/null +++ b/options/license/Sun-PPP @@ -0,0 +1,13 @@ +Copyright (c) 2001 by Sun Microsystems, Inc. +All rights reserved. + +Non-exclusive rights to redistribute, modify, translate, and use +this software in source and binary forms, in whole or in part, is +hereby granted, provided that the above copyright notice is +duplicated in any source form, and that neither the name of the +copyright holder nor the author is used to endorse or promote +products derived from this software. + +THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. diff --git a/options/license/UMich-Merit b/options/license/UMich-Merit new file mode 100644 index 0000000000..93e304b90e --- /dev/null +++ b/options/license/UMich-Merit @@ -0,0 +1,19 @@ +[C] The Regents of the University of Michigan and Merit Network, Inc. 1992, +1993, 1994, 1995 All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, provided +that the above copyright notice and this permission notice appear in all +copies of the software and derivative works or modified versions thereof, +and that both the copyright notice and this permission and disclaimer +notice appear in supporting documentation. + +THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE REGENTS OF THE +UNIVERSITY OF MICHIGAN AND MERIT NETWORK, INC. DO NOT WARRANT THAT THE +FUNCTIONS CONTAINED IN THE SOFTWARE WILL MEET LICENSEE'S REQUIREMENTS OR +THAT OPERATION WILL BE UNINTERRUPTED OR ERROR FREE. The Regents of the +University of Michigan and Merit Network, Inc. shall not be liable for any +special, indirect, incidental or consequential damages with respect to any +claim by Licensee or any third party arising from use of the software. diff --git a/options/license/bcrypt-Solar-Designer b/options/license/bcrypt-Solar-Designer new file mode 100644 index 0000000000..8cb05017fc --- /dev/null +++ b/options/license/bcrypt-Solar-Designer @@ -0,0 +1,11 @@ +Written by Solar Designer in 1998-2014. +No copyright is claimed, and the software is hereby placed in the public +domain. In case this attempt to disclaim copyright and place the software +in the public domain is deemed null and void, then the software is +Copyright (c) 1998-2014 Solar Designer and it is hereby released to the +general public under the following terms: + +Redistribution and use in source and binary forms, with or without +modification, are permitted. + +There's ABSOLUTELY NO WARRANTY, express or implied. diff --git a/options/license/gtkbook b/options/license/gtkbook new file mode 100644 index 0000000000..91215e80d6 --- /dev/null +++ b/options/license/gtkbook @@ -0,0 +1,6 @@ +Copyright 2005 Syd Logan, All Rights Reserved + +This code is distributed without warranty. You are free to use +this code for any purpose, however, if this code is republished or +redistributed in its original form, as hardcopy or electronically, +then you must include this copyright notice along with the code. diff --git a/options/license/softSurfer b/options/license/softSurfer new file mode 100644 index 0000000000..1bbc88c34c --- /dev/null +++ b/options/license/softSurfer @@ -0,0 +1,6 @@ +Copyright 2001, softSurfer (www.softsurfer.com) +This code may be freely used and modified for any purpose +providing that this copyright notice is included with it. +SoftSurfer makes no warranty for this code, and cannot be held +liable for any real or imagined damage resulting from its use. +Users of this code must verify correctness for their application. From b3f2447bc4b6a7220da748cc6eb24bd5568bee7c Mon Sep 17 00:00:00 2001 From: silverwind Date: Mon, 19 Feb 2024 03:23:06 +0100 Subject: [PATCH 118/271] Downscale pasted PNG images based on metadata (#29123) Some images like MacOS screenshots contain [pHYs](http://www.libpng.org/pub/png/book/chapter11.html#png.ch11.div.8) data which we can use to downscale uploaded images so they render in the same dppx ratio in which they were taken. Before: image After: image (cherry picked from commit 5e72526da4e915791f03af056890e16821bde052) --- web_src/js/features/comp/ImagePaste.js | 20 +++++++++-- web_src/js/utils/image.js | 47 ++++++++++++++++++++++++++ web_src/js/utils/image.test.js | 29 ++++++++++++++++ 3 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 web_src/js/utils/image.js create mode 100644 web_src/js/utils/image.test.js diff --git a/web_src/js/features/comp/ImagePaste.js b/web_src/js/features/comp/ImagePaste.js index 27abcfe56f..444ab89150 100644 --- a/web_src/js/features/comp/ImagePaste.js +++ b/web_src/js/features/comp/ImagePaste.js @@ -1,5 +1,7 @@ import $ from 'jquery'; +import {htmlEscape} from 'escape-goat'; import {POST} from '../../modules/fetch.js'; +import {imageInfo} from '../../utils/image.js'; async function uploadFile(file, uploadUrl) { const formData = new FormData(); @@ -109,10 +111,22 @@ const uploadClipboardImage = async (editor, dropzone, e) => { const placeholder = `![${name}](uploading ...)`; editor.insertPlaceholder(placeholder); - const data = await uploadFile(img, uploadUrl); - editor.replacePlaceholder(placeholder, `![${name}](/attachments/${data.uuid})`); - const $input = $(``).attr('id', data.uuid).val(data.uuid); + const {uuid} = await uploadFile(img, uploadUrl); + const {width, dppx} = await imageInfo(img); + + const url = `/attachments/${uuid}`; + let text; + if (width > 0 && dppx > 1) { + // Scale down images from HiDPI monitors. This uses the tag because it's the only + // method to change image size in Markdown that is supported by all implementations. + text = `${htmlEscape(name)}`; + } else { + text = `![${name}](${url})`; + } + editor.replacePlaceholder(placeholder, text); + + const $input = $(``).attr('id', uuid).val(uuid); $files.append($input); } }; diff --git a/web_src/js/utils/image.js b/web_src/js/utils/image.js new file mode 100644 index 0000000000..ed5d98e35a --- /dev/null +++ b/web_src/js/utils/image.js @@ -0,0 +1,47 @@ +export async function pngChunks(blob) { + const uint8arr = new Uint8Array(await blob.arrayBuffer()); + const chunks = []; + if (uint8arr.length < 12) return chunks; + const view = new DataView(uint8arr.buffer); + if (view.getBigUint64(0) !== 9894494448401390090n) return chunks; + + const decoder = new TextDecoder(); + let index = 8; + while (index < uint8arr.length) { + const len = view.getUint32(index); + chunks.push({ + name: decoder.decode(uint8arr.slice(index + 4, index + 8)), + data: uint8arr.slice(index + 8, index + 8 + len), + }); + index += len + 12; + } + + return chunks; +} + +// decode a image and try to obtain width and dppx. If will never throw but instead +// return default values. +export async function imageInfo(blob) { + let width = 0; // 0 means no width could be determined + let dppx = 1; // 1 dot per pixel for non-HiDPI screens + + if (blob.type === 'image/png') { // only png is supported currently + try { + for (const {name, data} of await pngChunks(blob)) { + const view = new DataView(data.buffer); + if (name === 'IHDR' && data?.length) { + // extract width from mandatory IHDR chunk + width = view.getUint32(0); + } else if (name === 'pHYs' && data?.length) { + // extract dppx from optional pHYs chunk, assuming pixels are square + const unit = view.getUint8(8); + if (unit === 1) { + dppx = Math.round(view.getUint32(0) / 39.3701) / 72; // meter to inch to dppx + } + } + } + } catch {} + } + + return {width, dppx}; +} diff --git a/web_src/js/utils/image.test.js b/web_src/js/utils/image.test.js new file mode 100644 index 0000000000..ba4758250c --- /dev/null +++ b/web_src/js/utils/image.test.js @@ -0,0 +1,29 @@ +import {pngChunks, imageInfo} from './image.js'; + +const pngNoPhys = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAAAAAA6fptVAAAADUlEQVQIHQECAP3/AAAAAgABzePRKwAAAABJRU5ErkJggg=='; +const pngPhys = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAIAAAACCAIAAAD91JpzAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAEElEQVQI12OQNZcAIgYIBQAL8gGxdzzM0A=='; +const pngEmpty = 'data:image/png;base64,'; + +async function dataUriToBlob(datauri) { + return await (await globalThis.fetch(datauri)).blob(); +} + +test('pngChunks', async () => { + expect(await pngChunks(await dataUriToBlob(pngNoPhys))).toEqual([ + {name: 'IHDR', data: new Uint8Array([0, 0, 0, 1, 0, 0, 0, 1, 8, 0, 0, 0, 0])}, + {name: 'IDAT', data: new Uint8Array([8, 29, 1, 2, 0, 253, 255, 0, 0, 0, 2, 0, 1])}, + {name: 'IEND', data: new Uint8Array([])}, + ]); + expect(await pngChunks(await dataUriToBlob(pngPhys))).toEqual([ + {name: 'IHDR', data: new Uint8Array([0, 0, 0, 2, 0, 0, 0, 2, 8, 2, 0, 0, 0])}, + {name: 'pHYs', data: new Uint8Array([0, 0, 22, 37, 0, 0, 22, 37, 1])}, + {name: 'IDAT', data: new Uint8Array([8, 215, 99, 144, 53, 151, 0, 34, 6, 8, 5, 0, 11, 242, 1, 177])}, + ]); + expect(await pngChunks(await dataUriToBlob(pngEmpty))).toEqual([]); +}); + +test('imageInfo', async () => { + expect(await imageInfo(await dataUriToBlob(pngNoPhys))).toEqual({width: 1, dppx: 1}); + expect(await imageInfo(await dataUriToBlob(pngPhys))).toEqual({width: 2, dppx: 2}); + expect(await imageInfo(await dataUriToBlob(pngEmpty))).toEqual({width: 0, dppx: 1}); +}); From e96e1bededfc04de60b2f8c730e84ecba538fc2d Mon Sep 17 00:00:00 2001 From: Jason Song Date: Mon, 19 Feb 2024 17:31:36 +0800 Subject: [PATCH 119/271] Do not use lower tag names to find releases/tags (#29261) Fix #26090, see https://github.com/go-gitea/gitea/issues/26090#issuecomment-1952013206 Since `TagName` stores the original tag name and `LowerTagName` stores the lower tag name, it doesn't make sense to use lowercase tags as `TagNames` in `FindReleasesOptions`. https://github.com/go-gitea/gitea/blob/5e72526da4e915791f03af056890e16821bde052/services/repository/push.go#L396-L397 While the only other usage looks correct: https://github.com/go-gitea/gitea/blob/5e72526da4e915791f03af056890e16821bde052/routers/web/repo/repo.go#L416 (cherry picked from commit 0ea8de2d0729e1e1d0ea9de1e59fbcb673e87fd2) --- services/repository/push.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/services/repository/push.go b/services/repository/push.go index 2ef8cac95e..5e2853b27d 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -321,14 +321,9 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo return nil } - lowerTags := make([]string, 0, len(tags)) - for _, tag := range tags { - lowerTags = append(lowerTags, strings.ToLower(tag)) - } - releases, err := db.Find[repo_model.Release](ctx, repo_model.FindReleasesOptions{ RepoID: repo.ID, - TagNames: lowerTags, + TagNames: tags, }) if err != nil { return fmt.Errorf("db.Find[repo_model.Release]: %w", err) @@ -338,6 +333,11 @@ func pushUpdateAddTags(ctx context.Context, repo *repo_model.Repository, gitRepo relMap[rel.LowerTagName] = rel } + lowerTags := make([]string, 0, len(tags)) + for _, tag := range tags { + lowerTags = append(lowerTags, strings.ToLower(tag)) + } + newReleases := make([]*repo_model.Release, 0, len(lowerTags)-len(relMap)) emailToUser := make(map[string]*user_model.User) From b1d66f50fbd1af1db8aa66b0c6393e57f8d08353 Mon Sep 17 00:00:00 2001 From: Markus Amshove Date: Mon, 19 Feb 2024 10:57:08 +0100 Subject: [PATCH 120/271] Disallow merge when required checked are missing (#29143) fixes #21892 This PR disallows merging a PR when not all commit status contexts configured in the branch protection are met. Previously, the PR was happy to merge when one commit status was successful and the other contexts weren't reported. Any feedback is welcome, first time Go :-) I'm also not sure if the changes in the template break something else Given the following branch protection: ![branch_protection](https://github.com/go-gitea/gitea/assets/2401875/f871b4e4-138b-435a-b496-f9ad432e3dec) This was shown before the change: ![before](https://github.com/go-gitea/gitea/assets/2401875/60424ff0-ee09-4fa0-856e-64e6e3fb0612) With the change, it is now shown as this: ![after](https://github.com/go-gitea/gitea/assets/2401875/4e464142-efb1-4889-8166-eb3be26c8f3d) --------- Co-authored-by: wxiaoguang (cherry picked from commit a11ccc9fcd61fb25ffb1c37b87a0df4ee9efd84e) --- routers/web/repo/pull.go | 30 +++++++++++++++++++++ services/pull/commit_status.go | 4 +++ templates/repo/issue/view_content/pull.tmpl | 1 + templates/repo/pulls/status.tmpl | 10 ++++++- 4 files changed, 44 insertions(+), 1 deletion(-) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index ca854a35f2..ab821f8884 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -662,6 +662,24 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C } if pb != nil && pb.EnableStatusCheck { + + var missingRequiredChecks []string + for _, requiredContext := range pb.StatusCheckContexts { + contextFound := false + matchesRequiredContext := createRequiredContextMatcher(requiredContext) + for _, presentStatus := range commitStatuses { + if matchesRequiredContext(presentStatus.Context) { + contextFound = true + break + } + } + + if !contextFound { + missingRequiredChecks = append(missingRequiredChecks, requiredContext) + } + } + ctx.Data["MissingRequiredChecks"] = missingRequiredChecks + ctx.Data["is_context_required"] = func(context string) bool { for _, c := range pb.StatusCheckContexts { if c == context { @@ -730,6 +748,18 @@ func PrepareViewPullInfo(ctx *context.Context, issue *issues_model.Issue) *git.C return compareInfo } +func createRequiredContextMatcher(requiredContext string) func(string) bool { + if gp, err := glob.Compile(requiredContext); err == nil { + return func(contextToCheck string) bool { + return gp.Match(contextToCheck) + } + } + + return func(contextToCheck string) bool { + return requiredContext == contextToCheck + } +} + type pullCommitList struct { Commits []pull_service.CommitInfo `json:"commits"` LastReviewCommitSha string `json:"last_review_commit_sha"` diff --git a/services/pull/commit_status.go b/services/pull/commit_status.go index 06e66fad77..27ee572640 100644 --- a/services/pull/commit_status.go +++ b/services/pull/commit_status.go @@ -52,6 +52,10 @@ func MergeRequiredContextsCommitStatus(commitStatuses []*git_model.CommitStatus, } } + if matchedCount != len(requiredContexts) { + return structs.CommitStatusPending + } + if matchedCount == 0 { status := git_model.CalcCommitStatus(commitStatuses) if status != nil { diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index a28b849f98..e86deb8915 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -24,6 +24,7 @@ {{template "repo/pulls/status" (dict "CommitStatus" .LatestCommitStatus "CommitStatuses" .LatestCommitStatuses + "MissingRequiredChecks" .MissingRequiredChecks "ShowHideChecks" true "is_context_required" .is_context_required )}} diff --git a/templates/repo/pulls/status.tmpl b/templates/repo/pulls/status.tmpl index ae508b8fa4..e8636ba1b8 100644 --- a/templates/repo/pulls/status.tmpl +++ b/templates/repo/pulls/status.tmpl @@ -2,6 +2,7 @@ Template Attributes: * CommitStatus: summary of all commit status state * CommitStatuses: all commit status elements +* MissingRequiredChecks: commit check contexts that are required by branch protection but not present * ShowHideChecks: whether use a button to show/hide the checks * is_context_required: Used in pull request commit status check table */}} @@ -9,7 +10,7 @@ Template Attributes: {{if .CommitStatus}}
    - {{if eq .CommitStatus.State "pending"}} + {{if or (eq .CommitStatus.State "pending") (.MissingRequiredChecks)}} {{ctx.Locale.Tr "repo.pulls.status_checking"}} {{else if eq .CommitStatus.State "success"}} {{ctx.Locale.Tr "repo.pulls.status_checks_success"}} @@ -46,6 +47,13 @@ Template Attributes:
    {{end}} + {{range .MissingRequiredChecks}} +
    + {{svg "octicon-dot-fill" 18 "commit-status icon text yellow"}} +
    {{.}}
    +
    {{ctx.Locale.Tr "repo.pulls.status_checks_requested"}}
    +
    + {{end}}
    {{end}} From 369fe5696697cef33a188d9b985ac4b9824a4bdf Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Mon, 19 Feb 2024 11:27:05 +0100 Subject: [PATCH 121/271] Show commit status for releases (#29149) Fixes #29082 ![grafik](https://github.com/go-gitea/gitea/assets/1666336/bb2ccde1-ee99-459d-9e74-0fb8ea79e8b3) (cherry picked from commit 7e8ff709401d09467c3eee7c69cd9600d26a97a3) --- routers/web/repo/release.go | 206 ++++++++++++++-------------- services/actions/commit_status.go | 3 + templates/repo/commit_statuses.tmpl | 4 +- templates/repo/release/list.tmpl | 152 ++++++++++---------- 4 files changed, 184 insertions(+), 181 deletions(-) diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 4d139f2b79..33e7f739ff 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/models" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" @@ -67,6 +68,88 @@ func calReleaseNumCommitsBehind(repoCtx *context.Repository, release *repo_model return nil } +type ReleaseInfo struct { + Release *repo_model.Release + CommitStatus *git_model.CommitStatus + CommitStatuses []*git_model.CommitStatus +} + +func getReleaseInfos(ctx *context.Context, opts *repo_model.FindReleasesOptions) ([]*ReleaseInfo, error) { + releases, err := db.Find[repo_model.Release](ctx, opts) + if err != nil { + return nil, err + } + + for _, release := range releases { + release.Repo = ctx.Repo.Repository + } + + if err = repo_model.GetReleaseAttachments(ctx, releases...); err != nil { + return nil, err + } + + // Temporary cache commits count of used branches to speed up. + countCache := make(map[string]int64) + cacheUsers := make(map[int64]*user_model.User) + if ctx.Doer != nil { + cacheUsers[ctx.Doer.ID] = ctx.Doer + } + var ok bool + + canReadActions := ctx.Repo.CanRead(unit.TypeActions) + + releaseInfos := make([]*ReleaseInfo, 0, len(releases)) + for _, r := range releases { + if r.Publisher, ok = cacheUsers[r.PublisherID]; !ok { + r.Publisher, err = user_model.GetUserByID(ctx, r.PublisherID) + if err != nil { + if user_model.IsErrUserNotExist(err) { + r.Publisher = user_model.NewGhostUser() + } else { + return nil, err + } + } + cacheUsers[r.PublisherID] = r.Publisher + } + + r.Note, err = markdown.RenderString(&markup.RenderContext{ + Links: markup.Links{ + Base: ctx.Repo.RepoLink, + }, + Metas: ctx.Repo.Repository.ComposeMetas(ctx), + GitRepo: ctx.Repo.GitRepo, + Ctx: ctx, + }, r.Note) + if err != nil { + return nil, err + } + + if !r.IsDraft { + if err := calReleaseNumCommitsBehind(ctx.Repo, r, countCache); err != nil { + return nil, err + } + } + + info := &ReleaseInfo{ + Release: r, + } + + if canReadActions { + statuses, _, err := git_model.GetLatestCommitStatus(ctx, r.Repo.ID, r.Sha1, db.ListOptions{ListAll: true}) + if err != nil { + return nil, err + } + + info.CommitStatus = git_model.CalcCommitStatus(statuses) + info.CommitStatuses = statuses + } + + releaseInfos = append(releaseInfos, info) + } + + return releaseInfos, nil +} + // Releases render releases list page func Releases(ctx *context.Context) { ctx.Data["PageIsReleaseList"] = true @@ -91,77 +174,21 @@ func Releases(ctx *context.Context) { writeAccess := ctx.Repo.CanWrite(unit.TypeReleases) ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived - opts := repo_model.FindReleasesOptions{ + releases, err := getReleaseInfos(ctx, &repo_model.FindReleasesOptions{ ListOptions: listOptions, // only show draft releases for users who can write, read-only users shouldn't see draft releases. IncludeDrafts: writeAccess, RepoID: ctx.Repo.Repository.ID, - } - - releases, err := db.Find[repo_model.Release](ctx, opts) + }) if err != nil { - ctx.ServerError("GetReleasesByRepoID", err) + ctx.ServerError("getReleaseInfos", err) return } - for _, release := range releases { - release.Repo = ctx.Repo.Repository - } - - if err = repo_model.GetReleaseAttachments(ctx, releases...); err != nil { - ctx.ServerError("GetReleaseAttachments", err) - return - } - - // Temporary cache commits count of used branches to speed up. - countCache := make(map[string]int64) - cacheUsers := make(map[int64]*user_model.User) - if ctx.Doer != nil { - cacheUsers[ctx.Doer.ID] = ctx.Doer - } - var ok bool - - for _, r := range releases { - if r.Publisher, ok = cacheUsers[r.PublisherID]; !ok { - r.Publisher, err = user_model.GetUserByID(ctx, r.PublisherID) - if err != nil { - if user_model.IsErrUserNotExist(err) { - r.Publisher = user_model.NewGhostUser() - } else { - ctx.ServerError("GetUserByID", err) - return - } - } - cacheUsers[r.PublisherID] = r.Publisher - } - - r.Note, err = markdown.RenderString(&markup.RenderContext{ - Links: markup.Links{ - Base: ctx.Repo.RepoLink, - }, - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, - }, r.Note) - if err != nil { - ctx.ServerError("RenderString", err) - return - } - - if r.IsDraft { - continue - } - - if err := calReleaseNumCommitsBehind(ctx.Repo, r, countCache); err != nil { - ctx.ServerError("calReleaseNumCommitsBehind", err) - return - } - } - ctx.Data["Releases"] = releases numReleases := ctx.Data["NumReleases"].(int64) - pager := context.NewPagination(int(numReleases), opts.PageSize, opts.Page, 5) + pager := context.NewPagination(int(numReleases), listOptions.PageSize, listOptions.Page, 5) pager.SetDefaultParams(ctx) ctx.Data["Page"] = pager @@ -249,15 +276,24 @@ func SingleRelease(ctx *context.Context) { writeAccess := ctx.Repo.CanWrite(unit.TypeReleases) ctx.Data["CanCreateRelease"] = writeAccess && !ctx.Repo.Repository.IsArchived - release, err := repo_model.GetRelease(ctx, ctx.Repo.Repository.ID, ctx.Params("*")) + releases, err := getReleaseInfos(ctx, &repo_model.FindReleasesOptions{ + ListOptions: db.ListOptions{Page: 1, PageSize: 1}, + RepoID: ctx.Repo.Repository.ID, + TagNames: []string{ctx.Params("*")}, + // only show draft releases for users who can write, read-only users shouldn't see draft releases. + IncludeDrafts: writeAccess, + }) if err != nil { - if repo_model.IsErrReleaseNotExist(err) { - ctx.NotFound("GetRelease", err) - return - } - ctx.ServerError("GetReleasesByRepoID", err) + ctx.ServerError("getReleaseInfos", err) return } + if len(releases) != 1 { + ctx.NotFound("SingleRelease", err) + return + } + + release := releases[0].Release + ctx.Data["PageIsSingleTag"] = release.IsTag if release.IsTag { ctx.Data["Title"] = release.TagName @@ -265,43 +301,7 @@ func SingleRelease(ctx *context.Context) { ctx.Data["Title"] = release.Title } - release.Repo = ctx.Repo.Repository - - err = repo_model.GetReleaseAttachments(ctx, release) - if err != nil { - ctx.ServerError("GetReleaseAttachments", err) - return - } - - release.Publisher, err = user_model.GetUserByID(ctx, release.PublisherID) - if err != nil { - if user_model.IsErrUserNotExist(err) { - release.Publisher = user_model.NewGhostUser() - } else { - ctx.ServerError("GetUserByID", err) - return - } - } - if !release.IsDraft { - if err := calReleaseNumCommitsBehind(ctx.Repo, release, make(map[string]int64)); err != nil { - ctx.ServerError("calReleaseNumCommitsBehind", err) - return - } - } - release.Note, err = markdown.RenderString(&markup.RenderContext{ - Links: markup.Links{ - Base: ctx.Repo.RepoLink, - }, - Metas: ctx.Repo.Repository.ComposeMetas(ctx), - GitRepo: ctx.Repo.GitRepo, - Ctx: ctx, - }, release.Note) - if err != nil { - ctx.ServerError("RenderString", err) - return - } - - ctx.Data["Releases"] = []*repo_model.Release{release} + ctx.Data["Releases"] = releases ctx.HTML(http.StatusOK, tplReleasesList) } diff --git a/services/actions/commit_status.go b/services/actions/commit_status.go index 72a3ab7ac6..edd1fd1568 100644 --- a/services/actions/commit_status.go +++ b/services/actions/commit_status.go @@ -64,6 +64,9 @@ func createCommitStatus(ctx context.Context, job *actions_model.ActionRunJob) er return fmt.Errorf("head of pull request is missing in event payload") } sha = payload.PullRequest.Head.Sha + case webhook_module.HookEventRelease: + event = string(run.Event) + sha = run.CommitSHA default: return nil } diff --git a/templates/repo/commit_statuses.tmpl b/templates/repo/commit_statuses.tmpl index ec2be6c38d..74c20a6a2c 100644 --- a/templates/repo/commit_statuses.tmpl +++ b/templates/repo/commit_statuses.tmpl @@ -1,10 +1,10 @@ {{if .Statuses}} {{if and (eq (len .Statuses) 1) .Status.TargetURL}} - + {{template "repo/commit_status" .Status}} {{else}} - + {{template "repo/commit_status" .Status}} {{end}} diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index fb2fce2950..6dbeb741db 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -5,90 +5,90 @@ {{template "base/alert" .}} {{template "repo/release_tag_header" .}}
      - {{range $idx, $release := .Releases}} + {{range $idx, $info := .Releases}} + {{$release := $info.Release}}
    • - {{svg "octicon-tag" 16 "gt-mr-2"}}{{.TagName}} - {{if and .Sha1 ($.Permission.CanRead $.UnitTypeCode)}} - {{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha .Sha1}} - {{template "repo/branch_dropdown" dict "root" $ "release" .}} - {{end}} + {{svg "octicon-tag" 16 "gt-mr-2"}}{{$release.TagName}} + {{if and $release.Sha1 ($.Permission.CanRead $.UnitTypeCode)}} + {{svg "octicon-git-commit" 16 "gt-mr-2"}}{{ShortSha $release.Sha1}} + {{template "repo/branch_dropdown" dict "root" $ "release" $release}} + {{end}}
      -
      -

      - {{.Title}} - {{if .IsDraft}} - {{ctx.Locale.Tr "repo.release.draft"}} - {{else if .IsPrerelease}} - {{ctx.Locale.Tr "repo.release.prerelease"}} - {{else}} - {{ctx.Locale.Tr "repo.release.stable"}} - {{end}} -

      -
      - {{if $.CanCreateRelease}} - - {{svg "octicon-pencil"}} - - {{end}} -
      -
      -

      - - {{if .OriginalAuthor}} - {{svg (MigrationIcon .Repo.GetOriginalURLHostname) 20 "gt-mr-2"}}{{.OriginalAuthor}} - {{else if .Publisher}} - {{ctx.AvatarUtils.Avatar .Publisher 20 "gt-mr-2"}} - {{.Publisher.GetDisplayName}} +

      +

      + {{$release.Title}} + {{template "repo/commit_statuses" dict "Status" $info.CommitStatus "Statuses" $info.CommitStatuses "AdditionalClasses" "gt-df"}} + {{if $release.IsDraft}} + {{ctx.Locale.Tr "repo.release.draft"}} + {{else if $release.IsPrerelease}} + {{ctx.Locale.Tr "repo.release.prerelease"}} {{else}} - Ghost + {{ctx.Locale.Tr "repo.release.stable"}} {{end}} - - - {{ctx.Locale.Tr "repo.released_this"}} - - {{if .CreatedUnix}} - {{TimeSinceUnix .CreatedUnix ctx.Locale}} +

      +
      + {{if $.CanCreateRelease}} + + {{svg "octicon-pencil"}} + {{end}} - {{if and (not .IsDraft) ($.Permission.CanRead $.UnitTypeCode)}} - | {{ctx.Locale.Tr "repo.release.ahead.commits" .NumCommitsBehind | Str2html}} {{ctx.Locale.Tr "repo.release.ahead.target" .TargetBehind}} - {{end}} -

      -
      - {{Str2html .Note}}
      -
      -
      - - {{ctx.Locale.Tr "repo.release.downloads"}} - - -
      +
      +

      + + {{if $release.OriginalAuthor}} + {{svg (MigrationIcon $release.Repo.GetOriginalURLHostname) 20 "gt-mr-2"}}{{$release.OriginalAuthor}} + {{else if $release.Publisher}} + {{ctx.AvatarUtils.Avatar $release.Publisher 20 "gt-mr-2"}} + {{$release.Publisher.GetDisplayName}} + {{else}} + Ghost + {{end}} + + + {{ctx.Locale.Tr "repo.released_this"}} + + {{if $release.CreatedUnix}} + {{TimeSinceUnix $release.CreatedUnix ctx.Locale}} + {{end}} + {{if and (not $release.IsDraft) ($.Permission.CanRead $.UnitTypeCode)}} + | {{ctx.Locale.Tr "repo.release.ahead.commits" $release.NumCommitsBehind | Str2html}} {{ctx.Locale.Tr "repo.release.ahead.target" $release.TargetBehind}} + {{end}} +

      +
      + {{Str2html $release.Note}} +
      +
      +
      + + {{ctx.Locale.Tr "repo.release.downloads"}} + + +
    • From 5fffab8d9d4f1202d9fa6f6da548bf2f4808b5a9 Mon Sep 17 00:00:00 2001 From: Johan Van de Wauw Date: Mon, 19 Feb 2024 11:51:58 +0100 Subject: [PATCH 122/271] Fix c/p error in inline documentation (#29148) Fix small copy/paste error in inline documentation --------- Co-authored-by: wxiaoguang (cherry picked from commit 740c6a226c4df26432641018fbfd9186977d573f) --- services/auth/source/db/source.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/auth/source/db/source.go b/services/auth/source/db/source.go index 50eae27439..bb2270cbd6 100644 --- a/services/auth/source/db/source.go +++ b/services/auth/source/db/source.go @@ -18,7 +18,7 @@ func (source *Source) FromDB(bs []byte) error { return nil } -// ToDB exports an SMTPConfig to a serialized format. +// ToDB exports the config to a byte slice to be saved into database (this method is just dummy and does nothing for DB source) func (source *Source) ToDB() ([]byte, error) { return nil, nil } From c7c20ff5ab6dd84eed79115c00c3f230963cc105 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Mon, 19 Feb 2024 19:25:58 +0800 Subject: [PATCH 123/271] Remove DataRaceCheck (#29258) Since #26254, it started using `{{ctx.Locale.Tr ...}}` Now the `ctx` seems stable enough, so the check could be removed. (cherry picked from commit 567a68a0bf78c8d70f08c8ab948fdbb455225aa9) --- modules/context/context_template.go | 14 -------------- templates/base/footer.tmpl | 1 - templates/base/head.tmpl | 1 - 3 files changed, 16 deletions(-) diff --git a/modules/context/context_template.go b/modules/context/context_template.go index ba90fc170a..7878d409ca 100644 --- a/modules/context/context_template.go +++ b/modules/context/context_template.go @@ -5,10 +5,7 @@ package context import ( "context" - "errors" "time" - - "code.gitea.io/gitea/modules/log" ) var _ context.Context = TemplateContext(nil) @@ -36,14 +33,3 @@ func (c TemplateContext) Err() error { func (c TemplateContext) Value(key any) any { return c.parentContext().Value(key) } - -// DataRaceCheck checks whether the template context function "ctx()" returns the consistent context -// as the current template's rendering context (request context), to help to find data race issues as early as possible. -// When the code is proven to be correct and stable, this function should be removed. -func (c TemplateContext) DataRaceCheck(dataCtx context.Context) (string, error) { - if c.parentContext() != dataCtx { - log.Error("TemplateContext.DataRaceCheck: parent context mismatch\n%s", log.Stack(2)) - return "", errors.New("parent context mismatch") - } - return "", nil -} diff --git a/templates/base/footer.tmpl b/templates/base/footer.tmpl index d65a3626a4..fed426a469 100644 --- a/templates/base/footer.tmpl +++ b/templates/base/footer.tmpl @@ -16,6 +16,5 @@ {{template "custom/footer" .}} - {{ctx.DataRaceCheck $.Context}} diff --git a/templates/base/head.tmpl b/templates/base/head.tmpl index d865d58b8e..a202ad7dd1 100644 --- a/templates/base/head.tmpl +++ b/templates/base/head.tmpl @@ -31,7 +31,6 @@ {{template "custom/header" .}} - {{ctx.DataRaceCheck $.Context}} {{template "custom/body_outer_pre" .}}
      From 8125fe063425a1a3d8c52a8ab3481af3ef1d5440 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Eahin=20Akkaya?= Date: Mon, 19 Feb 2024 15:47:38 +0300 Subject: [PATCH 124/271] Deduplicate translations for contributors graph (#29256) I have implemented three graph pages ([contributors](https://github.com/go-gitea/gitea/pull/27882), [code frequency](https://github.com/go-gitea/gitea/pull/29191) and [recent commits](https://github.com/go-gitea/gitea/pull/29210)) and they have all same page title as the tab name so I decided to use same translations for them. This PR is for contributors graph. Other PR's have their own respective commits. (cherry picked from commit 39a77d92d9677b0a0049cb8696960d6d2ac052d6) --- options/locale/locale_en-US.ini | 3 +-- routers/web/repo/contributors.go | 2 +- templates/repo/contributors.tmpl | 4 ++-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 3700017330..bdae9a29ac 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2023,12 +2023,10 @@ activity.git_stats_and_deletions = and activity.git_stats_deletion_1 = %d deletion activity.git_stats_deletion_n = %d deletions -contributors = Contributors contributors.contribution_type.filter_label = Contribution type: contributors.contribution_type.commits = Commits contributors.contribution_type.additions = Additions contributors.contribution_type.deletions = Deletions -contributors.what = contributions search = Search search.search_repo = Search repository @@ -2654,6 +2652,7 @@ component_loading = Loading %s... component_loading_failed = Could not load %s component_loading_info = This might take a bit… component_failed_to_load = An unexpected error happened. +contributors.what = contributions [org] org_name_holder = Organization Name diff --git a/routers/web/repo/contributors.go b/routers/web/repo/contributors.go index f7dedc0b34..bcfef7580a 100644 --- a/routers/web/repo/contributors.go +++ b/routers/web/repo/contributors.go @@ -18,7 +18,7 @@ const ( // Contributors render the page to show repository contributors graph func Contributors(ctx *context.Context) { - ctx.Data["Title"] = ctx.Tr("repo.contributors") + ctx.Data["Title"] = ctx.Tr("repo.activity.navbar.contributors") ctx.Data["PageIsActivity"] = true ctx.Data["PageIsContributors"] = true diff --git a/templates/repo/contributors.tmpl b/templates/repo/contributors.tmpl index 3bd343197b..4a258e5b70 100644 --- a/templates/repo/contributors.tmpl +++ b/templates/repo/contributors.tmpl @@ -4,8 +4,8 @@ data-locale-contribution-type-commits="{{ctx.Locale.Tr "repo.contributors.contribution_type.commits"}}" data-locale-contribution-type-additions="{{ctx.Locale.Tr "repo.contributors.contribution_type.additions"}}" data-locale-contribution-type-deletions="{{ctx.Locale.Tr "repo.contributors.contribution_type.deletions"}}" - data-locale-loading-title="{{ctx.Locale.Tr "graphs.component_loading" (ctx.Locale.Tr "repo.contributors.what")}}" - data-locale-loading-title-failed="{{ctx.Locale.Tr "graphs.component_loading_failed" (ctx.Locale.Tr "repo.contributors.what")}}" + data-locale-loading-title="{{ctx.Locale.Tr "graphs.component_loading" (ctx.Locale.Tr "graphs.contributors.what")}}" + data-locale-loading-title-failed="{{ctx.Locale.Tr "graphs.component_loading_failed" (ctx.Locale.Tr "graphs.contributors.what")}}" data-locale-loading-info="{{ctx.Locale.Tr "graphs.component_loading_info"}}" data-locale-component-failed-to-load="{{ctx.Locale.Tr "graphs.component_failed_to_load"}}" > From 51fb6f3983f15aa3c2db1feadcc13db1692315ec Mon Sep 17 00:00:00 2001 From: Shiny Nematoda Date: Tue, 20 Feb 2024 11:05:42 +0000 Subject: [PATCH 125/271] [FEAT] add fallback repo search using git grep --- routers/web/repo/search.go | 58 +++++++++------ services/repository/files/search.go | 90 ++++++++++++++++++++++++ services/repository/files/search_test.go | 48 +++++++++++++ templates/repo/home.tmpl | 30 ++++---- templates/repo/search.tmpl | 20 +++--- tests/integration/repo_search_test.go | 63 ++++++++++++----- 6 files changed, 246 insertions(+), 63 deletions(-) create mode 100644 services/repository/files/search.go create mode 100644 services/repository/files/search_test.go diff --git a/routers/web/repo/search.go b/routers/web/repo/search.go index 3c0fa4bc00..29b3b7b476 100644 --- a/routers/web/repo/search.go +++ b/routers/web/repo/search.go @@ -10,17 +10,13 @@ import ( "code.gitea.io/gitea/modules/context" code_indexer "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/services/repository/files" ) const tplSearch base.TplName = "repo/search" // Search render repository search page func Search(ctx *context.Context) { - if !setting.Indexer.RepoIndexerEnabled { - ctx.Redirect(ctx.Repo.RepoLink) - return - } - language := ctx.FormTrim("l") keyword := ctx.FormTrim("q") @@ -37,31 +33,49 @@ func Search(ctx *context.Context) { return } + ctx.Data["SourcePath"] = ctx.Repo.Repository.Link() + page := ctx.FormInt("page") if page <= 0 { page = 1 } - total, searchResults, searchResultLanguages, err := code_indexer.PerformSearch(ctx, []int64{ctx.Repo.Repository.ID}, - language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) - if err != nil { - if code_indexer.IsAvailable(ctx) { - ctx.ServerError("SearchResults", err) + if setting.Indexer.RepoIndexerEnabled { + ctx.Data["CodeIndexerEnabled"] = true + + total, searchResults, searchResultLanguages, err := code_indexer.PerformSearch(ctx, []int64{ctx.Repo.Repository.ID}, + language, keyword, page, setting.UI.RepoSearchPagingNum, isMatch) + if err != nil { + if code_indexer.IsAvailable(ctx) { + ctx.ServerError("SearchResults", err) + return + } + ctx.Data["CodeIndexerUnavailable"] = true + } else { + ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx) + } + + ctx.Data["SearchResults"] = searchResults + ctx.Data["SearchResultLanguages"] = searchResultLanguages + + pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) + pager.SetDefaultParams(ctx) + pager.AddParam(ctx, "l", "Language") + ctx.Data["Page"] = pager + } else { + data, err := files.NewRepoGrep(ctx, ctx.Repo.Repository, keyword) + if err != nil { + ctx.ServerError("NewRepoGrep", err) return } - ctx.Data["CodeIndexerUnavailable"] = true - } else { - ctx.Data["CodeIndexerUnavailable"] = !code_indexer.IsAvailable(ctx) + + ctx.Data["CodeIndexerEnabled"] = false + ctx.Data["SearchResults"] = data + + pager := context.NewPagination(len(data), setting.UI.RepoSearchPagingNum, page, 5) + pager.SetDefaultParams(ctx) + ctx.Data["Page"] = pager } - ctx.Data["SourcePath"] = ctx.Repo.Repository.Link() - ctx.Data["SearchResults"] = searchResults - ctx.Data["SearchResultLanguages"] = searchResultLanguages - - pager := context.NewPagination(total, setting.UI.RepoSearchPagingNum, page, 5) - pager.SetDefaultParams(ctx) - pager.AddParam(ctx, "l", "Language") - ctx.Data["Page"] = pager - ctx.HTML(http.StatusOK, tplSearch) } diff --git a/services/repository/files/search.go b/services/repository/files/search.go new file mode 100644 index 0000000000..f8317c4892 --- /dev/null +++ b/services/repository/files/search.go @@ -0,0 +1,90 @@ +package files + +import ( + "context" + "html/template" + "strconv" + "strings" + + repo_model "code.gitea.io/gitea/models/repo" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/highlight" + "code.gitea.io/gitea/modules/timeutil" + + "github.com/go-enry/go-enry/v2" +) + +type Result struct { + RepoID int64 // ignored + Filename string + CommitID string // branch + UpdatedUnix timeutil.TimeStamp // ignored + Language string + Color string + LineNumbers []int64 + FormattedLines template.HTML +} + +const pHEAD = "HEAD:" + +func NewRepoGrep(ctx context.Context, repo *repo_model.Repository, keyword string) ([]*Result, error) { + t, _, err := gitrepo.RepositoryFromContextOrOpen(ctx, repo) + if err != nil { + return nil, err + } + + data := []*Result{} + + stdout, _, err := git.NewCommand(ctx, + "grep", + "-1", // n before and after lines + "-z", + "--heading", + "--break", // easier parsing + "--fixed-strings", // disallow regex for now + "-n", // line nums + "-i", // ignore case + "--full-name", // full file path, rel to repo + //"--column", // for adding better highlighting support + ). + AddDynamicArguments(keyword). + AddArguments("HEAD"). + RunStdString(&git.RunOpts{Dir: t.Path}) + if err != nil { + return data, nil // non zero exit code when there are no results + } + + for _, block := range strings.Split(stdout, "\n\n") { + res := Result{CommitID: repo.DefaultBranch} + code := []string{} + + for _, line := range strings.Split(block, "\n") { + if strings.HasPrefix(line, pHEAD) { + res.Filename = strings.TrimPrefix(line, pHEAD) + continue + } + + if ln, after, ok := strings.Cut(line, "\x00"); ok { + i, err := strconv.ParseInt(ln, 10, 64) + if err != nil { + continue + } + + res.LineNumbers = append(res.LineNumbers, i) + code = append(code, after) + } + } + + if res.Filename == "" || len(code) == 0 || len(res.LineNumbers) == 0 { + continue + } + + res.FormattedLines, res.Language = highlight.Code(res.Filename, "", strings.Join(code, "\n")) + res.Color = enry.GetColor(res.Language) + + data = append(data, &res) + } + + return data, nil +} diff --git a/services/repository/files/search_test.go b/services/repository/files/search_test.go new file mode 100644 index 0000000000..c24bb731a8 --- /dev/null +++ b/services/repository/files/search_test.go @@ -0,0 +1,48 @@ +package files + +import ( + "testing" + + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/contexttest" + + "github.com/stretchr/testify/assert" +) + +func TestNewRepoGrep(t *testing.T) { + unittest.PrepareTestEnv(t) + ctx, _ := contexttest.MockContext(t, "user2/repo1") + ctx.SetParams(":id", "1") + contexttest.LoadRepo(t, ctx, 1) + contexttest.LoadRepoCommit(t, ctx) + contexttest.LoadUser(t, ctx, 2) + contexttest.LoadGitRepo(t, ctx) + defer ctx.Repo.GitRepo.Close() + + t.Run("with result", func(t *testing.T) { + res, err := NewRepoGrep(ctx, ctx.Repo.Repository, "Description") + assert.NoError(t, err) + + expected := []*Result{ + { + RepoID: 0, + Filename: "README.md", + CommitID: "master", + UpdatedUnix: 0, + Language: "Markdown", + Color: "#083fa1", + LineNumbers: []int64{2, 3}, + FormattedLines: "\nDescription for repo1", + }, + } + + assert.EqualValues(t, res, expected) + }) + + t.Run("empty result", func(t *testing.T) { + res, err := NewRepoGrep(ctx, ctx.Repo.Repository, "keyword that does not match in the repo") + assert.NoError(t, err) + + assert.EqualValues(t, res, []*Result{}) + }) +} diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index 5e27d9160c..9bac26ce1e 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -11,23 +11,21 @@ {{if $description}}{{$description | RenderCodeBlock}}{{else if .IsRepositoryAdmin}}{{ctx.Locale.Tr "repo.no_desc"}}{{end}} {{.Repository.Website}}
      - {{if .RepoSearchEnabled}} -
      {{range .Topics}}{{.Name}}{{end}} diff --git a/templates/repo/search.tmpl b/templates/repo/search.tmpl index b616b4de32..3b5c212af3 100644 --- a/templates/repo/search.tmpl +++ b/templates/repo/search.tmpl @@ -6,14 +6,16 @@
      - @@ -41,7 +43,7 @@

      {{.Filename}} - {{ctx.Locale.Tr "repo.diff.view_file"}} + {{ctx.Locale.Tr "repo.diff.view_file"}}

      @@ -50,7 +52,7 @@ {{range .LineNumbers}} - {{.}} + {{.}} {{end}} {{.FormattedLines}} diff --git a/tests/integration/repo_search_test.go b/tests/integration/repo_search_test.go index cf199e98c2..e5ee334ce8 100644 --- a/tests/integration/repo_search_test.go +++ b/tests/integration/repo_search_test.go @@ -11,14 +11,15 @@ import ( repo_model "code.gitea.io/gitea/models/repo" code_indexer "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/tests" "github.com/PuerkitoBio/goquery" "github.com/stretchr/testify/assert" ) -func resultFilenames(t testing.TB, doc *HTMLDoc) []string { - filenameSelections := doc.doc.Find(".repository.search").Find(".repo-search-result").Find(".header").Find("span.file") +func resultFilenames(t testing.TB, doc *goquery.Selection) []string { + filenameSelections := doc.Find(".header").Find("span.file") result := make([]string, filenameSelections.Length()) filenameSelections.Each(func(i int, selection *goquery.Selection) { result[i] = selection.Text() @@ -26,36 +27,66 @@ func resultFilenames(t testing.TB, doc *HTMLDoc) []string { return result } -func TestSearchRepo(t *testing.T) { +func checkResultLinks(t *testing.T, substr string, doc *goquery.Selection) { + t.Helper() + linkSelections := doc.Find("a[href]") + linkSelections.Each(func(i int, selection *goquery.Selection) { + assert.Contains(t, selection.AttrOr("href", ""), substr) + }) +} + +func testSearchRepo(t *testing.T, useExternalIndexer bool) { defer tests.PrepareTestEnv(t)() + defer test.MockVariableValue(&setting.Indexer.RepoIndexerEnabled, useExternalIndexer)() repo, err := repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "repo1") assert.NoError(t, err) - executeIndexer(t, repo, code_indexer.UpdateRepoIndexer) + gitReference := "/branch/" + repo.DefaultBranch - testSearch(t, "/user2/repo1/search?q=Description&page=1", []string{"README.md"}) + if useExternalIndexer { + gitReference = "/commit/" + executeIndexer(t, repo, code_indexer.UpdateRepoIndexer) + } - setting.Indexer.IncludePatterns = setting.IndexerGlobFromString("**.txt") - setting.Indexer.ExcludePatterns = setting.IndexerGlobFromString("**/y/**") + testSearch(t, "/user2/repo1/search?q=Description&page=1", gitReference, []string{"README.md"}) - repo, err = repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "glob") - assert.NoError(t, err) + if useExternalIndexer { + setting.Indexer.IncludePatterns = setting.IndexerGlobFromString("**.txt") + setting.Indexer.ExcludePatterns = setting.IndexerGlobFromString("**/y/**") - executeIndexer(t, repo, code_indexer.UpdateRepoIndexer) + repo, err = repo_model.GetRepositoryByOwnerAndName(db.DefaultContext, "user2", "glob") + assert.NoError(t, err) - testSearch(t, "/user2/glob/search?q=loren&page=1", []string{"a.txt"}) - testSearch(t, "/user2/glob/search?q=file3&page=1", []string{"x/b.txt"}) - testSearch(t, "/user2/glob/search?q=file4&page=1", []string{}) - testSearch(t, "/user2/glob/search?q=file5&page=1", []string{}) + executeIndexer(t, repo, code_indexer.UpdateRepoIndexer) + + testSearch(t, "/user2/glob/search?q=loren&page=1", gitReference, []string{"a.txt"}) + testSearch(t, "/user2/glob/search?q=file3&page=1", gitReference, []string{"x/b.txt"}) + testSearch(t, "/user2/glob/search?q=file4&page=1", gitReference, []string{}) + testSearch(t, "/user2/glob/search?q=file5&page=1", gitReference, []string{}) + } } -func testSearch(t *testing.T, url string, expected []string) { +func TestIndexerSearchRepo(t *testing.T) { + testSearchRepo(t, true) +} + +func TestNoIndexerSearchRepo(t *testing.T) { + testSearchRepo(t, false) +} + +func testSearch(t *testing.T, url, gitRef string, expected []string) { req := NewRequest(t, "GET", url) resp := MakeRequest(t, req, http.StatusOK) - filenames := resultFilenames(t, NewHTMLParser(t, resp.Body)) + doc := NewHTMLParser(t, resp.Body).doc. + Find(".repository.search"). + Find(".repo-search-result") + + filenames := resultFilenames(t, doc) assert.EqualValues(t, expected, filenames) + + checkResultLinks(t, gitRef, doc) } func executeIndexer(t *testing.T, repo *repo_model.Repository, op func(*repo_model.Repository)) { From 815abad84c68da1722f87c97a47b0e96a29f3967 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 20 Feb 2024 19:38:21 +0100 Subject: [PATCH 126/271] [BUG] Initalize Git for hook regeneration - The hook regeneration code relies on `git.SupportProcReceive` being set to determine if the `proc-receive` hook should be written, this variable is set when the git module is initialized. - Resolves #2414 --- cmd/admin_regenerate.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/cmd/admin_regenerate.go b/cmd/admin_regenerate.go index 0db505ff9c..efdfc8e5e4 100644 --- a/cmd/admin_regenerate.go +++ b/cmd/admin_regenerate.go @@ -5,6 +5,7 @@ package cmd import ( asymkey_model "code.gitea.io/gitea/models/asymkey" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/graceful" repo_service "code.gitea.io/gitea/services/repository" @@ -32,6 +33,12 @@ func runRegenerateHooks(_ *cli.Context) error { if err := initDB(ctx); err != nil { return err } + + // Detection of ProcReceive support relies on Git module being initalized. + if err := git.InitFull(ctx); err != nil { + return err + } + return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext()) } From c6d366e2832d21ef68ac253885fd6e983f2fb84e Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 21 Feb 2024 12:19:15 +0100 Subject: [PATCH 127/271] [THEMES] Port console colors - Port 1fd7e3d6bea0453b851afec6c7f74b7cf7b10a06 to the Forgejo themes, they are a copy paste, but have a bit darker console background color to have better contrast and match better with the overal Forgejo dark theme's shade. --- web_src/css/themes/theme-forgejo-dark.css | 9 +++++++++ web_src/css/themes/theme-forgejo-light.css | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/web_src/css/themes/theme-forgejo-dark.css b/web_src/css/themes/theme-forgejo-dark.css index decd3497d7..d09e9e3a63 100644 --- a/web_src/css/themes/theme-forgejo-dark.css +++ b/web_src/css/themes/theme-forgejo-dark.css @@ -75,6 +75,15 @@ --color-secondary-alpha-90: #2B3642e1; --color-secondary-hover: var(--color-secondary-light-1); --color-secondary-active: var(--color-secondary-light-2); + /* console colors - used for actions console and console files */ + --color-console-fg: #eeeff2; + --color-console-fg-subtle: #959cab; + --color-console-bg: #1f212b; + --color-console-border: #383c47; + --color-console-hover-bg: #ffffff16; + --color-console-active-bg: #454a57; + --color-console-menu-bg: #383c47; + --color-console-menu-border: #5c6374; /* colors */ --color-red: #b91c1c; --color-orange: #ea580c; diff --git a/web_src/css/themes/theme-forgejo-light.css b/web_src/css/themes/theme-forgejo-light.css index 495b8ce431..4182e9d719 100644 --- a/web_src/css/themes/theme-forgejo-light.css +++ b/web_src/css/themes/theme-forgejo-light.css @@ -93,6 +93,15 @@ --color-secondary-alpha-90: #d4d4d8e1; --color-secondary-hover: var(--color-secondary-dark-2); --color-secondary-active: var(--color-secondary-dark-4); + /* console colors - used for actions console and console files */ + --color-console-fg: #eeeff2; + --color-console-fg-subtle: #959cab; + --color-console-bg: #1f212b; + --color-console-border: #383c47; + --color-console-hover-bg: #ffffff16; + --color-console-active-bg: #454a57; + --color-console-menu-bg: #383c47; + --color-console-menu-border: #5c6374; /* colors */ --color-red: #dc2626; --color-orange: #ea580c; From 6fbfe441decc018a122b0b66185002db92b404a2 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 21 Feb 2024 12:42:12 +0100 Subject: [PATCH 128/271] [BUG] Load `AllUnitsEnabled` when necessary - In order to determine if the "Add more..." tab should be shown, the template has to know if the repository has all units enabled, this is done in the repository header which can be shown for quite a lot of pages (code, issues, projects, actions etc.) - This was previously set in the `RepoRefByType` function, which would be called by pages such as code, issues and releases, but it was not being called for all pages such as actions, packages and wiki. Which would in turn incorrectly show the "Add more..." button when it shouldn't. - Now call it from the template itself, so the value is 'always' loaded when necessary. --- models/repo/repo.go | 25 +++++++++++++++++++++++++ modules/context/repo.go | 26 -------------------------- templates/repo/header.tmpl | 2 +- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/models/repo/repo.go b/models/repo/repo.go index a7bc4b3c72..b24e5c1dbf 100644 --- a/models/repo/repo.go +++ b/models/repo/repo.go @@ -439,6 +439,31 @@ func (repo *Repository) GetUnit(ctx context.Context, tp unit.Type) (*RepoUnit, e return nil, ErrUnitTypeNotExist{tp} } +// AllUnitsEnabled returns true if all units are enabled for the repo. +func (repo *Repository) AllUnitsEnabled(ctx context.Context) bool { + hasAnyUnitEnabled := func(unitGroup []unit.Type) bool { + // Loop over the group of units + for _, unit := range unitGroup { + // If *any* of them is enabled, return true. + if repo.UnitEnabled(ctx, unit) { + return true + } + } + + // If none are enabled, return false. + return false + } + + for _, unitGroup := range unit.AllowedRepoUnitGroups { + // If any disabled unit is found, return false immediately. + if !hasAnyUnitEnabled(unitGroup) { + return false + } + } + + return true +} + // LoadOwner loads owner user func (repo *Repository) LoadOwner(ctx context.Context) (err error) { if repo.Owner != nil { diff --git a/modules/context/repo.go b/modules/context/repo.go index 8e8a42b695..9d63f9eec3 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -82,31 +82,6 @@ func (r *Repository) CanCreateBranch() bool { return r.Permission.CanWrite(unit_model.TypeCode) && r.Repository.CanCreateBranch() } -// AllUnitsEnabled returns true if all units are enabled for the repo. -func (r *Repository) AllUnitsEnabled(ctx context.Context) bool { - hasAnyUnitEnabled := func(unitGroup []unit_model.Type) bool { - // Loop over the group of units - for _, unit := range unitGroup { - // If *any* of them is enabled, return true. - if r.Repository.UnitEnabled(ctx, unit) { - return true - } - } - - // If none are enabled, return false. - return false - } - - for _, unitGroup := range unit_model.AllowedRepoUnitGroups { - // If any disabled unit is found, return false immediately. - if !hasAnyUnitEnabled(unitGroup) { - return false - } - } - - return true -} - // RepoMustNotBeArchived checks if a repo is archived func RepoMustNotBeArchived() func(ctx *Context) { return func(ctx *Context) { @@ -1079,7 +1054,6 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context ctx.Data["IsViewTag"] = ctx.Repo.IsViewTag ctx.Data["IsViewCommit"] = ctx.Repo.IsViewCommit ctx.Data["CanCreateBranch"] = ctx.Repo.CanCreateBranch() - ctx.Data["AllUnitsEnabled"] = ctx.Repo.AllUnitsEnabled(ctx) ctx.Repo.CommitsCount, err = ctx.Repo.GetCommitsCount() if err != nil { diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 086ffd85ff..2a3167f982 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -219,7 +219,7 @@ {{end}} {{if .Permission.IsAdmin}} - {{if not .AllUnitsEnabled}} + {{if not (.Repository.AllUnitsEnabled ctx)}} {{svg "octicon-diff-added"}} {{ctx.Locale.Tr "repo.settings.units.add_more"}} From 849de070644b6b2ce7832ca63794904fa876efe5 Mon Sep 17 00:00:00 2001 From: Michael Kriese Date: Wed, 21 Feb 2024 12:17:16 +0000 Subject: [PATCH 129/271] feat(xorm): add max idle time setting for db connections (#2418) Add a new optional `CONN_MAX_IDLETIME`[^1] This allows to set the `SetConnMaxIdleTime` on `database/sql`. It's useful to allow to close more idle connections to reduce database connections, especially on postgresql. For me i would like to use it to set a higher max idle connections but they will all be closed after being idle. So also the last idle connection will be closed when there is no load on forgejo. I also use it with max connection lifetime, because currently `database/sql` doesn't detect a postgresql master change[^2] and i'll get `[E] can't update runner status: pq: cannot execute UPDATE in a read-only transaction`[^3] on forgejo until the connection is closed. [^1]: https://pkg.go.dev/database/sql#DB.SetConnMaxIdleTime [^2]: https://stackoverflow.com/questions/51858659/how-to-safely-discard-golang-database-sql-pooled-connections-for-example-when-t [^3]: https://matrix.to/#/!zpNKWqkiEOyljSMQDK:matrix.org/$_AJft_amsGn5hXGOYw75JoBJQnW3aKJEpb-Iw53L_TU?via=schinas.net&via=matrix.org&via=nitro.chat Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2418 Reviewed-by: Gusted Reviewed-by: Earl Warren Co-authored-by: Michael Kriese Co-committed-by: Michael Kriese --- models/db/engine.go | 1 + modules/setting/database.go | 2 ++ 2 files changed, 3 insertions(+) diff --git a/models/db/engine.go b/models/db/engine.go index f1162ebd6e..660ea1f5e3 100755 --- a/models/db/engine.go +++ b/models/db/engine.go @@ -146,6 +146,7 @@ func InitEngine(ctx context.Context) error { xormEngine.SetMaxOpenConns(setting.Database.MaxOpenConns) xormEngine.SetMaxIdleConns(setting.Database.MaxIdleConns) xormEngine.SetConnMaxLifetime(setting.Database.ConnMaxLifetime) + xormEngine.SetConnMaxIdleTime(setting.Database.ConnMaxIdleTime) xormEngine.SetDefaultContext(ctx) if setting.Database.SlowQueryThreshold > 0 { diff --git a/modules/setting/database.go b/modules/setting/database.go index c7bc92e673..47d79d0de9 100644 --- a/modules/setting/database.go +++ b/modules/setting/database.go @@ -42,6 +42,7 @@ var ( DBConnectBackoff time.Duration MaxIdleConns int MaxOpenConns int + ConnMaxIdleTime time.Duration ConnMaxLifetime time.Duration IterateBufferSize int AutoMigration bool @@ -81,6 +82,7 @@ func loadDBSetting(rootCfg ConfigProvider) { } else { Database.ConnMaxLifetime = sec.Key("CONN_MAX_LIFETIME").MustDuration(0) } + Database.ConnMaxIdleTime = sec.Key("CONN_MAX_IDLETIME").MustDuration(0) Database.MaxOpenConns = sec.Key("MAX_OPEN_CONNS").MustInt(0) Database.IterateBufferSize = sec.Key("ITERATE_BUFFER_SIZE").MustInt(50) From 0081e59243738521595a3000bc54a318b5801d79 Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Wed, 21 Feb 2024 13:36:00 +0000 Subject: [PATCH 130/271] [I18N] Translations update from Weblate (#2384) Translations update from [Weblate](https://translate.codeberg.org) for [Forgejo/forgejo](https://translate.codeberg.org/projects/forgejo/forgejo/). Current translation status: ![Weblate translation status](https://translate.codeberg.org/widget/forgejo/forgejo/horizontal-auto.svg) Co-authored-by: earl-warren Co-authored-by: Kaede Fujisaki Co-authored-by: 0ko <0ko@users.noreply.translate.codeberg.org> Co-authored-by: Wuzzy Co-authored-by: meskobalazs Co-authored-by: Xinayder Co-authored-by: Anonymous Co-authored-by: Gusted Co-authored-by: Salif Mehmed Co-authored-by: Dirk Co-authored-by: fnetX Co-authored-by: Squeljur Co-authored-by: noureddin Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2384 Co-authored-by: Codeberg Translate Co-committed-by: Codeberg Translate --- options/locale/locale_ar.ini | 65 ++++- options/locale/locale_bg.ini | 32 ++- options/locale/locale_de-DE.ini | 451 +++++++++++++++++++------------- options/locale/locale_fr-FR.ini | 6 + options/locale/locale_hu-HU.ini | 9 + options/locale/locale_ja-JP.ini | 8 + options/locale/locale_nl-NL.ini | 176 +++++++++++-- options/locale/locale_pt-BR.ini | 14 +- options/locale/locale_ru-RU.ini | 99 ++++--- options/locale/locale_si-LK.ini | 4 +- 10 files changed, 598 insertions(+), 266 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index a97c642842..1af1951863 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -122,6 +122,9 @@ powered_by = مدعوم بواسطة %s retry = أعد المحاولة tracked_time_summary = ملخص للتتبع الزمني وفقًا لنتائج تصفية قائمة المسائل copy_hash = انسخ البصمة +remove = أزل +remove_all = أزل الكل +remove_label_str = أزل العنصر "%s" [install] db_name = اسم قاعدة البيانات @@ -232,8 +235,8 @@ default_keep_email_private = أخفِ عناوين البريد الإلكترو admin_name = اسم مستخدم المدير default_allow_create_organization_popup = اسمح بحسابات المستخدمين الجديدة بإنشاء المنظمات مبدئيا. password_algorithm = خوارزمية تجزئة كلمة المرور -invalid_password_algorithm = خوارزمية تجزئة كلمة المرور غير صالحة -password_algorithm_helper = اختر خوارزمية تجزئة كلمة المرور. الخوارزميات لديهم متطلبات وقوى مختلفة. خوارزمية argon2 هي آمنة ولكن تتطلب الكثير من الذاكرة ولذلك قد تكون غير ملائمة للأنظمة الصغيرة. +invalid_password_algorithm = خوارزمية بصمة كلمة المرور غير صالحة +password_algorithm_helper = اختر خوارزمية بصمة كلمة المرور. تختلف الخوارزميات في متطلباتها وقوتها. خوارزمية argon2 آمنة لكن تتطلب الكثير من الذاكرة ولذلك قد تكون غير ملائمة للأنظمة الصغيرة. [editor] buttons.list.ordered.tooltip = أضف قائمة مرقمة @@ -381,7 +384,7 @@ key_state_desc = هذا المفتاح أستُعمل خلال آخر 7 أيام webauthn_delete_key = أزِل مفتاح الأمان valid_forever = صالح للأبد can_read_info = قراءة -create_oauth2_application_button = أنشئ تطبيق +create_oauth2_application_button = أنشئ تطبيقا save_application = احفظ permissions_access_all = الكل (عام، خاص، ومحدود) valid_until_date = صالح حتى %s @@ -405,7 +408,7 @@ comment_type_group_deadline = الموعد النهائي add_key = أضف مفتاح gpg_no_key_email_found = هذا المفتاح الـGPG لا يُطابق أي بريد إلكتروني مُفعل ومربوط بحسابك. لا زال يمكن إضافته إن وقعت الرمز المقدم. keep_activity_private = اخف النشاط من صفحة الملف الشخصي -profile_desc = تحكم في كيفية ظهور ملفك الشخصي للمستخدمين الآخرين. سيتم استخدام عنوان بريدك الإلكتروني الأساسي للإشعارات واستعادة كلمة المرور وعمليات Git المستندة إلى الويب. +profile_desc = تحكم في كيفية ظهور ملفك الشخصي للمستخدمين الآخرين. سيتم استخدام عنوان بريدك الإلكتروني الأساسي للإشعارات واستعادة كلمة المرور وعمليات Git المعتمدة على الويب. can_not_add_email_activations_pending = هناك تفعيل قيد الانتظار، حاول مجدداً خلال بضع دقائق إن أردت أضافه بريد إلكتروني جديد. gpg_key_id_used = هناك مفتاح GPG عام بنفس المعرف موجود بالفعل. add_new_gpg_key = أضف مفتاح GPG @@ -420,7 +423,7 @@ add_new_key = أضف مفتاح SSH hidden_comment_types_description = أنواع التعليق المُختارة هنا لن تظهر داخل صفحات المسائل. أختيار "تصنيف" مثلاً يمسح كل تعليقات "<مستخدم> أضاف/مسح <تصنيف>". key_content_gpg_placeholder = يبدأ بـ '-----BEGIN PGP PUBLIC KEY BLOCK-----' add_email_confirmation_sent = بريد تفعيل جديد تم إرساله إلى "%s". يُرجى التحقق من البريد الوارد خلال %s لتأكيد عنوان البريد الإلكتروني. -ssh_desc = ترتبط هذه المفاتيح الـSSH العامة بحسابك. تسمح المفاتيح الخاصة المطابقة بالوصول الكامل إلى مستودعاتك. +ssh_desc = مفاتيح SSH العمومية هذه مرتبطة بحسابك. وتسمح المفاتيح الخصوصية المرافقة بالوصول الكامل إلى مستودعاتك. ويمكن استعمال مفاتيح SSH الموثَّقة لتوثيق إيداعات جت الموقَّعة بمفاتيح SSH. ssh_gpg_keys = مفاتيح SSH / GPG authorized_oauth2_applications_description = لقد منحتَ إمكانية الوصول إلى حسابك الشخصي على فورجيو لهذه التطبيقات من تطبيقات خارجية. الرجاء إلغاء وصول التطبيقات التي لم تعد بحاجة إليها. ssh_key_been_used = هذا المفتاح الـSSH تم إضافته بالفعل إلى هذا الخادم. @@ -470,6 +473,16 @@ gpg_token = رمز gpg_key_matched_identities_long = الهويات المدمجة في هذا المفتاح تطابق عناوين البريد الإلكتروني المفعلة التالية لهذا المستخدم. ويمكن التحقق من صحة الإيداعات المطابقة لهذه العناوين البريدية مع هذا المفتاح. key_content = المحتوى key_signature_gpg_placeholder = يبدأ بـ'-----BEGIN PGP SIGNATURE-----' +manage_oauth2_applications = إدارة تطبيقات OAuth2 +edit_oauth2_application = تعديل تطبيق OAuth2 +remove_oauth2_application = إزالة تطبيق OAuth2 +create_oauth2_application = أنشئ تطبيق OAuth2 جديدا +create_oauth2_application_success = لقد أنشأت بنجاح تطبيق OAuth2 جديدا. +remove_oauth2_application_success = أُزيل التطبيق. +update_oauth2_application_success = لقد حدّثت بنجاح تطبيق OAuth2. +oauth2_redirect_uris = روابط إعادة التوجيه. نرجو وضع كل رابط في سطر وحده. +remove_account_link = أزل الحساب المربوط +remove_account_link_success = أُزيل الحساب المربوط. [org] follow_blocked_user = لا يمكنك إتباع هذه المنظمة لأن هذه المنظمة حظرتك. @@ -1003,7 +1016,7 @@ settings.web_hook_name_discord = دسكورد settings.web_hook_name_telegram = تيليجرام editor.commit_signed_changes = أودع التعديلات الموقّعة editor.filename_is_invalid = اسم الملف غير صالح: "%s". -pulls.no_merge_access = ليس مسموحا لك بدمج هذا الطلب. +pulls.no_merge_access = ليس مسموحا لك دمج هذا الطلب. visibility_helper = اجعل المستودع خاصًا visibility_helper_forced = يفرض مدير موقعك أن تكون المستودعات الجديدة خاصة. wiki.page_content = محتوى الصفحة @@ -1280,9 +1293,24 @@ issues.ref_reopened_from = `أعاد فتح هذه المسأل issues.reference_issue.body = المحتوى issues.reference_link = للإشارة: %s settings.actions_desc = فعّل الإجراءات في المستودع +pulls.push_rejected = تعذر الدمج: تم رفض الدفع. راجع خطاطيف جت لهذا المستودع. +contributors.contribution_type.additions = الإضافات +search = بحث +pulls.require_signed_wont_sign = يطلب الفرع إيداعات موقّعة، لكن لن يكون هذا الدمج موقّعًا +pulls.update_branch = تحديث الفرع بالدمج +pulls.update_branch_rebase = تحديث الفرع بإعادة التأسيس +pulls.update_branch_success = نجح تحديث الفرع +pulls.update_not_allowed = ليس مسموحا لك تحديث الفرع +contributors.contribution_type.commits = الإيداعات +contributors.contribution_type.deletions = الإزالات +unit_disabled = لقد عطّل مدير الموقع قسم المستودع هذا. +pulls.fast_forward_only_merge_pull_request = تسريع وحسب +pulls.merge_conflict = تعذر الدمج: حدث نزاع خلال الدمج. مساعدة: جرب طريقة أخرى +pulls.rebase_conflict = تعذر الدمج: حدث نزاع خلال إعادة تأسيس الإيداع: %[1]s. مساعدة: جرب طريقة أخرى +pulls.has_merged = فشل: لقد تم دمج هذا الطلب، فلا يمكنك دمجه مجددا أو تغيير الفرع الهدف. [mail] -admin.new_user.text = من فضلك اضغط هنا لإدارة المستخدم من لوحة الإدارة. +admin.new_user.text = من فضلك اضغط هنا لإدارة هذا المستخدم من لوحة الإدارة. admin.new_user.subject = مستخدم جديد: %s سجل حالاً admin.new_user.user_info = معلومات المستخدم activate_account.text_1 = أهلا يا %[1]s، شكرا لك للتسجيل في %[2]s! @@ -1461,6 +1489,8 @@ sspi_auth_failed = فشلت عملية استيثاق SSPI openid_connect_desc = مسار الـOpenID المختار مجهول. اربطه مع حساب جديد هنا. openid_signin_desc = أدخل مسار الـOpenID الخاص بك. مثلاً: alice.openid.example.org أو https://openid.example.org/alice. openid_register_desc = مسار الـOpenID المختار مجهول. اربطه مع حساب جديد هنا. +remember_me = تذكر هذا الجهاز +remember_me.compromised = رمز الاحتفاظ بتسجيل الدخول لم يعد صالحا، مما قد يعني اختراق الحساب. نرجو مراجعة حسابك لرؤية أي نشاط غير مألوف. [packages] rpm.repository.multiple_groups = هذه الحزمة متوفرة في مجموعات متعددة. @@ -1681,7 +1711,7 @@ username_has_not_been_changed = لم يتم تغيير اسم المستخدم username_change_not_local_user = المستخدمين غير المحليين غير مسموح لهم بتغيير أسماؤهم. captcha_incorrect = الكابتشا خاطئة. AdminEmail = عنوان البريد الإلكتروني للمدير -team_no_units_error = اسمح الوصول بعلى الأقل قسم مستودع واحد. +team_no_units_error = اسمح بالوصول إلى قسم واحد على الأقل في المستودعات. must_use_public_key = المفتاح الذي قدمته هو مفتاح خاص. من فضلك لا ترفع مفتاحك الخاص في أي مكان. استخدم مفتاحك العام بدلاً من ذلك. unable_verify_ssh_key = "تعذر التحقق من مفتاح الـSSH، تأكد منه مجدداً." invalid_gpg_key = فشل تحقق مفتاح الـGPG: %s @@ -1835,6 +1865,7 @@ symbolic_link = رابط رمزي invalid_input_type = لا يمكنك رفع ملفات من هذا النوع. default_message = اسحب الملفات أو اضغط هنا لرفعها. file_too_big = حجم الملف ({{filesize}} مب) يتعدى الحد الأقصى ({{maxFilesize}} مب). +remove_file = أزل الملف [notification] notifications = الإشعارات @@ -1894,4 +1925,20 @@ reject_pull_request = `اقترح تغييرات في %[3]s#%[2] publish_release = `أصدر "%[4]s" في %[3]s` [units] -unit = وحدة \ No newline at end of file +unit = وحدة +error.no_unit_allowed_repo = ليس مسموحا لك الوصول إلى أي قسم في هذا المستودع. +error.unit_not_allowed = ليس مسموحا لك الوصول إلى هذا القسم في المستودع. + +[gpg] +default_key = موقّع بالمفتاح المبدئي +error.extract_sign = تعذّر استخراج التوقيع +error.generate_hash = تعذّر إنشاء بصمة الإيداع +error.no_committer_account = لا حساب مرتبط ببريد المودِع +error.not_signed_commit = "ليس إيداعًا موقّعًا" +error.failed_retrieval_gpg_keys = "تعذّر جلب مفتاح مرتبط بحساب المودِع" + +[graphs] +component_loading = يحمّل %s... +component_loading_failed = تعذر تحميل %s +component_loading_info = قد يحتاج هذا وقتا… +component_failed_to_load = حدث خطأ غير متوقع. \ No newline at end of file diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 24c5ae2dba..c18d2a2f80 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -155,6 +155,7 @@ home = Начало email = Адрес на ел. поща issues = Задачи retry = Повторен опит +remove = Премахване [repo] issues.context.edit = Редактиране @@ -210,6 +211,17 @@ issues.new_label_desc_placeholder = Описание watch_guest_user = Влезте, за да наблюдавате това хранилище. migrate_items_milestones = Етапи unstar = Премахване на звездата +owner = Притежател +issues.num_comments_1 = %d коментар +issues.context.delete = Изтриване +issues.label_title = Име +issues.save = Запазване +issues.label_edit = Редактиране +issues.label_delete = Изтриване +issues.previous = Предишна +create_repo = Създаване на хранилище +template_helper = Хранилището да е шаблон +repo_name = Име на хранилището [modal] confirm = Потвърждаване @@ -257,6 +269,12 @@ settings.full_name = Пълно Име members.leave = Напускане members.leave.detail = Напускане на %s? teams.read_access = Четене +org_name_holder = Име на организацията +create_org = Създаване на организация +settings.visibility.public = Публична +settings.visibility.limited_shortname = Ограничена +settings.visibility.private_shortname = Частна +settings.permission = Разрешения [install] admin_password = Парола @@ -284,6 +302,7 @@ issue.in_tree_path = В %s: release.note = Бележка: hi_user_x = Здравейте %s, admin.new_user.user_info = Информация за потребителя +register_notify = Добре дошли във Forgejo [user] joined_on = Присъединен на %s @@ -341,6 +360,7 @@ orgs.name = Име users.edit = Редактиране config.db_user = Потребителско име config.db_name = Име +first_page = Първа [error] not_found = Целта не може да бъде намерена. @@ -352,6 +372,7 @@ occurred = Възникна грешка UserName = Потребителско име Email = Адрес на е-поща Password = Парола +RepoName = Име на хранилището [action] close_issue = `затвори задача %[3]s#%[2]s` @@ -417,9 +438,18 @@ code_last_indexed_at = Последно индексиран %s [actions] runners.version = Версия +variables = Променливи [heatmap] less = По-малко number_of_contributions_in_the_last_12_months = %s приноса през последните 12 месеца no_contributions = Няма приноси -more = Повече \ No newline at end of file +more = Повече + +[git.filemode] +directory = Директория +symbolic_link = Символна връзка +normal_file = Обикновен файл +executable_file = Изпълним файл +changed_filemode = %[1]s → %[2]s +submodule = Подмодул \ No newline at end of file diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 6045e2a8b1..52bc88dfac 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -14,12 +14,12 @@ register=Registrieren version=Version powered_by=Powered by %s page=Seite -template=Template +template=Vorlage language=Sprache notifications=Benachrichtigungen active_stopwatch=Aktive Zeiterfassung -create_new=Erstellen… -user_profile_and_more=Profil und Einstellungen… +create_new=Erstellen … +user_profile_and_more=Profil und Einstellungen … signed_in_as=Angemeldet als enable_javascript=Diese Website benötigt JavaScript. toc=Inhaltsverzeichnis @@ -38,12 +38,12 @@ passcode=PIN webauthn_insert_key=Hardware-Sicherheitsschlüssel einstecken webauthn_sign_in=Drücke den Knopf auf deinem Sicherheitsschlüssel. Wenn dein Sicherheitsschlüssel keinen Knopf hat, stecke ihn erneut ein. -webauthn_press_button=Drücke den Knopf auf deinem Sicherheitsschlüssel… +webauthn_press_button=Drücke den Knopf auf deinem Sicherheitsschlüssel … webauthn_use_twofa=Zwei-Faktor-Authentifizierung via Handy verwenden webauthn_error=Dein Sicherheitsschlüssel konnte nicht gelesen werden. webauthn_unsupported_browser=Dein Browser unterstützt derzeit keinen WebAuthn. webauthn_error_unknown=Ein unbekannter Fehler ist aufgetreten. Bitte versuche es erneut. -webauthn_error_insecure=WebAuthn unterstützt nur sichere Verbindungen. Zum Testen über HTTP kannst Du "localhost" oder "127.0.0.1" als Host verwenden +webauthn_error_insecure=WebAuthn unterstützt nur sichere Verbindungen. Zum Testen über HTTP kannst du „localhost“ oder „127.0.0.1“ als Host verwenden webauthn_error_unable_to_process=Der Server konnte deine Anfrage nicht bearbeiten. webauthn_error_duplicated=Für diese Anfrage ist der Sicherheitsschlüssel nicht erlaubt. Bitte stell sicher, dass er nicht bereits registriert ist. webauthn_error_empty=Du musst einen Namen für diesen Schlüssel festlegen. @@ -107,16 +107,16 @@ copy_type_unsupported=Dieser Dateityp kann nicht kopiert werden write=Verfassen preview=Vorschau -loading=Laden… +loading=Laden … error=Fehler -error404=Die Seite, die Du versuchst aufzurufen, existiert nicht oder Du bist nicht berechtigt, diese anzusehen. +error404=Die Seite, die du versuchst aufzurufen, existiert nicht oder du bist nicht berechtigt, diese anzusehen. go_back=Zurück never=Niemals unknown=Unbekannt -rss_feed=RSS Feed +rss_feed=RSS-Feed pin=Anheften unpin=Loslösen @@ -188,10 +188,10 @@ install=Einfach zu installieren install_desc=Starte einfach die Anwendung für deine Plattform oder nutze Docker. Es existieren auch paketierte Versionen. platform=Plattformübergreifend platform_desc=Forgejo läuft überall, wo Go kompiliert: Windows, macOS, Linux, ARM, etc. Wähle das System, das dir am meisten gefällt! -lightweight=Leichtgewicht +lightweight=Leichtgewichtig lightweight_desc=Forgejo hat minimale Systemanforderungen und kann selbst auf einem günstigen und stromsparenden Raspberry Pi betrieben werden! license=Quelloffen -license_desc=Der komplette Code befindet sich auf Forgejo! Unterstütze uns bei der Verbesserung dieses Projekts. Trau dich! +license_desc=Hole dir Forgejo! Tritt uns bei, indem du uns hilfst, dieses Projekt noch besser zu machen. Scheue dich nicht davor, bei uns mitzuwirken! [install] install=Installation @@ -205,14 +205,14 @@ user=Benutzername password=Passwort db_name=Datenbankname db_schema=Schema -db_schema_helper=Leer lassen, um das Standard-Schema ("public") zu verwenden. +db_schema_helper=Leer lassen, um den Datenbank-Standardwert („public“) zu verwenden. ssl_mode=SSL path=Pfad -sqlite_helper=Dateipfad zur SQLite3 Datenbank.
      Gebe einen absoluten Pfad an, wenn Forgejo als Service gestartet wird. +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_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 weist, was du tust, bestätigen 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 & Spiegelungen könnten nicht mehr richtig funktionieren. Durch Ankreuzung dieses Kästchens bestätigst du, dass die aktuelle app.ini Datei den korrekten SECRET_KEY enthält. -reinstall_confirm_check_2=Die Repositories und Einstellungen müssen eventuell neu synchronisiert werden. Durch das Ankreuzen dieses Kästchens bestätigst Du, dass Du die Hooks für die Repositories und die authorized_keys Datei manuell neu synchronisierst. Du bestätigst, dass Du sicherstellst, dass die Repository- und Spiegel-Einstellungen korrekt sind. +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 Mirrors 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 Repositories und die authorized_keys-Datei manuell neu synchronisierst. Du bestätigst, dass du sicherstellst, dass die Repository- und Mirror-Einstellungen 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. no_admin_and_disable_registration=Du kannst Selbst-Registrierungen nicht deaktivieren, ohne ein Administratorkonto zu erstellen. @@ -226,7 +226,7 @@ general_title=Allgemeine Einstellungen app_name=Seitentitel app_name_helper=Du kannst hier den Namen deines Unternehmens eingeben. repo_path=Repository-Verzeichnis -repo_path_helper=Remote-Git-Repositories werden in diesem Verzeichnis gespeichert. +repo_path_helper=Remote-Git-Repositorys werden in diesem Verzeichnis gespeichert. lfs_path=Git-LFS-Wurzelpfad lfs_path_helper=In diesem Verzeichnis werden die Dateien von Git LFS abgespeichert. Leer lassen, um LFS zu deaktivieren. run_user=Ausführen als @@ -267,7 +267,7 @@ openid_signin_popup=Benutzeranmeldung via OpenID aktivieren. openid_signup=OpenID-Selbstregistrierung aktivieren openid_signup_popup=OpenID-basierte Selbstregistrierung aktivieren. enable_captcha=Registrierungs-Captcha aktivieren -enable_captcha_popup=Captcha-Eingabe bei der Registrierung erforderlich. +enable_captcha_popup=Eine Captcha-Eingabe bei der Benutzerselbstregistrierung verlangen. require_sign_in_view=Ansehen erfordert Anmeldung require_sign_in_view_popup=Seitenzugriff auf angemeldete Benutzer beschränken. Besucher sehen nur die Anmelde- und Registrierungsseite. admin_setting_desc=Das Erstellen eines Administrator-Kontos ist optional. Der erste registrierte Benutzer wird automatisch Administrator. @@ -280,7 +280,7 @@ install_btn_confirm=Forgejo installieren test_git_failed=Fehler beim Test des „git“-Befehls: %v sqlite3_not_available=Diese Forgejo-Version unterstützt SQLite3 nicht. Bitte lade die offizielle binäre Version von %s herunter (nicht die „gobuild“-Version). invalid_db_setting=Datenbankeinstellungen sind ungültig: %v -invalid_db_table=Die Datenbanktabelle "%s" ist ungültig: %v +invalid_db_table=Die Datenbanktabelle „%s“ ist ungültig: %v invalid_repo_path=Repository-Verzeichnis ist ungültig: %v invalid_app_data_path=Der App-Daten-Pfad ist ungültig: %v run_user_not_match=Der „Ausführen als“-Benutzername ist nicht der aktuelle Benutzername: %s -> %s @@ -294,7 +294,7 @@ default_keep_email_private_popup=E-Mail-Adressen von neuen Benutzern standardmä default_allow_create_organization=Erstellen von Organisationen standardmäßig erlauben default_allow_create_organization_popup=Neuen Nutzern das Erstellen von Organisationen standardmäßig erlauben. default_enable_timetracking=Zeiterfassung standardmäßig aktivieren -default_enable_timetracking_popup=Zeiterfassung standardmäßig für neue Repositories aktivieren. +default_enable_timetracking_popup=Zeiterfassung standardmäßig für neue Repositorys aktivieren. no_reply_address=Versteckte E-Mail-Domain no_reply_address_helper=Domain-Name für Benutzer mit einer versteckten Emailadresse. Zum Beispiel wird der Benutzername „Joe“ in Git als „joe@noreply.example.org“ protokolliert, wenn die versteckte E-Mail-Domain „noreply.example.org“ festgelegt ist. password_algorithm=Passwort Hashing Algorithmus @@ -311,16 +311,16 @@ enable_update_checker_helper_forgejo = Prüft regelmäßig auf neue Forgejo-Vers uname_holder=E-Mail-Adresse oder Benutzername password_holder=Passwort switch_dashboard_context=Kontext der Übersichtsseite wechseln -my_repos=Repositories -show_more_repos=Zeige mehr Repositories… -collaborative_repos=Gemeinschaftliche Repositories +my_repos=Repositorys +show_more_repos=Zeige mehr Repositorys … +collaborative_repos=Gemeinschaftliche Repositorys my_orgs=Meine Organisationen my_mirrors=Meine Mirrors view_home=%s ansehen -search_repos=Finde ein Repository… +search_repos=Finde ein Repository … filter=Andere Filter -filter_by_team_repositories=Nach Team-Repositories filtern -feed_of=`Feed von "%s"` +filter_by_team_repositories=Nach Team-Repositorys filtern +feed_of=Feed von „%s“ show_archived=Archiviert show_both_archived_unarchived=Archivierte und nicht archivierte anzeigen @@ -332,10 +332,10 @@ show_both_private_public=Öffentliche und private anzeigen show_only_private=Nur private anzeigen show_only_public=Nur öffentliche anzeigen -issues.in_your_repos=Eigene Repositories +issues.in_your_repos=Eigene Repositorys [explore] -repos=Repositories +repos=Repositorys users=Benutzer organizations=Organisationen search=Suche @@ -347,14 +347,14 @@ search.fuzzy.tooltip=Zeige auch Ergebnisse, die dem Suchbegriff ähneln search.match=Genau search.match.tooltip=Zeige nur Ergebnisse, die exakt mit dem Suchbegriff übereinstimmen code_search_unavailable=Derzeit ist die Code-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator. -repo_no_results=Keine passenden Repositories gefunden. +repo_no_results=Keine passenden Repositorys gefunden. user_no_results=Keine passenden Benutzer gefunden. org_no_results=Keine passenden Organisationen gefunden. code_no_results=Es konnte kein passender Code für deinen Suchbegriff gefunden werden. -code_search_results=`Suchergebnisse für "%s"` +code_search_results=Suchergebnisse für „%s“ code_last_indexed_at=Zuletzt indexiert %s -relevant_repositories_tooltip=Repositories, die Forks sind oder die kein Thema, kein Symbol und keine Beschreibung haben, werden ausgeblendet. -relevant_repositories=Es werden nur relevante Repositories angezeigt, ungefilterte Ergebnisse anzeigen. +relevant_repositories_tooltip=Repositorys, die Forks sind oder die kein Thema, kein Symbol und keine Beschreibung haben, werden ausgeblendet. +relevant_repositories=Es werden nur relevante Repositorys angezeigt, ungefilterte Ergebnisse anzeigen. [auth] create_new_account=Konto anlegen @@ -418,8 +418,8 @@ email_domain_blacklisted=Du kannst dich nicht mit deiner E-Mail-Adresse registri authorize_application=Anwendung autorisieren authorize_redirect_notice=Du wirst zu %s weitergeleitet, wenn du diese Anwendung autorisierst. authorize_application_created_by=Diese Anwendung wurde von %s erstellt. -authorize_application_description=Wenn du diese Anwendung autorisierst, wird sie die Berechtigung erhalten, alle Informationen zu deinem Account zu bearbeiten oder zu lesen. Dies beinhaltet auch private Repositories und Organisationen. -authorize_title=`"%s" den Zugriff auf deinen Account gestatten?` +authorize_application_description=Wenn du diese Anwendung autorisierst, wird sie die Berechtigung erhalten, alle Informationen zu deinem Account zu bearbeiten oder zu lesen. Dies beinhaltet auch private Repositorys und Organisationen. +authorize_title=„%s“ den Zugriff auf deinen Account gestatten? authorization_failed=Autorisierung fehlgeschlagen authorization_failed_desc=Die Autorisierung ist fehlgeschlagen, da wir eine ungültige Anfrage erkannt haben. Bitte kontaktiere den Betreuer der App, die du zu autorisieren versucht hast. sspi_auth_failed=SSPI-Authentifizierung fehlgeschlagen @@ -427,13 +427,14 @@ password_pwned=Das von dir gewählte Passwort befindet sich auf einer %s, activate_account=Bitte aktiviere dein Konto @@ -448,7 +449,7 @@ activate_email.text=Bitte klicke innerhalb von %s auf folgenden Link, um register_notify=Willkommen bei Forgejo register_notify.title=%[1]s, willkommen bei %[2]s register_notify.text_1=dies ist deine Bestätigungs-E-Mail für %s! -register_notify.text_2=Du kannst dich jetzt mit dem Benutzernamen "%s" anmelden. +register_notify.text_2=Du kannst dich jetzt mit dem Benutzernamen „%s“ anmelden. register_notify.text_3=Wenn dieser Account von dir erstellt wurde, musst du zuerst dein Passwort setzen. reset_password=Stelle dein Konto wieder her @@ -457,7 +458,7 @@ reset_password.text=Bitte klicke innerhalb von %s auf folgenden Link, um register_success=Registrierung erfolgreich -issue_assigned.pull=@%[1]s hat dich im Repository %[3]s dem Pull Request %[2]s zugewiesen. +issue_assigned.pull=@%[1]s hat dich im Repository %[3]s dem Pull-Request %[2]s zugewiesen. issue_assigned.issue=@%[1]s hat dich im Repository %[3]s dem Issue %[2]s zugewiesen. issue.x_mentioned_you=@%s hat dich erwähnt: @@ -467,11 +468,11 @@ issue.action.push_n=@%[1]s hat %[3]d Commits auf %[2]s gepusht issue.action.close=@%[1]s hat #%[2]d geschlossen. issue.action.reopen=@%[1]s hat #%[2]d wieder geöffnet. issue.action.merge=@%[1]s hat #%[2]d in %[3]s gemergt. -issue.action.approve=@%[1]s hat diesen Pull-Request approved. +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 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.new=@%[1]s hat #%[2]d geöffnet. issue.in_tree_path=In %s: @@ -483,8 +484,8 @@ release.downloads=Downloads: release.download.zip=Quellcode (ZIP Datei) release.download.targz=Quellcode (TAR.GZ Datei) -repo.transfer.subject_to=%s möchte "%s" an %s übertragen -repo.transfer.subject_to_you=%s möchte dir "%s" übertragen +repo.transfer.subject_to=%s möchte „%s“ an %s übertragen +repo.transfer.subject_to_you=%s möchte dir „%s“ übertragen repo.transfer.to_you=dir repo.transfer.body=Um es anzunehmen oder abzulehnen, öffne %s, oder ignoriere es einfach. @@ -537,11 +538,11 @@ size_error=` muss die Größe %s haben.` min_size_error=` muss mindestens %s Zeichen enthalten.` max_size_error=` darf höchstens %s Zeichen enthalten.` email_error=` ist keine gültige E-Mail-Adresse.` -url_error=`"%s" ist keine gültige URL.` -include_error=` muss den Text "%s" enthalten.` -glob_pattern_error=` Der Glob Pattern ist ungültig: %s.` +url_error=`„%s“ ist keine gültige URL.` +include_error=` muss den Text „%s“ enthalten.` +glob_pattern_error=` Glob-Pattern ist ungültig: %s.` regex_pattern_error=` regex ist ungültig: %s.` -username_error=` darf nur alphanumerische Zeichen ('0-9','a-z','A-Z'), Bindestriche ('-'), Unterstriche ('_') und Punkte ('.') enthalten. Es kann nicht mit nicht-alphanumerischen Zeichen beginnen oder enden und aufeinanderfolgende nicht-alphanumerische Zeichen sind ebenfalls verboten.` +username_error=` darf nur alphanumerische Zeichen („0-9“, „a-z“, „A-Z“), Bindestriche („-“), Unterstriche („_“) und Punkte („.“) enthalten. Es kann nicht mit nicht-alphanumerischen Zeichen beginnen oder enden und aufeinanderfolgende nicht-alphanumerische Zeichen sind ebenfalls verboten.` invalid_group_team_map_error=` Zuordnung ist ungültig: %s` unknown_error=Unbekannter Fehler: captcha_incorrect=Der eingegebene CAPTCHA-Code ist falsch. @@ -552,7 +553,7 @@ username_been_taken=Der Benutzername ist bereits vergeben. username_change_not_local_user=Nicht-lokale Benutzer dürfen ihren Nutzernamen nicht ändern. username_has_not_been_changed=Benutzername wurde nicht geändert repo_name_been_taken=Der Repository-Name wird schon verwendet. -repository_force_private=Privat erzwingen ist aktiviert: Private Repositories können nicht veröffentlicht werden. +repository_force_private=Privat erzwingen ist aktiviert: Private Repositorys können nicht veröffentlicht werden. repository_files_already_exist=Dateien für dieses Repository sind bereits vorhanden. Kontaktiere den Systemadministrator. 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. @@ -564,7 +565,7 @@ team_name_been_taken=Der Teamname ist bereits vergeben. team_no_units_error=Das Team muss auf mindestens einen Bereich Zugriff haben. email_been_used=Die E-Mail-Adresse wird bereits verwendet. email_invalid=Die E-Mail-Adresse ist ungültig. -openid_been_used=Die OpenID-Adresse "%s" wird bereits verwendet. +openid_been_used=Die OpenID-Adresse „%s“ wird bereits verwendet. username_password_incorrect=Benutzername oder Passwort ist falsch. password_complexity=Das Passwort erfüllt nicht die Komplexitätsanforderungen: password_lowercase_one=Mindestens ein Kleinbuchstabe @@ -572,12 +573,12 @@ password_uppercase_one=Mindestens ein Großbuchstabe password_digit_one=Mindestens eine Ziffer password_special_one=Mindestens ein Sonderzeichen (Satzzeichen, Klammern, Anführungszeichen, etc.) enterred_invalid_repo_name=Der eingegebenen Repository-Name ist falsch. -enterred_invalid_org_name=Der eingegebene Organisation-Name ist falsch. +enterred_invalid_org_name=Der eingegebene Organisationsname ist falsch. enterred_invalid_owner_name=Der Name des neuen Besitzers ist ungültig. enterred_invalid_password=Das eingegebene Passwort ist falsch. user_not_exist=Dieser Benutzer ist nicht vorhanden. team_not_exist=Dieses Team existiert nicht. -last_org_owner=Du kannst den letzten Benutzer nicht aus dem 'Besitzer'-Team entfernen. Es muss mindestens einen Besitzer in einer Organisation geben. +last_org_owner=Du kannst den letzten Benutzer nicht aus dem „Besitzer“-Team entfernen. Es muss mindestens einen Besitzer in einer Organisation geben. cannot_add_org_to_team=Eine Organisation kann nicht als Teammitglied hinzugefügt werden. duplicate_invite_to_team=Der Benutzer wurde bereits als Teammitglied eingeladen. organization_leave_success=Du hast die Organisation %s erfolgreich verlassen. @@ -586,27 +587,27 @@ invalid_ssh_key=Dein SSH-Key kann nicht überprüft werden: %s invalid_gpg_key=Dein GPG-Key kann nicht überprüft werden: %s invalid_ssh_principal=Ungültige Identität: %s must_use_public_key=Der von Dir bereitgestellte Key ist ein privater Key. Bitte lade Deinen privaten Key nirgendwo hoch. Verwende stattdessen Deinen öffentlichen Key. -unable_verify_ssh_key=Der SSH-Key kann nicht verifiziert werden, überprüfe ihn auf Fehler. +unable_verify_ssh_key=„Der SSH-Key kann nicht verifiziert werden, überprüfe ihn auf Fehler.“ auth_failed=Authentifizierung fehlgeschlagen: %v -still_own_repo=Dein Konto besitzt ein oder mehrere Repositories. Diese müssen erst gelöscht oder übertragen werden. -still_has_org=Dein Konto ist Mitglied einer oder mehrerer Organisationen, verlasse diese zuerst. -still_own_packages=Dein Konto besitzt ein oder mehrere Pakete, lösche diese zuerst. -org_still_own_repo=Diese Organisation besitzt noch ein oder mehrere Repositories. Diese müssen zuerst gelöscht oder übertragen werden. -org_still_own_packages=Diese Organisation besitzt noch ein oder mehrere Pakete, lösche diese zuerst. +still_own_repo=„Dein Konto besitzt ein oder mehrere Repositorys. Diese müssen erst gelöscht oder übertragen werden.“ +still_has_org=„Dein Konto ist Mitglied einer oder mehrerer Organisationen, verlasse diese zuerst.“ +still_own_packages=„Dein Konto besitzt ein oder mehrere Pakete, lösche diese zuerst.“ +org_still_own_repo=„Diese Organisation besitzt noch ein oder mehrere Repositorys. Diese müssen zuerst gelöscht oder übertragen werden.“ +org_still_own_packages=„Diese Organisation besitzt noch ein oder mehrere Pakete, lösche diese zuerst.“ target_branch_not_exist=Der Ziel-Branch existiert nicht. -username_error_no_dots = ` darf nur alphanumerische Zeichen ('0-9','a-z','A-Z'), Bindestriche ('-') und Unterstriche ('_') enthalten. Es kann nicht mit nicht-alphanumerischen Zeichen beginnen oder enden und aufeinanderfolgende nicht-alphanumerische Zeichen sind ebenfalls verboten.` -admin_cannot_delete_self = Sie können sich nicht löschen, wenn Sie ein Admin sind. Bitte entfernen Sie zuerst Ihre Adminrechte. +username_error_no_dots = ` darf nur alphanumerische Zeichen („0-9“, „a-z“, „A-Z“), Bindestriche („-“), Unterstriche („_“) enthalten. Es kann nicht mit nicht-alphanumerischen Zeichen beginnen oder enden und aufeinanderfolgende nicht-alphanumerische Zeichen sind ebenfalls verboten.` +admin_cannot_delete_self = Du kannst dich nicht selbst löschen, wenn du ein Admin bist. Bitte entferne zuerst deine Adminrechte. [user] -change_avatar=Profilbild ändern… +change_avatar=Profilbild ändern … joined_on=Beigetreten am %s -repositories=Repositories +repositories=Repositorys activity=Öffentliche Aktivität followers=Follower -starred=Favoriten -watched=Beobachtete Repositories +starred=Favorisierte Repositorys +watched=Beobachtete Repositorys code=Quelltext projects=Projekte overview=Übersicht @@ -616,17 +617,21 @@ unfollow=Nicht mehr folgen user_bio=Biografie disabled_public_activity=Dieser Benutzer hat die öffentliche Sichtbarkeit der Aktivität deaktiviert. email_visibility.limited=Ihre E-Mail-Adresse ist für alle authentifizierten Benutzer sichtbar -email_visibility.private=Deine E-Mail-Adresse ist nur für Dich und Administratoren sichtbar +email_visibility.private=Deine E-Mail-Adresse ist nur für dich und Administratoren sichtbar show_on_map=Diesen Ort auf einer Karte anzeigen settings=Benutzereinstellungen -form.name_reserved=Der Benutzername "%s" ist reserviert. -form.name_pattern_not_allowed=Das Muster "%s" ist nicht in einem Benutzernamen erlaubt. -form.name_chars_not_allowed=Benutzername "%s" enthält ungültige Zeichen. -block_user = Nutzer Blockieren -block_user.detail = Bitte beachten Sie, dass andere Maßnahmen ergriffen werden, wenn Sie diesen Benutzer blockieren, wie: -block_user.detail_2 = Dieser Benutzer kann nicht mit ihrem Repository interagieren, Issues erstellen und kommentieren. -block_user.detail_1 = Dieser Benutzer folgt ihnen nicht mehr. +form.name_reserved=Der Benutzername „%s“ ist reserviert. +form.name_pattern_not_allowed=Das Muster „%s“ ist nicht in einem Benutzernamen erlaubt. +form.name_chars_not_allowed=Benutzername „%s“ enthält ungültige Zeichen. +block_user = Benutzer blockieren +block_user.detail = Bitte beachte, dass andere Maßnahmen ergriffen werden, wenn du diesen Benutzer blockierst, wie: +block_user.detail_2 = Dieser Benutzer kann nicht mit deinem Repository, erstellten Issues und Kommentaren interagieren. +block_user.detail_1 = Dieser Benutzer folgt dir nicht mehr. +block = Blockieren +follow_blocked_user = Du kannst diesen Benutzer nicht folgen, weil du ihn blockiert hast, oder er dich blockiert hat. +block_user.detail_3 = Dieser Benutzer kann dich nicht als einen Mitarbeiter hinzufügen, und du kannst ihn nicht als Mitarbeiter hinzufügen. +unblock = Nicht mehr blockieren [settings] profile=Profil @@ -639,7 +644,7 @@ ssh_gpg_keys=SSH- / GPG-Schlüssel social=Soziale Konten applications=Anwendungen orgs=Organisationen verwalten -repos=Repositories +repos=Repositorys delete=Konto löschen twofa=Zwei-Faktor-Authentifizierung account_link=Verknüpfte Benutzerkonten @@ -648,9 +653,9 @@ uid=UID webauthn=Hardware-Sicherheitsschlüssel public_profile=Öffentliches Profil -biography_placeholder=Erzähle uns ein wenig über Dich selbst! (Du kannst Markdown verwenden) -location_placeholder=Teile Deinen ungefähren Standort mit anderen -profile_desc=Lege fest, wie dein Profil anderen Benutzern angezeigt wird. Deine primäre E-Mail-Adresse wird für Benachrichtigungen, Passwort-Wiederherstellung und webbasierte Git-Operationen verwendet. +biography_placeholder=Erzähle uns ein wenig über dich selbst! (Du kannst Markdown verwenden) +location_placeholder=Teile deinen ungefähren Standort mit anderen +profile_desc=Leg fest, wie dein Profil anderen Benutzern angezeigt wird. Deine primäre E-Mail-Adresse wird für Benachrichtigungen, Passwort-Wiederherstellung und webbasierte Git-Operationen verwendet. password_username_disabled=Benutzer, die nicht von Forgejo verwaltet werden können ihren Benutzernamen nicht ändern. Bitte kontaktiere deinen Administrator für mehr Details. full_name=Vollständiger Name website=Webseite @@ -658,7 +663,7 @@ location=Standort update_theme=Theme ändern update_profile=Profil aktualisieren update_language=Sprache aktualisieren -update_language_not_found=Sprache "%s" ist nicht verfügbar. +update_language_not_found=Sprache „%s“ ist nicht verfügbar. update_language_success=Sprache wurde aktualisiert. update_profile_success=Dein Profil wurde aktualisiert. change_username=Dein Benutzername wurde geändert. @@ -668,9 +673,9 @@ continue=Weiter cancel=Abbrechen language=Sprache ui=Theme -hidden_comment_types=Ausgeblendeter Kommentartypen -hidden_comment_types_description=Die hier markierten Kommentartypen werden nicht innerhalb der Issue-Seiten angezeigt. Das Überprüfen von "Label" entfernt beispielsweise alle " hinzugefügt/entfernt
      -
      +
      {{ run.commit.localeCommit }} {{ run.commit.shortSHA }} {{ run.commit.localePushedBy }} @@ -378,6 +383,10 @@ export function initRepositoryActionView() { {{ run.commit.branch.name }}
      +
      + {{ run.commit.localeWorkflow }} + {{ workflowName }} +
      @@ -500,7 +509,7 @@ export function initRepositoryActionView() { flex: 1; } -.action-commit-summary { +.action-summary { display: flex; gap: 5px; margin: 0 0 0 28px; From 704615fa65476f435e296021cc59c73ec00746b1 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Tue, 20 Feb 2024 13:37:10 +0100 Subject: [PATCH 133/271] [RELEASE] v1.21.6-0 release notes Refs: https://codeberg.org/forgejo/forgejo/issues/2408 --- RELEASE-NOTES.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md index 65f79e3628..c72c898f67 100644 --- a/RELEASE-NOTES.md +++ b/RELEASE-NOTES.md @@ -4,6 +4,54 @@ A Forgejo release is published shortly after a Gitea release is published and th The Forgejo admin should carefully read the required manual actions before upgrading. A point release (e.g. v1.21.1-0 or v1.21.2-0) does not require manual actions but others might (e.g. v1.20, v1.21). +## 1.21.6-0 + +The [complete list of commits](https://codeberg.org/forgejo/forgejo/commits/branch/v1.21/forgejo) included in the `Forgejo v1.21.6-0` release can be reviewed from the command line with: + +```shell +$ git clone https://codeberg.org/forgejo/forgejo/ +$ git -C forgejo log --oneline --no-merges v1.21.5-0..v1.21.6-0 +``` + +This stable release contains bug fixes and a **security fix**, as explained in the [v1.21.6-0 companion blog post](https://forgejo.org/2024-02-release-v1-21-6-0/). + +* Recommended Action + + We **strongly recommend** that all Forgejo installations are [upgraded](https://forgejo.org/docs/v1.21/admin/upgrade/) to the latest version as soon as possible. + +* [Forgejo Semantic Version](https://forgejo.org/docs/v1.21/user/semver/) + + The semantic version was updated to `6.0.6+0-gitea-1.21.6` + +* Security fix + + * [Fix XSS vulnerabilities](https://codeberg.org/forgejo/forgejo/pulls/2434). It enabled attackers to inject client-side scripts into web pages displayed to Forgejo visitors. + +* Bug fixes + + The most prominent ones are described here, others can be found in the list of commits included in the release as described above. + + * [Always write proc-receive hook for all git versions](https://codeberg.org/forgejo/forgejo/commit/a1fb6a2346193439dafaee5acf071632246e6dd7). + * [Fix debian InRelease Acquire-By-Hash newline](https://codeberg.org/forgejo/forgejo/commit/8a2c4e9ff2743f47a8d1f081b9e35dcc16431115). + * [Fix missing link on outgoing new release notifications](https://codeberg.org/forgejo/forgejo/commit/3a061083d65bdfc9acf0cb5839b84f6a9c17a727). + * [Workaround to clean up old reviews on creating a new one](https://codeberg.org/forgejo/forgejo/commit/8377ecbfe1f2b72ec7d65c46cbc9022ad0ccd75f). + * [Fix push to create with capitalize repo name](https://codeberg.org/forgejo/forgejo/commit/8782275c9c66ad6fc7c44503d7df9dae7196aa65). + * In Markdown [don't try to make the link absolute if the link has a schema that's defined in `[markdown].CUSTOM_URL_SCHEMES`](https://codeberg.org/forgejo/forgejo/commit/6c100083c29fb0ccf0cc52e8767e540a260d9468), because they can't be made absolute. + * [Fix Ctrl+Enter on submitting review comment](https://codeberg.org/forgejo/forgejo/commit/1c3a31d85112d10fb948d6f0b763191ed6f68e90). + * In Git version v2.43.1, the behavior of `GIT_FLUSH` was accidentially flipped. This causes Forgejo to hang on the `check-attr` command, because no output was being flushed. [Workaround this by detecting if Git v2.43.1 is used and set `GIT_FLUSH=0` thus getting the correct behavior](https://codeberg.org/forgejo/forgejo/commit/ff468ab5e426582b068586ce13d5a5348365e783). + * [When setting `url.host` on a URL object with no port specified (like is the case of default port), the resulting URL's port will not change. Workaround this quirk in the URL standard by explicitly setting port for the http and https protocols](https://codeberg.org/forgejo/forgejo/commit/628e1036cfbcfae442cb6494249fe11410447056). + * [Fix elasticsearch Request Entity Too Large](https://codeberg.org/forgejo/forgejo/commit/e6f59f6e1489d63d53de0da1de406a7a71a82adb). + * [Do not send update/delete release notifications when it is in a draft state](https://codeberg.org/forgejo/forgejo/commit/3c54a1dbf62e56d948feb1008512900140033737). + * [Do not run Forgejo Actions workflows synchronized events on the same commit as the one used to create a pull request](https://codeberg.org/forgejo/forgejo/commit/ce96379aef6e92cff2e9982031d5248ef8b01947). + * [Fix a MySQL performance regression introduced in v1.21.4-0](https://codeberg.org/forgejo/forgejo/commit/af98a0a7c6f4cbb5340974958ebe4389e3bf4e9a). + * [Fix Internal Server Error when resolving comments](https://codeberg.org/forgejo/forgejo/commit/ad67d9ef1a219b21309f811c14e7353cbc4982e3). + * Packages + * Swift: [fix a failure to resolve from package registry](https://codeberg.org/forgejo/forgejo/commit/fab6780fda5d8ded020a98253a793e87ed94f634). + * Alpine: [if the APKINFO contains an install if condition, write it in the APKINDEX](https://codeberg.org/forgejo/forgejo/commit/7afbc62057b876fb6711ef58743f664a2509dde4). + * org-mode files + * [It is possible that the description of an `Regularlink` is `Text` and not another `Regularlink`](https://codeberg.org/forgejo/forgejo/commit/781d2a68ccb276bf13caf0b378b74d9efeab3d39). + * [Fix relative links on orgmode](https://codeberg.org/forgejo/forgejo/commit/fa700333ba2649d14f1670dd2745957704a33b40). + ## 1.21.5-0 The [complete list of commits](https://codeberg.org/forgejo/forgejo/commits/branch/v1.21/forgejo) included in the `Forgejo v1.21.5-0` release can be reviewed from the command line with: From 565e3312385d533f96c359979a3ae7cc14eba671 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 17 Jan 2024 16:16:46 +0100 Subject: [PATCH 134/271] [SECURITY] Test XSS in wiki last commit information On the wiki and revisions page, information is shown about the last commit that modified that wiki page. This includes the time it was last edited and by whom. Verify it is sanitized. --- tests/integration/xss_test.go | 75 +++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/integration/xss_test.go b/tests/integration/xss_test.go index e575ed3990..42ce35150c 100644 --- a/tests/integration/xss_test.go +++ b/tests/integration/xss_test.go @@ -4,14 +4,24 @@ package integration import ( + "context" + "fmt" "net/http" + "net/url" + "os" + "path/filepath" "testing" + "time" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/tests" + gogit "github.com/go-git/go-git/v5" + "github.com/go-git/go-git/v5/plumbing/object" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestXSSUserFullName(t *testing.T) { @@ -37,3 +47,68 @@ func TestXSSUserFullName(t *testing.T) { htmlDoc.doc.Find("div.content").Find(".header.text.center").Text(), ) } + +func TestXSSWikiLastCommitInfo(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + // Prepare the environment. + dstPath := t.TempDir() + r := fmt.Sprintf("%suser2/repo1.wiki.git", u.String()) + u, err := url.Parse(r) + assert.NoError(t, err) + u.User = url.UserPassword("user2", userPassword) + assert.NoError(t, git.CloneWithArgs(context.Background(), git.AllowLFSFiltersArgs(), u.String(), dstPath, git.CloneRepoOptions{})) + + // Use go-git here, because using git wouldn't work, it has code to remove + // `<`, `>` and `\n` in user names. Even though this is permitted and + // wouldn't result in a error by a Git server. + gitRepo, err := gogit.PlainOpen(dstPath) + require.NoError(t, err) + + w, err := gitRepo.Worktree() + require.NoError(t, err) + + filename := filepath.Join(dstPath, "Home.md") + err = os.WriteFile(filename, []byte("Oh, a XSS attack?"), 0o644) + require.NoError(t, err) + + _, err = w.Add("Home.md") + require.NoError(t, err) + + _, err = w.Commit("Yay XSS", &gogit.CommitOptions{ + Author: &object.Signature{ + Name: `Gusted`, + Email: "valid@example.org", + When: time.Date(2024, time.January, 31, 0, 0, 0, 0, time.UTC), + }, + }) + require.NoError(t, err) + + // Push. + _, _, err = git.NewCommand(git.DefaultContext, "push").AddArguments(git.ToTrustedCmdArgs([]string{"origin", "master"})...).RunStdString(&git.RunOpts{Dir: dstPath}) + require.NoError(t, err) + + // Check on page view. + t.Run("Page view", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, http.MethodGet, "/user2/repo1/wiki/Home") + resp := MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + + htmlDoc.AssertElement(t, "script.evil", false) + assert.Contains(t, htmlDoc.Find(".ui.sub.header").Text(), `Gusted edited this page 2024-01-31`) + }) + + // Check on revisions page. + t.Run("Revision page", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, http.MethodGet, "/user2/repo1/wiki/Home?action=_revision") + resp := MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + + htmlDoc.AssertElement(t, "script.evil", false) + assert.Contains(t, htmlDoc.Find(".ui.sub.header").Text(), `Gusted edited this page 2024-01-31`) + }) + }) +} From ca798e4cc2a8c6e3d1f2cfed01f47d8b3da9361f Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 18 Jan 2024 00:18:39 +0100 Subject: [PATCH 135/271] [SECURITY] Test XSS in dismissed review It's possible for reviews to not be assiocated with users, when they were migrated from another forge instance. In the migration code, there's no sanitization check for author names, so they could contain HTML tags and thus needs to be properely escaped. --- templates/repo/issue/view_content/comments.tmpl | 2 +- .../fixtures/TestXSSReviewDismissed/comment.yml | 9 +++++++++ .../fixtures/TestXSSReviewDismissed/review.yml | 8 ++++++++ tests/integration/xss_test.go | 15 +++++++++++++++ 4 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/integration/fixtures/TestXSSReviewDismissed/comment.yml create mode 100644 tests/integration/fixtures/TestXSSReviewDismissed/review.yml diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 9e50ee4d94..a4fd97297f 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -619,7 +619,7 @@ {{else}} {{$reviewerName = .Review.OriginalAuthor}} {{end}} - {{ctx.Locale.Tr "repo.issues.review.dismissed" $reviewerName $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.review.dismissed" $reviewerName $createdStr | Safe}}
      {{if .Content}} diff --git a/tests/integration/fixtures/TestXSSReviewDismissed/comment.yml b/tests/integration/fixtures/TestXSSReviewDismissed/comment.yml new file mode 100644 index 0000000000..50162a4e7e --- /dev/null +++ b/tests/integration/fixtures/TestXSSReviewDismissed/comment.yml @@ -0,0 +1,9 @@ +- + id: 1000 + type: 32 # dismiss review + poster_id: 2 + issue_id: 2 # in repo_id 1 + content: "XSS time!" + review_id: 1000 + created_unix: 1700000000 + updated_unix: 1700000000 diff --git a/tests/integration/fixtures/TestXSSReviewDismissed/review.yml b/tests/integration/fixtures/TestXSSReviewDismissed/review.yml new file mode 100644 index 0000000000..56bc08d35f --- /dev/null +++ b/tests/integration/fixtures/TestXSSReviewDismissed/review.yml @@ -0,0 +1,8 @@ +- + id: 1000 + type: 1 + issue_id: 2 + original_author: "Otto " + content: "XSS time!" + updated_unix: 1700000000 + created_unix: 1700000000 diff --git a/tests/integration/xss_test.go b/tests/integration/xss_test.go index 42ce35150c..acd716c7c7 100644 --- a/tests/integration/xss_test.go +++ b/tests/integration/xss_test.go @@ -13,6 +13,7 @@ import ( "testing" "time" + issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" @@ -112,3 +113,17 @@ func TestXSSWikiLastCommitInfo(t *testing.T) { }) }) } + +func TestXSSReviewDismissed(t *testing.T) { + defer tests.AddFixtures("tests/integration/fixtures/TestXSSReviewDismissed/")() + defer tests.PrepareTestEnv(t)() + + review := unittest.AssertExistsAndLoadBean(t, &issues_model.Review{ID: 1000}) + + req := NewRequest(t, http.MethodGet, fmt.Sprintf("/user2/repo1/pulls/%d", +review.IssueID)) + resp := MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + + htmlDoc.AssertElement(t, "script.evil", false) + assert.Contains(t, htmlDoc.Find("#issuecomment-1000 .dismissed-message").Text(), `dismissed Otto ’s review`) +} From e5b5585ee24610f2d6dc959fccf78b3435452a62 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Thu, 22 Feb 2024 22:33:22 +0500 Subject: [PATCH 136/271] Fixes & Improvements for English locale --- options/locale/locale_ar.ini | 4 ++-- options/locale/locale_bg.ini | 4 ++-- options/locale/locale_cs-CZ.ini | 2 +- options/locale/locale_de-DE.ini | 4 ++-- options/locale/locale_el-GR.ini | 2 +- options/locale/locale_en-US.ini | 25 ++++++++++---------- options/locale/locale_eo.ini | 4 ++-- options/locale/locale_es-ES.ini | 2 +- options/locale/locale_fa-IR.ini | 2 +- options/locale/locale_fi-FI.ini | 2 +- options/locale/locale_fr-FR.ini | 2 +- options/locale/locale_hu-HU.ini | 2 +- options/locale/locale_id-ID.ini | 2 +- options/locale/locale_is-IS.ini | 2 +- options/locale/locale_it-IT.ini | 2 +- options/locale/locale_ja-JP.ini | 2 +- options/locale/locale_ko-KR.ini | 2 +- options/locale/locale_lv-LV.ini | 2 +- options/locale/locale_nl-NL.ini | 4 ++-- options/locale/locale_pl-PL.ini | 2 +- options/locale/locale_pt-BR.ini | 2 +- options/locale/locale_pt-PT.ini | 2 +- options/locale/locale_ru-RU.ini | 4 ++-- options/locale/locale_si-LK.ini | 2 +- options/locale/locale_sk-SK.ini | 2 +- options/locale/locale_sl.ini | 2 +- options/locale/locale_sv-SE.ini | 2 +- options/locale/locale_tr-TR.ini | 2 +- options/locale/locale_uk-UA.ini | 2 +- options/locale/locale_zh-CN.ini | 2 +- options/locale/locale_zh-TW.ini | 2 +- templates/user/auth/change_passwd_inner.tmpl | 4 ++-- templates/user/settings/account.tmpl | 4 ++-- 33 files changed, 53 insertions(+), 52 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index 1af1951863..ca1127d624 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -324,7 +324,7 @@ twofa_disable = تعطيل الاستيثاق الثنائي retype_new_password = تأكيد كلمة المرور الجديدة manage_emails = أدر عناوين البريد الإلكتروني then_enter_passcode = وأدخل رمز الدخول الظاهر في التطبيق: -change_password = حدّث كلمة المرور +update_password = حدّث كلمة المرور continue = استمر emails = عناوين البريد الإلكتروني confirm_delete_account = أكُد الحذف @@ -1941,4 +1941,4 @@ error.failed_retrieval_gpg_keys = "تعذّر جلب مفتاح مرتبط بح component_loading = يحمّل %s... component_loading_failed = تعذر تحميل %s component_loading_info = قد يحتاج هذا وقتا… -component_failed_to_load = حدث خطأ غير متوقع. \ No newline at end of file +component_failed_to_load = حدث خطأ غير متوقع. diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index c18d2a2f80..2cd88b4a62 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -47,7 +47,7 @@ ssh_gpg_keys = SSH / GPG Ключове comment_type_group_milestone = Етап manage_emails = Управление на адресите на ел. поща permission_read = Четене -change_password = Обновяване на паролата +update_password = Обновяване на паролата biography_placeholder = Разкажете ни малко за себе си! (Можете да използвате Markdown) orgs = Управление на организациите continue = Продължаване @@ -452,4 +452,4 @@ symbolic_link = Символна връзка normal_file = Обикновен файл executable_file = Изпълним файл changed_filemode = %[1]s → %[2]s -submodule = Подмодул \ No newline at end of file +submodule = Подмодул diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 78268104ff..88cfc04020 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -689,7 +689,7 @@ uploaded_avatar_is_too_big=Nahraný soubor (%d KiB) přesahuje maximální velik update_avatar_success=Vaše avatar byl aktualizován. update_user_avatar_success=Uživatelův avatar byl aktualizován. -change_password=Aktualizovat heslo +update_password=Aktualizovat heslo old_password=Stávající heslo new_password=Nové heslo retype_new_password=Potvrdit nové heslo diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index 52bc88dfac..e57bf94277 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -707,7 +707,7 @@ uploaded_avatar_is_too_big=Die hochgeladene Dateigröße (%d KiB) überschreitet update_avatar_success=Dein Profilbild wurde geändert. update_user_avatar_success=Der Avatar des Benutzers wurde aktualisiert. -change_password=Passwort aktualisieren +update_password=Passwort aktualisieren old_password=Aktuelles Passwort new_password=Neues Passwort retype_new_password=Neues Passwort bestätigen @@ -3670,4 +3670,4 @@ component_loading_failed = Konnte %s nicht laden component_loading_info = Dies könnte einen Moment dauern … component_failed_to_load = Ein unerwarteter Fehler ist aufgetreten. component_loading = Lade %s … -contributors.what = Beiträge \ No newline at end of file +contributors.what = Beiträge diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 3065990e8f..212cb3f918 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -688,7 +688,7 @@ uploaded_avatar_is_too_big=Το μέγεθος αρχείου που ανέβη update_avatar_success=Η εικόνα σας έχει ενημερωθεί. update_user_avatar_success=Το avatar του χρήστη ενημερώθηκε. -change_password=Ενημέρωση Κωδικού Πρόσβασης +update_password=Ενημέρωση Κωδικού Πρόσβασης old_password=Τρέχων Κωδικός Πρόσβασης new_password=Νέος Κωδικός Πρόσβασης retype_new_password=Επιβεβαίωση Νέου Κωδικού Πρόσβασης diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index bdae9a29ac..a8e7601df4 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -710,16 +710,17 @@ uploaded_avatar_is_too_big = The uploaded file size (%d KiB) exceeds the maximum update_avatar_success = Your avatar has been updated. update_user_avatar_success = The user's avatar has been updated. -change_password = Update Password -old_password = Current Password -new_password = New Password -retype_new_password = Confirm New Password +change_password = Change password +update_password = Update Password +old_password = Current password +new_password = New password +retype_new_password = Confirm new password password_incorrect = The current password is incorrect. change_password_success = Your password has been updated. Sign in using your new password from now on. password_change_disabled = Non-local users cannot update their password through the Forgejo web interface. emails = Email Addresses -manage_emails = Manage Email Addresses +manage_emails = Manage email addresses manage_themes = Select default theme manage_openid = Manage OpenID Addresses email_desc = Your primary email address will be used for notifications, password recovery and, provided that it is not hidden, web-based Git operations. @@ -740,7 +741,7 @@ theme_update_error = The selected theme does not exist. openid_deletion = Remove OpenID Address openid_deletion_desc = Removing this OpenID address from your account will prevent you from signing in with it. Continue? openid_deletion_success = The OpenID address has been removed. -add_new_email = Add New Email Address +add_new_email = Add email address add_new_openid = Add New OpenID URI add_email = Add Email Address add_openid = Add OpenID URI @@ -758,7 +759,7 @@ manage_gpg_keys = Manage GPG Keys add_key = Add Key ssh_desc = These public SSH keys are associated with your account. The corresponding private keys allow full access to your repositories. SSH keys that have been verified can be used to verify SSH-signed Git commits. principal_desc = These SSH certificate principals are associated with your account and allow full access to your repositories. -gpg_desc = These public GPG keys are associated with your account. Keep your private keys safe as they allow commits to be verified. +gpg_desc = These public GPG keys are associated with your account and used to verify your commits. Keep your private keys safe as they allow to sign commits with your identity. ssh_helper = Need help? Have a look at the guide to create your own SSH keys or solve common problems you may encounter using SSH. gpg_helper = Need help? Have a look at the guide about GPG. add_new_key = Add SSH Key @@ -881,7 +882,7 @@ oauth2_application_remove_description = Removing an OAuth2 application will prev oauth2_application_locked = Forgejo pre-registers some OAuth2 applications on startup if enabled in config. To prevent unexpected behavior, these can neither be edited nor removed. Please refer to the OAuth2 documentation for more information. authorized_oauth2_applications = Authorized OAuth2 Applications -authorized_oauth2_applications_description = You have granted access to your personal Forgejo account to these third party applications. Please revoke access for applications you no longer need. +authorized_oauth2_applications_description = You have granted access to your personal Forgejo account to these third party applications. Please revoke access for applications that are no longer in use. revoke_key = Revoke revoke_oauth2_grant = Revoke Access revoke_oauth2_grant_description = Revoking access for this third party application will prevent this application from accessing your data. Are you sure? @@ -926,7 +927,7 @@ hooks.desc = Add webhooks which will be triggered for all repositoriesCANNOT be undone. @@ -1671,7 +1672,7 @@ issues.dependency.issue_closing_blockedby = Closing this issue is blocked by the issues.dependency.issue_close_blocks = This issue blocks closing of the following issues issues.dependency.pr_close_blocks = This pull request blocks closing of the following issues issues.dependency.issue_close_blocked = You need to close all issues blocking this issue before you can close it. -issues.dependency.issue_batch_close_blocked = "Cannot batch close issues that you choose, because issue #%d still has open dependencies" +issues.dependency.issue_batch_close_blocked = "Cannot batch close chosen issues, because issue #%d still has open dependencies" issues.dependency.pr_close_blocked = You need to close all issues blocking this pull request before you can merge it. issues.dependency.blocks_short = Blocks issues.dependency.blocked_by_short = Depends on @@ -2842,7 +2843,7 @@ dashboard.reinit_missing_repos = Reinitialize all missing Git repositories for w dashboard.sync_external_users = Synchronize external user data dashboard.cleanup_hook_task_table = Cleanup hook_task table dashboard.cleanup_packages = Cleanup expired packages -dashboard.cleanup_actions = Cleanup actions expired logs and artifacts +dashboard.cleanup_actions = Cleanup expired logs and artifacts from actions dashboard.server_uptime = Server Uptime dashboard.current_goroutine = Current Goroutines dashboard.current_memory_usage = Current Memory Usage @@ -3536,7 +3537,7 @@ owner.settings.cargo.rebuild.success = The Cargo index was successfully rebuild. owner.settings.cleanuprules.title = Manage Cleanup Rules owner.settings.cleanuprules.add = Add Cleanup Rule owner.settings.cleanuprules.edit = Edit Cleanup Rule -owner.settings.cleanuprules.none = No cleanup rules available. Please consult the documentation. +owner.settings.cleanuprules.none = There are no cleanup rules yet. owner.settings.cleanuprules.preview = Cleanup Rule Preview owner.settings.cleanuprules.preview.overview = %d packages are scheduled to be removed. owner.settings.cleanuprules.preview.none = Cleanup rule does not match any packages. diff --git a/options/locale/locale_eo.ini b/options/locale/locale_eo.ini index a2d576b9ea..0f3f2ea010 100644 --- a/options/locale/locale_eo.ini +++ b/options/locale/locale_eo.ini @@ -674,7 +674,7 @@ manage_emails = Mastrumi retpoŝtadresojn generate_token_name_duplicate = %s jam uziĝis kiel programnomo iam. Bonvolu elekti novan. permission_read = Lega ssh_helper = Ĉu bezonas helpon? Legetu la gvidon pri kreado de SSH-ŝlosiloj aŭ ripari oftajn problemojn kiujn vi eble trafus uzante SSH. -change_password = Konservi pasvorton +update_password = Konservi pasvorton ssh_key_been_used = Ĉi tiu SSH-ŝlosilo estas jam aldonita al la servilo. password_change_disabled = Nelokaj uzantoj ne povas ŝanĝi sian pasvorton per la Forgejo retfasado. emails = Retpoŝadresoj @@ -714,4 +714,4 @@ follow = Aboni followers = Abonantoj block_user.detail_2 = La uzanto ne povos interagi viajn deponejojn, erarojn, kaj komentojn. block_user = Bloki uzanton -change_avatar = Ŝanĝi vian profilbildon… \ No newline at end of file +change_avatar = Ŝanĝi vian profilbildon… diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index a941799e83..9230b937cd 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -706,7 +706,7 @@ uploaded_avatar_is_too_big=El tamaño del archivo subido (%d KiB) excede el tama update_avatar_success=Su avatar ha sido actualizado. update_user_avatar_success=El avatar del usuario se ha actualizado. -change_password=Actualizar contraseña +update_password=Actualizar contraseña old_password=Contraseña actual new_password=Nueva contraseña retype_new_password=Confirme la nueva contraseña diff --git a/options/locale/locale_fa-IR.ini b/options/locale/locale_fa-IR.ini index fb3da145c6..4b4ecd4a0a 100644 --- a/options/locale/locale_fa-IR.ini +++ b/options/locale/locale_fa-IR.ini @@ -526,7 +526,7 @@ uploaded_avatar_not_a_image=فایل بار‌گذاری شده تصویر نم update_avatar_success=آواتار شما تغییر کرد. update_user_avatar_success=آواتار کاربر بروز رسانی شده است. -change_password=تغییر گذرواژه +update_password=تغییر گذرواژه old_password=گذارواژه فعلی new_password=گذرواژه جدید password_incorrect=گذرواژه فعلی شما اشتباه است. diff --git a/options/locale/locale_fi-FI.ini b/options/locale/locale_fi-FI.ini index f038532460..c0710f2cd8 100644 --- a/options/locale/locale_fi-FI.ini +++ b/options/locale/locale_fi-FI.ini @@ -500,7 +500,7 @@ delete_current_avatar=Poista nykyinen profiilikuva uploaded_avatar_not_a_image=Palvelimelle lähetetty tiedosto ei ole kuva. update_avatar_success=Profiilikuva on päivitetty. -change_password=Päivitä salasana +update_password=Päivitä salasana old_password=Nykyinen salasana new_password=Uusi salasana password_incorrect=Nykyinen salasanasi on virheellinen. diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index dfb0422991..7c41c3e4e4 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -706,7 +706,7 @@ uploaded_avatar_is_too_big=La taille du fichier téléversé (%d Kio) dépasse l update_avatar_success=Votre avatar a été mis à jour. update_user_avatar_success=L'avatar de l'utilisateur a été mis à jour. -change_password=Modifier le mot de passe +update_password=Modifier le mot de passe old_password=Mot de passe actuel new_password=Nouveau mot de passe retype_new_password=Confirmer le nouveau mot de passe diff --git a/options/locale/locale_hu-HU.ini b/options/locale/locale_hu-HU.ini index 05d3682147..d61c40671c 100644 --- a/options/locale/locale_hu-HU.ini +++ b/options/locale/locale_hu-HU.ini @@ -425,7 +425,7 @@ delete_current_avatar=Jelenlegi profilkép törlése uploaded_avatar_not_a_image=A feltöltött fájl nem kép. update_avatar_success=A profilképe frissítve lett. -change_password=Jelszó frissítése +update_password=Jelszó frissítése old_password=Jelenlegi jelszó new_password=Új jelszó password_incorrect=A megadott jelenlegi jelszó helytelen. diff --git a/options/locale/locale_id-ID.ini b/options/locale/locale_id-ID.ini index 444d13745f..9e1065756d 100644 --- a/options/locale/locale_id-ID.ini +++ b/options/locale/locale_id-ID.ini @@ -347,7 +347,7 @@ delete_current_avatar=Hapus Avatar Saat Ini uploaded_avatar_not_a_image=Berkas yang diunggah bukanlah gambar. update_avatar_success=Avatar Anda telah diperbarui. -change_password=Perbarui kata sandi +update_password=Perbarui kata sandi old_password=Kata Sandi Saat Ini new_password=Kata Sandi Baru password_incorrect=Kata sandi saat ini salah. diff --git a/options/locale/locale_is-IS.ini b/options/locale/locale_is-IS.ini index b68777a1de..a51b8c0a44 100644 --- a/options/locale/locale_is-IS.ini +++ b/options/locale/locale_is-IS.ini @@ -472,7 +472,7 @@ uploaded_avatar_not_a_image=Skráin sem hlaðin var upp er ekki mynd. update_avatar_success=Notandamynd þín hefur verið uppfærð. update_user_avatar_success=Notandamynd þessara notanda hefur verið uppfærð. -change_password=Uppfæra Lykilorð +update_password=Uppfæra Lykilorð old_password=Núverandi Lykilorð new_password=Nýtt Lykilorð password_incorrect=Núverandi lykilorðið er rangt. diff --git a/options/locale/locale_it-IT.ini b/options/locale/locale_it-IT.ini index 82e089111f..191242731c 100644 --- a/options/locale/locale_it-IT.ini +++ b/options/locale/locale_it-IT.ini @@ -569,7 +569,7 @@ uploaded_avatar_not_a_image=Il file caricato non è un'immagine. update_avatar_success=Il tuo avatar è stato aggiornato. update_user_avatar_success=L'avatar dell'utente è stato aggiornato. -change_password=Aggiorna Password +update_password=Aggiorna Password old_password=Password attuale new_password=Nuova Password password_incorrect=La password attuale non è corretta. diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 3232e1fca8..ba0813ace4 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -702,7 +702,7 @@ uploaded_avatar_is_too_big=アップロードされたファイルサイズ(%d K update_avatar_success=アバターを更新しました。 update_user_avatar_success=ユーザーのアバターを更新しました。 -change_password=パスワードを更新 +update_password=パスワードを更新 old_password=現在のパスワード new_password=新しいパスワード retype_new_password=新しいパスワードの確認 diff --git a/options/locale/locale_ko-KR.ini b/options/locale/locale_ko-KR.ini index a64094e7da..b10f146759 100644 --- a/options/locale/locale_ko-KR.ini +++ b/options/locale/locale_ko-KR.ini @@ -403,7 +403,7 @@ delete_current_avatar=현재 아바타 삭제 uploaded_avatar_not_a_image=업로드 된 파일은 이미지가 아닙니다. update_avatar_success=아바타가 변경되었습니다. -change_password=비밀번호 변경 +update_password=비밀번호 변경 old_password=현재 비밀번호 new_password=새 비밀번호 password_incorrect=현재 비밀번호가 올바르지 않습니다. diff --git a/options/locale/locale_lv-LV.ini b/options/locale/locale_lv-LV.ini index 9d2f6484dc..3057b871c7 100644 --- a/options/locale/locale_lv-LV.ini +++ b/options/locale/locale_lv-LV.ini @@ -661,7 +661,7 @@ uploaded_avatar_not_a_image=Augšupielādētais fails nav attēls. update_avatar_success=Profila attēls tika saglabāts. update_user_avatar_success=Lietotāja profila attēls tika atjaunots. -change_password=Mainīt paroli +update_password=Mainīt paroli old_password=Pašreizējā parole new_password=Jauna parole password_incorrect=Ievadīta nepareiza pašreizējā parole. diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 5649570de1..4d7a5b3024 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -695,7 +695,7 @@ uploaded_avatar_not_a_image=Het geüploade bestand is geen afbeelding. update_avatar_success=Je avatar is bijgewerkt. update_user_avatar_success=De avatar van de gebruiker is bijgewerkt. -change_password=Wachtwoord bijwerken +update_password=Wachtwoord bijwerken old_password=Huidige wachtwoord new_password=Nieuw wachtwoord password_incorrect=Het wachtwoord is niet correct. @@ -3471,4 +3471,4 @@ component_loading_info = Dit kan even duren… component_failed_to_load = Er is een onverwachte fout opgetreden. contributors.what = bijdragen component_loading_failed = %s kon niet worden geladen -component_loading = Bezig met laden van %s... \ No newline at end of file +component_loading = Bezig met laden van %s... diff --git a/options/locale/locale_pl-PL.ini b/options/locale/locale_pl-PL.ini index 0337580928..c26a19d9e3 100644 --- a/options/locale/locale_pl-PL.ini +++ b/options/locale/locale_pl-PL.ini @@ -553,7 +553,7 @@ delete_current_avatar=Usuń obecny Avatar uploaded_avatar_not_a_image=Załadowany plik nie jest obrazem. update_avatar_success=Twój awatar został zmieniony. -change_password=Aktualizuj hasło +update_password=Aktualizuj hasło old_password=Aktualne hasło new_password=Nowe hasło password_incorrect=Bieżące hasło nie jest prawidłowe. diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 66a637245b..0eec655460 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -705,7 +705,7 @@ uploaded_avatar_is_too_big=O tamanho do arquivo enviado (%d KiB) excede o tamanh update_avatar_success=Seu avatar foi atualizado. update_user_avatar_success=O avatar do usuário foi atualizado. -change_password=Atualizar senha +update_password=Atualizar senha old_password=Senha atual new_password=Nova senha retype_new_password=Confirmar nova senha diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index bf0e624502..0a6eb11365 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -688,7 +688,7 @@ uploaded_avatar_is_too_big=O tamanho do ficheiro carregado (%d KiB) excede o tam update_avatar_success=O seu avatar foi substituído. update_user_avatar_success=O avatar do utilizador foi modificado. -change_password=Substituir a senha +update_password=Substituir a senha old_password=Senha corrente new_password=Nova senha retype_new_password=Confirme a nova senha diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 0e1f1cc396..a7c5bec349 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -707,7 +707,7 @@ uploaded_avatar_is_too_big=Размер загружаемого файла (%d update_avatar_success=Ваш аватар был изменен. update_user_avatar_success=Аватар пользователя обновлён. -change_password=Обновить пароль +update_password=Обновить пароль old_password=Текущий пароль new_password=Новый пароль retype_new_password=Подтверждение нового пароля @@ -3636,4 +3636,4 @@ component_loading_failed = Не удалось загрузить %s component_failed_to_load = Случилась непредвиденная ошибка. contributors.what = участие component_loading = Загрузка %s... -component_loading_info = Это займёт некоторое время… \ No newline at end of file +component_loading_info = Это займёт некоторое время… diff --git a/options/locale/locale_si-LK.ini b/options/locale/locale_si-LK.ini index 8ba8eaedb6..885907df15 100644 --- a/options/locale/locale_si-LK.ini +++ b/options/locale/locale_si-LK.ini @@ -513,7 +513,7 @@ uploaded_avatar_not_a_image=උඩුගත කරන ලද ගොනුව ර update_avatar_success=ඔබගේ අවතාරය යාවත්කාලීන කර ඇත. update_user_avatar_success=පරිශීලකයාගේ අවතාරය යාවත්කාලීන කර ඇත. -change_password=මුරපදය යාවත්කාල කරන්න +update_password=මුරපදය යාවත්කාල කරන්න old_password=වත්මන් මුරපදය new_password=නව මුරපදය password_incorrect=වත්මන් මුරපදය වැරදිය. diff --git a/options/locale/locale_sk-SK.ini b/options/locale/locale_sk-SK.ini index 85e0b2b93d..c8fe29e56d 100644 --- a/options/locale/locale_sk-SK.ini +++ b/options/locale/locale_sk-SK.ini @@ -642,7 +642,7 @@ uploaded_avatar_not_a_image=Nahraný súbor nieje obrázok. update_avatar_success=Váš avatar sa aktualizoval. update_user_avatar_success=Užívateľov avatar bol aktualizovaný. -change_password=Aktualizovať heslo +update_password=Aktualizovať heslo old_password=Aktuálne heslo new_password=Nové heslo password_incorrect=Aktuálne heslo nie je správne. diff --git a/options/locale/locale_sl.ini b/options/locale/locale_sl.ini index 4a4aa1377a..5fc59990bb 100644 --- a/options/locale/locale_sl.ini +++ b/options/locale/locale_sl.ini @@ -363,4 +363,4 @@ show_only_unarchived = Prikaz samo nearhiviranih show_private = Zasebno show_both_private_public = Prikaz javnih in zasebnih show_only_private = Prikaz samo zasebno -issues.in_your_repos = V vašem repozitorijev \ No newline at end of file +issues.in_your_repos = V vašem repozitorijev diff --git a/options/locale/locale_sv-SE.ini b/options/locale/locale_sv-SE.ini index 8eb27d2d6b..2e520b3b84 100644 --- a/options/locale/locale_sv-SE.ini +++ b/options/locale/locale_sv-SE.ini @@ -448,7 +448,7 @@ delete_current_avatar=Tag bort aktuell avatar uploaded_avatar_not_a_image=Den uppladdade filen är inte en bild. update_avatar_success=Din avatar har blivit uppdaterad. -change_password=Ändra Lösenordet +update_password=Ändra Lösenordet old_password=Nuvarande lösenord new_password=Nytt lösenord password_incorrect=Det nuvarande lösenordet är felaktigt. diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 094a970f42..6edc30d1f8 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -688,7 +688,7 @@ uploaded_avatar_is_too_big=Yüklenen dosyanın boyutu (%d KiB), azami boyutu (%d update_avatar_success=Profil resminiz değiştirildi. update_user_avatar_success=Kullanıcının avatarı güncellendi. -change_password=Parolayı Güncelle +update_password=Parolayı Güncelle old_password=Mevcut Parola new_password=Yeni Parola retype_new_password=Yeni Parolayı Onayla diff --git a/options/locale/locale_uk-UA.ini b/options/locale/locale_uk-UA.ini index 07bb39324a..376e7efb9e 100644 --- a/options/locale/locale_uk-UA.ini +++ b/options/locale/locale_uk-UA.ini @@ -589,7 +589,7 @@ uploaded_avatar_not_a_image=Завантажений файл не є зобра update_avatar_success=Ваш аватар був змінений. update_user_avatar_success=Аватар користувача оновлено. -change_password=Оновити пароль +update_password=Оновити пароль old_password=Поточний пароль new_password=Новий пароль password_incorrect=Поточний пароль неправильний. diff --git a/options/locale/locale_zh-CN.ini b/options/locale/locale_zh-CN.ini index ea1a6cf045..47bcf2639a 100644 --- a/options/locale/locale_zh-CN.ini +++ b/options/locale/locale_zh-CN.ini @@ -707,7 +707,7 @@ uploaded_avatar_is_too_big=上传的文件大小(%d KiB) 超过最大限制(%d K update_avatar_success=您的头像已更新。 update_user_avatar_success=用户头像已更新。 -change_password=更新密码 +update_password=更新密码 old_password=当前密码 new_password=新的密码 retype_new_password=确认新密码 diff --git a/options/locale/locale_zh-TW.ini b/options/locale/locale_zh-TW.ini index 604f727292..0cdf8f3ceb 100644 --- a/options/locale/locale_zh-TW.ini +++ b/options/locale/locale_zh-TW.ini @@ -641,7 +641,7 @@ uploaded_avatar_not_a_image=上傳的檔案不是圖片 update_avatar_success=您的大頭貼已更新 update_user_avatar_success=已更新使用者的大頭貼。 -change_password=更新密碼 +update_password=更新密碼 old_password=目前的密碼 new_password=新的密碼 retype_new_password=確認新密碼 diff --git a/templates/user/auth/change_passwd_inner.tmpl b/templates/user/auth/change_passwd_inner.tmpl index cffc798a64..74c2b1a561 100644 --- a/templates/user/auth/change_passwd_inner.tmpl +++ b/templates/user/auth/change_passwd_inner.tmpl @@ -2,7 +2,7 @@ {{template "base/alert" .}} {{end}}

      - {{ctx.Locale.Tr "settings.change_password"}} + {{ctx.Locale.Tr "settings.update_password"}}

      @@ -17,7 +17,7 @@
      - +
      diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl index 7c6fd49a08..820d48a94b 100644 --- a/templates/user/settings/account.tmpl +++ b/templates/user/settings/account.tmpl @@ -1,7 +1,7 @@ {{template "user/settings/layout_head" (dict "ctxData" . "pageClass" "user settings account")}}

      - {{ctx.Locale.Tr "settings.password"}} + {{ctx.Locale.Tr "settings.change_password"}}

      {{if or (.SignedUser.IsLocal) (.SignedUser.IsOAuth2)}} @@ -24,7 +24,7 @@
      - + {{ctx.Locale.Tr "auth.forgot_password"}}
      From e8585eff5c997783a5200bf6d9b89c70f1a6a6a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Fri, 16 Feb 2024 17:59:02 +0100 Subject: [PATCH 137/271] Do not run e2e tests in parallel --- playwright.config.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/playwright.config.js b/playwright.config.js index b7badf1cc0..6c0029bb5d 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -11,6 +11,13 @@ export default { testDir: './tests/e2e/', testMatch: /.*\.test\.e2e\.js/, // Match any .test.e2e.js files + /** + * Only run one test at a time, running multiple could lead to a inconsistent + * database state. + */ + fullyParallel: false, + workers: 1, + /* Maximum time one test can run for. */ timeout: 30 * 1000, From 9018b4c9fe44cf47e4dd906a0177482fb9baed62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Malte=20J=C3=BCrgens?= Date: Fri, 16 Feb 2024 18:34:43 +0100 Subject: [PATCH 138/271] Enable e2e testing on Firefox --- playwright.config.js | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/playwright.config.js b/playwright.config.js index b7badf1cc0..38851e2d12 100644 --- a/playwright.config.js +++ b/playwright.config.js @@ -64,13 +64,12 @@ export default { }, }, - // disabled because of https://github.com/go-gitea/gitea/issues/21355 - // { - // name: 'firefox', - // use: { - // ...devices['Desktop Firefox'], - // }, - // }, + { + name: 'firefox', + use: { + ...devices['Desktop Firefox'], + }, + }, { name: 'webkit', From 785f336c127b8c6f050defacfe9a6b4b67f306d2 Mon Sep 17 00:00:00 2001 From: "Panagiotis \"Ivory\" Vasilopoulos" Date: Thu, 22 Feb 2024 22:12:44 +0100 Subject: [PATCH 139/271] [UI] Actions: Improve frontend testing --- tests/integration/actions_route_test.go | 31 ++++++++++++++----------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/tests/integration/actions_route_test.go b/tests/integration/actions_route_test.go index aca67a40c0..aed9327a88 100644 --- a/tests/integration/actions_route_test.go +++ b/tests/integration/actions_route_test.go @@ -22,6 +22,15 @@ import ( "github.com/stretchr/testify/assert" ) +func GetWorkflowRunRedirectURI(t *testing.T, repo_url string, workflow string) string { + t.Helper() + + req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/workflows/%s/runs/latest", repo_url, workflow)) + resp := MakeRequest(t, req, http.StatusTemporaryRedirect) + + return resp.Header().Get("Location") +} + func TestActionsWebRouteLatestWorkflowRun(t *testing.T) { onGiteaRun(t, func(t *testing.T, u *url.URL) { user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) @@ -44,13 +53,7 @@ func TestActionsWebRouteLatestWorkflowRun(t *testing.T) { ) defer f() - // helpers - getWorkflowRunRedirectURI := func(workflow string) string { - req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/workflows/%s/runs/latest", repo.HTMLURL(), workflow)) - resp := MakeRequest(t, req, http.StatusTemporaryRedirect) - - return resp.Header().Get("Location") - } + repoURL := repo.HTMLURL() t.Run("valid workflows", func(t *testing.T) { defer tests.PrintCurrentTest(t)() @@ -59,8 +62,8 @@ func TestActionsWebRouteLatestWorkflowRun(t *testing.T) { assert.Equal(t, 2, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID})) // Get the redirect URIs for both workflows - workflowOneURI := getWorkflowRunRedirectURI("workflow-1.yml") - workflowTwoURI := getWorkflowRunRedirectURI("workflow-2.yml") + workflowOneURI := GetWorkflowRunRedirectURI(t, repoURL, "workflow-1.yml") + workflowTwoURI := GetWorkflowRunRedirectURI(t, repoURL, "workflow-2.yml") // Verify that the two are different. assert.NotEqual(t, workflowOneURI, workflowTwoURI) @@ -82,7 +85,7 @@ func TestActionsWebRouteLatestWorkflowRun(t *testing.T) { // Get the redirect URI workflow := "workflow-1.yml" - workflowOneURI := getWorkflowRunRedirectURI(workflow) + workflowOneURI := GetWorkflowRunRedirectURI(t, repoURL, workflow) // Fetch the page that shows information about the run initiated by "workflow-1.yml". // routers/web/repo/actions/view.go: data-workflow-url is constructed using data-workflow-name. @@ -91,21 +94,21 @@ func TestActionsWebRouteLatestWorkflowRun(t *testing.T) { htmlDoc := NewHTMLParser(t, resp.Body) // Verify that URL of the workflow is shown correctly. - rightURL := fmt.Sprintf("/user2/actionsTestRepo/actions?workflow=%s", workflow) - htmlDoc.AssertElement(t, fmt.Sprintf("#repo-action-view[data-workflow-url=\"%s\"]", rightURL), true) + expectedURL := fmt.Sprintf("/user2/actionsTestRepo/actions?workflow=%s", workflow) + htmlDoc.AssertElement(t, fmt.Sprintf("#repo-action-view[data-workflow-url=\"%s\"]", expectedURL), true) }) t.Run("existing workflow, non-existent branch", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/workflows/workflow-1.yml/runs/latest?branch=foobar", repo.HTMLURL())) + req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/workflows/workflow-1.yml/runs/latest?branch=foobar", repoURL)) MakeRequest(t, req, http.StatusNotFound) }) t.Run("non-existing workflow", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/workflows/workflow-3.yml/runs/latest", repo.HTMLURL())) + req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/workflows/workflow-3.yml/runs/latest", repoURL)) MakeRequest(t, req, http.StatusNotFound) }) }) From f68bc0ec6ac437b8c2339719b6063068fc6bbdfb Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 22 Feb 2024 22:25:19 +0100 Subject: [PATCH 140/271] [REFACTOR] Simplify converting struct to map in admin stats - Instead of relying on JSON to convert the struct to map, use `reflect` to do this conversion. Also simplify it a bit by only passing one variable to the template. - This avoids issues where the conversion to JSON causes changes in the value, for example huge numbers are converted to its scientific notation but are consequently not converted back when being displayed. - Adds unit tests. - Resolves an issue where the amount of comments is being displayed in scientific notation on Codeberg. --- routers/web/admin/admin.go | 35 +++++++++++------------- routers/web/admin/admin_test.go | 48 +++++++++++++++++++++++++++++++++ templates/admin/stats.tmpl | 4 +-- 3 files changed, 65 insertions(+), 22 deletions(-) diff --git a/routers/web/admin/admin.go b/routers/web/admin/admin.go index 9fbd429f74..58bb281731 100644 --- a/routers/web/admin/admin.go +++ b/routers/web/admin/admin.go @@ -7,8 +7,8 @@ package admin import ( "fmt" "net/http" + "reflect" "runtime" - "sort" "time" activities_model "code.gitea.io/gitea/models/activities" @@ -16,7 +16,6 @@ import ( "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/graceful" - "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/updatechecker" @@ -225,26 +224,22 @@ func CronTasks(ctx *context.Context) { func MonitorStats(ctx *context.Context) { ctx.Data["Title"] = ctx.Tr("admin.monitor.stats") ctx.Data["PageIsAdminMonitorStats"] = true - bs, err := json.Marshal(activities_model.GetStatistic(ctx).Counter) - if err != nil { - ctx.ServerError("MonitorStats", err) - return - } - statsCounter := map[string]any{} - err = json.Unmarshal(bs, &statsCounter) - if err != nil { - ctx.ServerError("MonitorStats", err) - return - } - statsKeys := make([]string, 0, len(statsCounter)) - for k := range statsCounter { - if statsCounter[k] == nil { + modelStats := activities_model.GetStatistic(ctx).Counter + stats := map[string]any{} + + // To avoid manually converting the values of the stats struct to an map, + // and to avoid using JSON to do this for us (JSON encoder converts numbers to + // scientific notation). Use reflect to convert the struct to an map. + rv := reflect.ValueOf(modelStats) + for i := 0; i < rv.NumField(); i++ { + field := rv.Field(i) + // Preserve old behavior, do not show arrays that are empty. + if field.Kind() == reflect.Slice && field.Len() == 0 { continue } - statsKeys = append(statsKeys, k) + stats[rv.Type().Field(i).Name] = field.Interface() } - sort.Strings(statsKeys) - ctx.Data["StatsKeys"] = statsKeys - ctx.Data["StatsCounter"] = statsCounter + + ctx.Data["Stats"] = stats ctx.HTML(http.StatusOK, tplStats) } diff --git a/routers/web/admin/admin_test.go b/routers/web/admin/admin_test.go index 2b65ab3ea3..452291e179 100644 --- a/routers/web/admin/admin_test.go +++ b/routers/web/admin/admin_test.go @@ -6,6 +6,11 @@ package admin import ( "testing" + activities_model "code.gitea.io/gitea/models/activities" + "code.gitea.io/gitea/models/unittest" + "code.gitea.io/gitea/modules/contexttest" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" "github.com/stretchr/testify/assert" ) @@ -66,3 +71,46 @@ func TestShadowPassword(t *testing.T) { assert.EqualValues(t, k.Result, shadowPassword(k.Provider, k.CfgItem)) } } + +func TestMonitorStats(t *testing.T) { + unittest.PrepareTestEnv(t) + + t.Run("Normal", func(t *testing.T) { + defer test.MockVariableValue(&setting.Metrics.EnabledIssueByLabel, false)() + defer test.MockVariableValue(&setting.Metrics.EnabledIssueByRepository, false)() + + ctx, _ := contexttest.MockContext(t, "admin/stats") + MonitorStats(ctx) + + // Test some of the stats manually. + mappedStats := ctx.Data["Stats"].(map[string]any) + stats := activities_model.GetStatistic(ctx).Counter + + assert.EqualValues(t, stats.Comment, mappedStats["Comment"]) + assert.EqualValues(t, stats.Issue, mappedStats["Issue"]) + assert.EqualValues(t, stats.User, mappedStats["User"]) + assert.EqualValues(t, stats.Milestone, mappedStats["Milestone"]) + + // Ensure that these aren't set. + assert.Empty(t, stats.IssueByLabel) + assert.Empty(t, stats.IssueByRepository) + assert.Nil(t, mappedStats["IssueByLabel"]) + assert.Nil(t, mappedStats["IssueByRepository"]) + }) + + t.Run("IssueByX", func(t *testing.T) { + defer test.MockVariableValue(&setting.Metrics.EnabledIssueByLabel, true)() + defer test.MockVariableValue(&setting.Metrics.EnabledIssueByRepository, true)() + + ctx, _ := contexttest.MockContext(t, "admin/stats") + MonitorStats(ctx) + + mappedStats := ctx.Data["Stats"].(map[string]any) + stats := activities_model.GetStatistic(ctx).Counter + + assert.NotEmpty(t, stats.IssueByLabel) + assert.NotEmpty(t, stats.IssueByRepository) + assert.EqualValues(t, stats.IssueByLabel, mappedStats["IssueByLabel"]) + assert.EqualValues(t, stats.IssueByRepository, mappedStats["IssueByRepository"]) + }) +} diff --git a/templates/admin/stats.tmpl b/templates/admin/stats.tmpl index 04fa862a85..70f2aa7fb4 100644 --- a/templates/admin/stats.tmpl +++ b/templates/admin/stats.tmpl @@ -5,10 +5,10 @@
      - {{range $statsKey := .StatsKeys}} + {{range $statsKey, $statsValue := .Stats}} - + {{end}}
      {{$statsKey}}{{index $.StatsCounter $statsKey}}{{$statsValue}}
      From 2a0a5c6ec0cf61b97d7b8ec74bff6ab6a8adc0be Mon Sep 17 00:00:00 2001 From: "Panagiotis \"Ivory\" Vasilopoulos" Date: Thu, 22 Feb 2024 22:26:11 +0100 Subject: [PATCH 141/271] [UI] Actions: Oops, forgot to lint the tests. --- tests/integration/actions_route_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integration/actions_route_test.go b/tests/integration/actions_route_test.go index aed9327a88..059f5bf334 100644 --- a/tests/integration/actions_route_test.go +++ b/tests/integration/actions_route_test.go @@ -22,7 +22,7 @@ import ( "github.com/stretchr/testify/assert" ) -func GetWorkflowRunRedirectURI(t *testing.T, repo_url string, workflow string) string { +func GetWorkflowRunRedirectURI(t *testing.T, repo_url, workflow string) string { t.Helper() req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/workflows/%s/runs/latest", repo_url, workflow)) From 62f3ff607424263014a0c5eb6b0507f13e757a5e Mon Sep 17 00:00:00 2001 From: "Panagiotis \"Ivory\" Vasilopoulos" Date: Thu, 22 Feb 2024 22:31:26 +0100 Subject: [PATCH 142/271] [UI] Actions: I will always run make fmt before pushing I will always run make fmt before pushing I will always run make fmt before pushing I will always run make fmt before pushing --- tests/integration/actions_route_test.go | 4 ++-- tests/integration/pull_reopen_test.go | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/integration/actions_route_test.go b/tests/integration/actions_route_test.go index 059f5bf334..b4c3db4b4b 100644 --- a/tests/integration/actions_route_test.go +++ b/tests/integration/actions_route_test.go @@ -22,10 +22,10 @@ import ( "github.com/stretchr/testify/assert" ) -func GetWorkflowRunRedirectURI(t *testing.T, repo_url, workflow string) string { +func GetWorkflowRunRedirectURI(t *testing.T, repoURL, workflow string) string { t.Helper() - req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/workflows/%s/runs/latest", repo_url, workflow)) + req := NewRequest(t, "GET", fmt.Sprintf("%s/actions/workflows/%s/runs/latest", repoURL, workflow)) resp := MakeRequest(t, req, http.StatusTemporaryRedirect) return resp.Header().Get("Location") diff --git a/tests/integration/pull_reopen_test.go b/tests/integration/pull_reopen_test.go index d8dfffc36a..51f208794e 100644 --- a/tests/integration/pull_reopen_test.go +++ b/tests/integration/pull_reopen_test.go @@ -26,6 +26,7 @@ import ( repo_service "code.gitea.io/gitea/services/repository" files_service "code.gitea.io/gitea/services/repository/files" "code.gitea.io/gitea/tests" + "github.com/stretchr/testify/assert" ) From c551d3f3ab13379b0740fc45bc4dfc8f2fb84e16 Mon Sep 17 00:00:00 2001 From: FuXiaoHei Date: Sun, 18 Feb 2024 18:33:50 +0800 Subject: [PATCH 143/271] Artifact deletion in actions ui (#27172) Add deletion link in runs view page. Fix #26315 ![image](https://github.com/go-gitea/gitea/assets/2142787/aa65a4ab-f434-4deb-b953-21e63c212033) When click deletion button. It marks this artifact `need-delete`. This artifact would be deleted when actions cleanup cron task. --- models/actions/artifact.go | 22 ++++++++++ options/locale/locale_en-US.ini | 1 + routers/web/repo/actions/view.go | 51 +++++++++++++++++++----- routers/web/web.go | 1 + services/actions/cleanup.go | 38 +++++++++++++++++- templates/repo/actions/view.tmpl | 1 + web_src/js/components/RepoActionView.vue | 15 ++++++- web_src/js/svg.js | 2 + 8 files changed, 120 insertions(+), 11 deletions(-) diff --git a/models/actions/artifact.go b/models/actions/artifact.go index 5390f6288f..3d0a288e62 100644 --- a/models/actions/artifact.go +++ b/models/actions/artifact.go @@ -26,6 +26,8 @@ const ( ArtifactStatusUploadConfirmed // 2, ArtifactStatusUploadConfirmed is the status of an artifact upload that is confirmed ArtifactStatusUploadError // 3, ArtifactStatusUploadError is the status of an artifact upload that is errored ArtifactStatusExpired // 4, ArtifactStatusExpired is the status of an artifact that is expired + ArtifactStatusPendingDeletion // 5, ArtifactStatusPendingDeletion is the status of an artifact that is pending deletion + ArtifactStatusDeleted // 6, ArtifactStatusDeleted is the status of an artifact that is deleted ) func init() { @@ -147,8 +149,28 @@ func ListNeedExpiredArtifacts(ctx context.Context) ([]*ActionArtifact, error) { Where("expired_unix < ? AND status = ?", timeutil.TimeStamp(time.Now().Unix()), ArtifactStatusUploadConfirmed).Find(&arts) } +// ListPendingDeleteArtifacts returns all artifacts in pending-delete status. +// limit is the max number of artifacts to return. +func ListPendingDeleteArtifacts(ctx context.Context, limit int) ([]*ActionArtifact, error) { + arts := make([]*ActionArtifact, 0, limit) + return arts, db.GetEngine(ctx). + Where("status = ?", ArtifactStatusPendingDeletion).Limit(limit).Find(&arts) +} + // SetArtifactExpired sets an artifact to expired func SetArtifactExpired(ctx context.Context, artifactID int64) error { _, err := db.GetEngine(ctx).Where("id=? AND status = ?", artifactID, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusExpired)}) return err } + +// SetArtifactNeedDelete sets an artifact to need-delete, cron job will delete it +func SetArtifactNeedDelete(ctx context.Context, runID int64, name string) error { + _, err := db.GetEngine(ctx).Where("run_id=? AND artifact_name=? AND status = ?", runID, name, ArtifactStatusUploadConfirmed).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusPendingDeletion)}) + return err +} + +// SetArtifactDeleted sets an artifact to deleted +func SetArtifactDeleted(ctx context.Context, artifactID int64) error { + _, err := db.GetEngine(ctx).ID(artifactID).Cols("status").Update(&ActionArtifact{Status: int64(ArtifactStatusDeleted)}) + return err +} diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index bdae9a29ac..83061bcd9d 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -124,6 +124,7 @@ pin = Pin unpin = Unpin artifacts = Artifacts +confirm_delete_artifact = Are you sure you want to delete the artifact '%s' ? archived = Archived diff --git a/routers/web/repo/actions/view.go b/routers/web/repo/actions/view.go index ba2e63c3cc..903ff2632f 100644 --- a/routers/web/repo/actions/view.go +++ b/routers/web/repo/actions/view.go @@ -98,15 +98,16 @@ type ViewRequest struct { type ViewResponse struct { State struct { Run struct { - Link string `json:"link"` - Title string `json:"title"` - Status string `json:"status"` - CanCancel bool `json:"canCancel"` - CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve - CanRerun bool `json:"canRerun"` - Done bool `json:"done"` - Jobs []*ViewJob `json:"jobs"` - Commit ViewCommit `json:"commit"` + Link string `json:"link"` + Title string `json:"title"` + Status string `json:"status"` + CanCancel bool `json:"canCancel"` + CanApprove bool `json:"canApprove"` // the run needs an approval and the doer has permission to approve + CanRerun bool `json:"canRerun"` + CanDeleteArtifact bool `json:"canDeleteArtifact"` + Done bool `json:"done"` + Jobs []*ViewJob `json:"jobs"` + Commit ViewCommit `json:"commit"` } `json:"run"` CurrentJob struct { Title string `json:"title"` @@ -187,6 +188,7 @@ func ViewPost(ctx *context_module.Context) { resp.State.Run.CanCancel = !run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.CanApprove = run.NeedApproval && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.CanRerun = run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) + resp.State.Run.CanDeleteArtifact = run.Status.IsDone() && ctx.Repo.CanWrite(unit.TypeActions) resp.State.Run.Done = run.Status.IsDone() resp.State.Run.Jobs = make([]*ViewJob, 0, len(jobs)) // marshal to '[]' instead fo 'null' in json resp.State.Run.Status = run.Status.String() @@ -576,6 +578,29 @@ func ArtifactsView(ctx *context_module.Context) { ctx.JSON(http.StatusOK, artifactsResponse) } +func ArtifactsDeleteView(ctx *context_module.Context) { + if !ctx.Repo.CanWrite(unit.TypeActions) { + ctx.Error(http.StatusForbidden, "no permission") + return + } + + runIndex := ctx.ParamsInt64("run") + artifactName := ctx.Params("artifact_name") + + run, err := actions_model.GetRunByIndex(ctx, ctx.Repo.Repository.ID, runIndex) + if err != nil { + ctx.NotFoundOrServerError("GetRunByIndex", func(err error) bool { + return errors.Is(err, util.ErrNotExist) + }, err) + return + } + if err = actions_model.SetArtifactNeedDelete(ctx, run.ID, artifactName); err != nil { + ctx.Error(http.StatusInternalServerError, err.Error()) + return + } + ctx.JSON(http.StatusOK, struct{}{}) +} + func ArtifactsDownloadView(ctx *context_module.Context) { runIndex := ctx.ParamsInt64("run") artifactName := ctx.Params("artifact_name") @@ -603,6 +628,14 @@ func ArtifactsDownloadView(ctx *context_module.Context) { return } + // if artifacts status is not uploaded-confirmed, treat it as not found + for _, art := range artifacts { + if art.Status != int64(actions_model.ArtifactStatusUploadConfirmed) { + ctx.Error(http.StatusNotFound, "artifact not found") + return + } + } + ctx.Resp.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s.zip; filename*=UTF-8''%s.zip", url.PathEscape(artifactName), artifactName)) writer := zip.NewWriter(ctx.Resp) diff --git a/routers/web/web.go b/routers/web/web.go index 400bed9288..0684b2ac82 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1401,6 +1401,7 @@ func registerRoutes(m *web.Route) { m.Post("/approve", reqRepoActionsWriter, actions.Approve) m.Post("/artifacts", actions.ArtifactsView) m.Get("/artifacts/{artifact_name}", actions.ArtifactsDownloadView) + m.Delete("/artifacts/{artifact_name}", actions.ArtifactsDeleteView) m.Post("/rerun", reqRepoActionsWriter, actions.Rerun) }) }) diff --git a/services/actions/cleanup.go b/services/actions/cleanup.go index 785eeb5838..59e2cc85de 100644 --- a/services/actions/cleanup.go +++ b/services/actions/cleanup.go @@ -20,8 +20,15 @@ func Cleanup(taskCtx context.Context, olderThan time.Duration) error { return CleanupArtifacts(taskCtx) } -// CleanupArtifacts removes expired artifacts and set records expired status +// CleanupArtifacts removes expired add need-deleted artifacts and set records expired status func CleanupArtifacts(taskCtx context.Context) error { + if err := cleanExpiredArtifacts(taskCtx); err != nil { + return err + } + return cleanNeedDeleteArtifacts(taskCtx) +} + +func cleanExpiredArtifacts(taskCtx context.Context) error { artifacts, err := actions.ListNeedExpiredArtifacts(taskCtx) if err != nil { return err @@ -40,3 +47,32 @@ func CleanupArtifacts(taskCtx context.Context) error { } return nil } + +// deleteArtifactBatchSize is the batch size of deleting artifacts +const deleteArtifactBatchSize = 100 + +func cleanNeedDeleteArtifacts(taskCtx context.Context) error { + for { + artifacts, err := actions.ListPendingDeleteArtifacts(taskCtx, deleteArtifactBatchSize) + if err != nil { + return err + } + log.Info("Found %d artifacts pending deletion", len(artifacts)) + for _, artifact := range artifacts { + if err := storage.ActionsArtifacts.Delete(artifact.StoragePath); err != nil { + log.Error("Cannot delete artifact %d: %v", artifact.ID, err) + continue + } + if err := actions.SetArtifactDeleted(taskCtx, artifact.ID); err != nil { + log.Error("Cannot set artifact %d deleted: %v", artifact.ID, err) + continue + } + log.Info("Artifact %d set deleted", artifact.ID) + } + if len(artifacts) < deleteArtifactBatchSize { + log.Debug("No more artifacts pending deletion") + break + } + } + return nil +} diff --git a/templates/repo/actions/view.tmpl b/templates/repo/actions/view.tmpl index 6b07e7000a..f8b106147b 100644 --- a/templates/repo/actions/view.tmpl +++ b/templates/repo/actions/view.tmpl @@ -19,6 +19,7 @@ data-locale-status-skipped="{{ctx.Locale.Tr "actions.status.skipped"}}" data-locale-status-blocked="{{ctx.Locale.Tr "actions.status.blocked"}}" data-locale-artifacts-title="{{ctx.Locale.Tr "artifacts"}}" + data-locale-confirm-delete-artifact="{{ctx.Locale.Tr "confirm_delete_artifact"}}" data-locale-show-timestamps="{{ctx.Locale.Tr "show_timestamps"}}" data-locale-show-log-seconds="{{ctx.Locale.Tr "show_log_seconds"}}" data-locale-show-full-screen="{{ctx.Locale.Tr "show_full_screen"}}" diff --git a/web_src/js/components/RepoActionView.vue b/web_src/js/components/RepoActionView.vue index 797869b78c..c4a7389bc5 100644 --- a/web_src/js/components/RepoActionView.vue +++ b/web_src/js/components/RepoActionView.vue @@ -5,7 +5,7 @@ import {createApp} from 'vue'; import {toggleElem} from '../utils/dom.js'; import {getCurrentLocale} from '../utils.js'; import {renderAnsi} from '../render/ansi.js'; -import {POST} from '../modules/fetch.js'; +import {POST, DELETE} from '../modules/fetch.js'; const sfc = { name: 'RepoActionView', @@ -200,6 +200,12 @@ const sfc = { return await resp.json(); }, + async deleteArtifact(name) { + if (!window.confirm(this.locale.confirmDeleteArtifact.replace('%s', name))) return; + await DELETE(`${this.run.link}/artifacts/${name}`); + await this.loadJob(); + }, + async fetchJob() { const logCursors = this.currentJobStepsStates.map((it, idx) => { // cursor is used to indicate the last position of the logs @@ -329,6 +335,8 @@ export function initRepositoryActionView() { cancel: el.getAttribute('data-locale-cancel'), rerun: el.getAttribute('data-locale-rerun'), artifactsTitle: el.getAttribute('data-locale-artifacts-title'), + areYouSure: el.getAttribute('data-locale-are-you-sure'), + confirmDeleteArtifact: el.getAttribute('data-locale-confirm-delete-artifact'), rerun_all: el.getAttribute('data-locale-rerun-all'), showTimeStamps: el.getAttribute('data-locale-show-timestamps'), showLogSeconds: el.getAttribute('data-locale-show-log-seconds'), @@ -404,6 +412,9 @@ export function initRepositoryActionView() { {{ artifact.name }} + + +
    @@ -528,6 +539,8 @@ export function initRepositoryActionView() { .job-artifacts-item { margin: 5px 0; padding: 6px; + display: flex; + justify-content: space-between; } .job-artifacts-list { diff --git a/web_src/js/svg.js b/web_src/js/svg.js index 084256587c..471b5136bd 100644 --- a/web_src/js/svg.js +++ b/web_src/js/svg.js @@ -67,6 +67,7 @@ import octiconStrikethrough from '../../public/assets/img/svg/octicon-strikethro import octiconSync from '../../public/assets/img/svg/octicon-sync.svg'; import octiconTable from '../../public/assets/img/svg/octicon-table.svg'; import octiconTag from '../../public/assets/img/svg/octicon-tag.svg'; +import octiconTrash from '../../public/assets/img/svg/octicon-trash.svg'; import octiconTriangleDown from '../../public/assets/img/svg/octicon-triangle-down.svg'; import octiconX from '../../public/assets/img/svg/octicon-x.svg'; import octiconXCircleFill from '../../public/assets/img/svg/octicon-x-circle-fill.svg'; @@ -139,6 +140,7 @@ const svgs = { 'octicon-sync': octiconSync, 'octicon-table': octiconTable, 'octicon-tag': octiconTag, + 'octicon-trash': octiconTrash, 'octicon-triangle-down': octiconTriangleDown, 'octicon-x': octiconX, 'octicon-x-circle-fill': octiconXCircleFill, From 7f64e4d2a3f20b7d7de6542de5e0856c643e821f Mon Sep 17 00:00:00 2001 From: FuXiaoHei Date: Sun, 18 Feb 2024 22:25:14 +0800 Subject: [PATCH 144/271] Expire artifacts before deleting them physically (#29241) https://github.com/go-gitea/gitea/pull/27172#discussion_r1493735466 When cleanup artifacts, it removes storage first. If storage is not exist (maybe delete manually), it gets error and continue loop. It makes a dead loop if there are a lot pending but non-existing artifacts. Now it updates db record at first to avoid keep a lot of pending status artifacts. --- services/actions/cleanup.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/services/actions/cleanup.go b/services/actions/cleanup.go index 59e2cc85de..5376c2624c 100644 --- a/services/actions/cleanup.go +++ b/services/actions/cleanup.go @@ -35,14 +35,14 @@ func cleanExpiredArtifacts(taskCtx context.Context) error { } log.Info("Found %d expired artifacts", len(artifacts)) for _, artifact := range artifacts { - if err := storage.ActionsArtifacts.Delete(artifact.StoragePath); err != nil { - log.Error("Cannot delete artifact %d: %v", artifact.ID, err) - continue - } if err := actions.SetArtifactExpired(taskCtx, artifact.ID); err != nil { log.Error("Cannot set artifact %d expired: %v", artifact.ID, err) continue } + if err := storage.ActionsArtifacts.Delete(artifact.StoragePath); err != nil { + log.Error("Cannot delete artifact %d: %v", artifact.ID, err) + continue + } log.Info("Artifact %d set expired", artifact.ID) } return nil @@ -59,14 +59,14 @@ func cleanNeedDeleteArtifacts(taskCtx context.Context) error { } log.Info("Found %d artifacts pending deletion", len(artifacts)) for _, artifact := range artifacts { - if err := storage.ActionsArtifacts.Delete(artifact.StoragePath); err != nil { - log.Error("Cannot delete artifact %d: %v", artifact.ID, err) - continue - } if err := actions.SetArtifactDeleted(taskCtx, artifact.ID); err != nil { log.Error("Cannot set artifact %d deleted: %v", artifact.ID, err) continue } + if err := storage.ActionsArtifacts.Delete(artifact.StoragePath); err != nil { + log.Error("Cannot delete artifact %d: %v", artifact.ID, err) + continue + } log.Info("Artifact %d set deleted", artifact.ID) } if len(artifacts) < deleteArtifactBatchSize { From 98943fdb43fd28fcc8a3fd5185bfca36de63aa0a Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Fri, 23 Feb 2024 09:24:43 +0100 Subject: [PATCH 145/271] tests: Add a basic test for artifact deletion Adds a very bare-bones test for artifact deletion. It does not exercise the functionality itself, just the presence of the functionality. Signed-off-by: Gergely Nagy --- tests/integration/actions_route_test.go | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/tests/integration/actions_route_test.go b/tests/integration/actions_route_test.go index c941fca2e5..9a4a5801d2 100644 --- a/tests/integration/actions_route_test.go +++ b/tests/integration/actions_route_test.go @@ -125,3 +125,38 @@ func TestActionsWebRouteLatestRun(t *testing.T) { assert.Equal(t, workflow.HTMLURL(), resp.Header().Get("Location")) }) } + +func TestActionsArtifactDeletion(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + // create the repo + repo, _, f := CreateDeclarativeRepo(t, user2, "", + []unit_model.Type{unit_model.TypeActions}, nil, + []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: ".gitea/workflows/pr.yml", + ContentReader: strings.NewReader("name: test\non:\n push:\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - run: echo helloworld\n"), + }, + }, + ) + defer f() + + // a run has been created + assert.Equal(t, 1, unittest.GetCount(t, &actions_model.ActionRun{RepoID: repo.ID})) + + // Load the run we just created + run := unittest.AssertExistsAndLoadBean(t, &actions_model.ActionRun{RepoID: repo.ID}) + err := run.LoadAttributes(context.Background()) + assert.NoError(t, err) + + // Visit it's web view + req := NewRequest(t, "GET", run.HTMLURL()) + resp := MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + + // Assert that the artifact deletion markup exists + htmlDoc.AssertElement(t, "[data-locale-confirm-delete-artifact]", true) + }) +} From 5b3a82d621e75ef9198213d20081da8b28511403 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 21 Feb 2024 22:18:44 +0100 Subject: [PATCH 146/271] [FEAT] Enable ambiguous character detection in configured contexts - The ambiguous character detection is an important security feature to combat against sourcebase attacks (https://trojansource.codes/). - However there are a few problems with the feature as it stands today (i) it's apparantly an big performance hitter, it's twice as slow as syntax highlighting (ii) it contains false positives, because it's reporting valid problems but not valid within the context of a programming language (ambiguous charachters in code comments being a prime example) that can lead to security issues (iii) charachters from certain languages always being marked as ambiguous. It's a lot of effort to fix the aforementioned issues. - Therefore, make it configurable in which context the ambiguous character detection should be run, this avoids running detection in all contexts such as file views, but still enable it in commits and pull requests diffs where it matters the most. Ideally this also becomes an per-repository setting, but the code architecture doesn't allow for a clean implementation of that. - Adds unit test. - Adds integration tests to ensure that the contexts and instance-wide is respected (and that ambigious charachter detection actually work in different places). - Ref: https://codeberg.org/forgejo/forgejo/pulls/2395#issuecomment-1575547 - Ref: https://codeberg.org/forgejo/forgejo/issues/564 --- modules/charset/escape.go | 22 +++++-- modules/charset/escape_test.go | 22 ++++++- modules/setting/ui.go | 2 + routers/web/repo/blame.go | 2 +- routers/web/repo/setting/lfs.go | 2 +- routers/web/repo/view.go | 6 +- routers/web/repo/wiki.go | 2 +- services/gitdiff/gitdiff.go | 4 +- tests/integration/view_test.go | 104 ++++++++++++++++++++++++++++++++ 9 files changed, 151 insertions(+), 15 deletions(-) diff --git a/modules/charset/escape.go b/modules/charset/escape.go index 92e417d1f7..ba0eb73a3a 100644 --- a/modules/charset/escape.go +++ b/modules/charset/escape.go @@ -10,6 +10,7 @@ package charset import ( "html/template" "io" + "slices" "strings" "code.gitea.io/gitea/modules/log" @@ -20,16 +21,29 @@ import ( // RuneNBSP is the codepoint for NBSP const RuneNBSP = 0xa0 +type escapeContext string + +// Keep this consistent with the documentation of [ui].SKIP_ESCAPE_CONTEXTS +// Defines the different contexts that could be used to escape in. +const ( + // Wiki pages. + WikiContext escapeContext = "wiki" + // Rendered content (except markup), source code and blames. + FileviewContext escapeContext = "file-view" + // Commits or pull requet's diff. + DiffContext escapeContext = "diff" +) + // EscapeControlHTML escapes the unicode control sequences in a provided html document -func EscapeControlHTML(html template.HTML, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, output template.HTML) { +func EscapeControlHTML(html template.HTML, locale translation.Locale, context escapeContext, allowed ...rune) (escaped *EscapeStatus, output template.HTML) { sb := &strings.Builder{} - escaped, _ = EscapeControlReader(strings.NewReader(string(html)), sb, locale, allowed...) // err has been handled in EscapeControlReader + escaped, _ = EscapeControlReader(strings.NewReader(string(html)), sb, locale, context, allowed...) // err has been handled in EscapeControlReader return escaped, template.HTML(sb.String()) } // EscapeControlReader escapes the unicode control sequences in a provided reader of HTML content and writer in a locale and returns the findings as an EscapeStatus -func EscapeControlReader(reader io.Reader, writer io.Writer, locale translation.Locale, allowed ...rune) (escaped *EscapeStatus, err error) { - if !setting.UI.AmbiguousUnicodeDetection { +func EscapeControlReader(reader io.Reader, writer io.Writer, locale translation.Locale, context escapeContext, allowed ...rune) (escaped *EscapeStatus, err error) { + if !setting.UI.AmbiguousUnicodeDetection || slices.Contains(setting.UI.SkipEscapeContexts, string(context)) { _, err = io.Copy(writer, reader) return &EscapeStatus{}, err } diff --git a/modules/charset/escape_test.go b/modules/charset/escape_test.go index a353ced631..7442a80d7f 100644 --- a/modules/charset/escape_test.go +++ b/modules/charset/escape_test.go @@ -4,6 +4,7 @@ package charset import ( + "html/template" "strings" "testing" @@ -14,6 +15,8 @@ import ( "github.com/stretchr/testify/assert" ) +var testContext = escapeContext("test") + type escapeControlTest struct { name string text string @@ -159,7 +162,7 @@ func TestEscapeControlReader(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { output := &strings.Builder{} - status, err := EscapeControlReader(strings.NewReader(tt.text), output, &translation.MockLocale{}) + status, err := EscapeControlReader(strings.NewReader(tt.text), output, &translation.MockLocale{}, testContext) assert.NoError(t, err) assert.Equal(t, tt.status, *status) assert.Equal(t, tt.result, output.String()) @@ -169,9 +172,22 @@ func TestEscapeControlReader(t *testing.T) { func TestSettingAmbiguousUnicodeDetection(t *testing.T) { defer test.MockVariableValue(&setting.UI.AmbiguousUnicodeDetection, true)() - _, out := EscapeControlHTML("a test", &translation.MockLocale{}) + + _, out := EscapeControlHTML("a test", &translation.MockLocale{}, testContext) assert.EqualValues(t, `a test`, out) setting.UI.AmbiguousUnicodeDetection = false - _, out = EscapeControlHTML("a test", &translation.MockLocale{}) + _, out = EscapeControlHTML("a test", &translation.MockLocale{}, testContext) assert.EqualValues(t, `a test`, out) } + +func TestAmbiguousUnicodeDetectionContext(t *testing.T) { + defer test.MockVariableValue(&setting.UI.SkipEscapeContexts, []string{"test"})() + + input := template.HTML("a test") + + _, out := EscapeControlHTML(input, &translation.MockLocale{}, escapeContext("not-test")) + assert.EqualValues(t, `a test`, out) + + _, out = EscapeControlHTML(input, &translation.MockLocale{}, testContext) + assert.EqualValues(t, input, out) +} diff --git a/modules/setting/ui.go b/modules/setting/ui.go index 02a213d478..47e1393ef3 100644 --- a/modules/setting/ui.go +++ b/modules/setting/ui.go @@ -38,6 +38,7 @@ var UI = struct { PreferredTimestampTense string AmbiguousUnicodeDetection bool + SkipEscapeContexts []string Notification struct { MinTimeout time.Duration @@ -89,6 +90,7 @@ var UI = struct { PreferredTimestampTense: "mixed", AmbiguousUnicodeDetection: true, + SkipEscapeContexts: []string{}, Notification: struct { MinTimeout time.Duration diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index d7c861c42b..dca963c8ef 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -279,7 +279,7 @@ func renderBlame(ctx *context.Context, blameParts []*git.BlamePart, commitNames lexerName = lexerNameForLine } - br.EscapeStatus, br.Code = charset.EscapeControlHTML(line, ctx.Locale) + br.EscapeStatus, br.Code = charset.EscapeControlHTML(line, ctx.Locale, charset.FileviewContext) rows = append(rows, br) escapeStatus = escapeStatus.Or(br.EscapeStatus) } diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go index cd0f11d548..e360ae2b8c 100644 --- a/routers/web/repo/setting/lfs.go +++ b/routers/web/repo/setting/lfs.go @@ -307,7 +307,7 @@ func LFSFileGet(ctx *context.Context) { // Building code view blocks with line number on server side. escapedContent := &bytes.Buffer{} - ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, escapedContent, ctx.Locale) + ctx.Data["EscapeStatus"], _ = charset.EscapeControlReader(rd, escapedContent, ctx.Locale, charset.FileviewContext) var output bytes.Buffer lines := strings.Split(escapedContent.String(), "\n") diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index e48865a2f5..86977062cb 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -338,7 +338,7 @@ func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.Tr log.Error("Read readme content failed: %v", err) } contentEscaped := template.HTMLEscapeString(util.UnsafeBytesToString(content)) - ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlHTML(template.HTML(contentEscaped), ctx.Locale) + ctx.Data["EscapeStatus"], ctx.Data["FileContent"] = charset.EscapeControlHTML(template.HTML(contentEscaped), ctx.Locale, charset.FileviewContext) } if !fInfo.isLFSFile && ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { @@ -572,7 +572,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { status := &charset.EscapeStatus{} statuses := make([]*charset.EscapeStatus, len(fileContent)) for i, line := range fileContent { - statuses[i], fileContent[i] = charset.EscapeControlHTML(line, ctx.Locale) + statuses[i], fileContent[i] = charset.EscapeControlHTML(line, ctx.Locale, charset.FileviewContext) status = status.Or(statuses[i]) } ctx.Data["EscapeStatus"] = status @@ -678,7 +678,7 @@ func markupRender(ctx *context.Context, renderCtx *markup.RenderContext, input i go func() { sb := &strings.Builder{} // We allow NBSP here this is rendered - escaped, _ = charset.EscapeControlReader(markupRd, sb, ctx.Locale, charset.RuneNBSP) + escaped, _ = charset.EscapeControlReader(markupRd, sb, ctx.Locale, charset.FileviewContext, charset.RuneNBSP) output = template.HTML(sb.String()) close(done) }() diff --git a/routers/web/repo/wiki.go b/routers/web/repo/wiki.go index 79f446ea88..157ebd4d5d 100644 --- a/routers/web/repo/wiki.go +++ b/routers/web/repo/wiki.go @@ -254,7 +254,7 @@ func renderViewPage(ctx *context.Context) (*git.Repository, *git.TreeEntry) { done := make(chan struct{}) go func() { // We allow NBSP here this is rendered - escaped, _ = charset.EscapeControlReader(markupRd, buf, ctx.Locale, charset.RuneNBSP) + escaped, _ = charset.EscapeControlReader(markupRd, buf, ctx.Locale, charset.WikiContext, charset.RuneNBSP) output = buf.String() buf.Reset() close(done) diff --git a/services/gitdiff/gitdiff.go b/services/gitdiff/gitdiff.go index 241d849c9f..715c5bf9b8 100644 --- a/services/gitdiff/gitdiff.go +++ b/services/gitdiff/gitdiff.go @@ -284,14 +284,14 @@ type DiffInline struct { // DiffInlineWithUnicodeEscape makes a DiffInline with hidden unicode characters escaped func DiffInlineWithUnicodeEscape(s template.HTML, locale translation.Locale) DiffInline { - status, content := charset.EscapeControlHTML(s, locale) + status, content := charset.EscapeControlHTML(s, locale, charset.DiffContext) return DiffInline{EscapeStatus: status, Content: content} } // DiffInlineWithHighlightCode makes a DiffInline with code highlight and hidden unicode characters escaped func DiffInlineWithHighlightCode(fileName, language, code string, locale translation.Locale) DiffInline { highlighted, _ := highlight.Code(fileName, language, code) - status, content := charset.EscapeControlHTML(highlighted, locale) + status, content := charset.EscapeControlHTML(highlighted, locale, charset.DiffContext) return DiffInline{EscapeStatus: status, Content: content} } diff --git a/tests/integration/view_test.go b/tests/integration/view_test.go index f434446801..cd63304a91 100644 --- a/tests/integration/view_test.go +++ b/tests/integration/view_test.go @@ -5,8 +5,16 @@ package integration import ( "net/http" + "net/url" + "strings" "testing" + unit_model "code.gitea.io/gitea/models/unit" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" + files_service "code.gitea.io/gitea/services/repository/files" "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" @@ -25,3 +33,99 @@ func TestRenderFileSVGIsInImgTag(t *testing.T) { assert.True(t, exists, "The SVG image should be in an tag so that scripts in the SVG are not run") assert.Equal(t, "/user2/repo2/raw/branch/master/line.svg", src) } + +func TestAmbiguousCharacterDetection(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) + + // Prepare the environments. File view, commit view (diff), wiki page. + repo, commitID, f := CreateDeclarativeRepo(t, user2, "", + []unit_model.Type{unit_model.TypeCode, unit_model.TypeWiki}, nil, + []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: "test.sh", + ContentReader: strings.NewReader("Hello there!\nline western"), + }, + }, + ) + defer f() + + req := NewRequestWithValues(t, "POST", repo.Link()+"/wiki?action=new", map[string]string{ + "_csrf": GetCSRF(t, session, repo.Link()+"/wiki?action=new"), + "title": "Normal", + "content": "Hello – Hello", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + assertCase := func(t *testing.T, fileContext, commitContext, wikiContext bool) { + t.Helper() + + t.Run("File context", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", repo.Link()+"/src/branch/main/test.sh") + resp := session.MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + htmlDoc.AssertElement(t, ".unicode-escape-prompt", fileContext) + }) + t.Run("Commit context", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", repo.Link()+"/commit/"+commitID) + resp := session.MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + htmlDoc.AssertElement(t, ".lines-escape .toggle-escape-button", commitContext) + }) + t.Run("Wiki context", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", repo.Link()+"/wiki/Normal") + resp := session.MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + htmlDoc.AssertElement(t, ".unicode-escape-prompt", wikiContext) + }) + } + + t.Run("Enabled all context", func(t *testing.T) { + defer test.MockVariableValue(&setting.UI.SkipEscapeContexts, []string{})() + + assertCase(t, true, true, true) + }) + + t.Run("Enabled file context", func(t *testing.T) { + defer test.MockVariableValue(&setting.UI.SkipEscapeContexts, []string{"diff", "wiki"})() + + assertCase(t, true, false, false) + }) + + t.Run("Enabled commit context", func(t *testing.T) { + defer test.MockVariableValue(&setting.UI.SkipEscapeContexts, []string{"file-view", "wiki"})() + + assertCase(t, false, true, false) + }) + + t.Run("Enabled wiki context", func(t *testing.T) { + defer test.MockVariableValue(&setting.UI.SkipEscapeContexts, []string{"file-view", "diff"})() + + assertCase(t, false, false, true) + }) + + t.Run("No context", func(t *testing.T) { + defer test.MockVariableValue(&setting.UI.SkipEscapeContexts, []string{"file-view", "wiki", "diff"})() + + assertCase(t, false, false, false) + }) + + t.Run("Disabled detection", func(t *testing.T) { + defer test.MockVariableValue(&setting.UI.SkipEscapeContexts, []string{})() + defer test.MockVariableValue(&setting.UI.AmbiguousUnicodeDetection, false)() + + assertCase(t, false, false, false) + }) + }) +} From 2762dd95979a776c164db60ab604828e10cd09cc Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Fri, 23 Feb 2024 14:01:02 +0100 Subject: [PATCH 147/271] [CI] name the test release after the latest v*-dev tag Also ignore the *-test tags when figuring out the Forgejo version, they exist in the integration repository and experimental repository for daily releases. --- .forgejo/workflows/build-release-integration.yml | 2 +- .forgejo/workflows/build-release.yml | 4 ++-- Makefile | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.forgejo/workflows/build-release-integration.yml b/.forgejo/workflows/build-release-integration.yml index ab2f2ebc10..e066f0af5e 100644 --- a/.forgejo/workflows/build-release-integration.yml +++ b/.forgejo/workflows/build-release-integration.yml @@ -110,7 +110,7 @@ jobs: # # Push a commit to a branch that triggers the build of a test release # - version=0.0-test + version=1.2-test ( git clone $url/root/forgejo /tmp/forgejo cd /tmp/forgejo diff --git a/.forgejo/workflows/build-release.yml b/.forgejo/workflows/build-release.yml index e619c4dd27..c012991b3a 100644 --- a/.forgejo/workflows/build-release.yml +++ b/.forgejo/workflows/build-release.yml @@ -54,7 +54,7 @@ jobs: ref="${{ github.ref }}" if [[ $ref =~ ^refs/heads/ ]] ; then if test "$ref" = "refs/heads/forgejo" ; then - version=0.0-test + version=$(git tag -l --sort=version:refname --merged | grep -v -e '-test$' | tail -1 | sed -E -e 's/^(v[0-9]+\.[0-9]+).*/\1/')-test else version=${ref#refs/heads/} version=${version%/forgejo}-test @@ -70,7 +70,7 @@ jobs: exit 1 fi version=${version#v} - git describe --tags --always + git describe --exclude '*-test' --tags --always echo "sha=${{ github.sha }}" >> "$GITHUB_OUTPUT" echo "version=$version" >> "$GITHUB_OUTPUT" echo "override=$override" >> "$GITHUB_OUTPUT" diff --git a/Makefile b/Makefile index e12660e4a7..194da59ef0 100644 --- a/Makefile +++ b/Makefile @@ -90,7 +90,7 @@ STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null) ifneq ($(STORED_VERSION),) FORGEJO_VERSION ?= $(STORED_VERSION) else - FORGEJO_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//') + FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/-/+/' | sed 's/^v//') endif RELEASE_VERSION ?= ${FORGEJO_VERSION} VERSION ?= ${RELEASE_VERSION} From f304441341e52327b78806bd623576b23a01f2d2 Mon Sep 17 00:00:00 2001 From: Gusted Date: Fri, 23 Feb 2024 17:40:44 +0100 Subject: [PATCH 148/271] [BUG] Fix diff patch operation in web UI - The form used by the diff patch operation requires that the value of `tree_path` is set, even though it's not used. Set it to `patch` so this feature can be used again. - Regression of 08fe6f8c7ee4b446ae489843414d237cf3e458b5. --- templates/repo/editor/patch.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/editor/patch.tmpl b/templates/repo/editor/patch.tmpl index 44c30bd5f9..b9ca530058 100644 --- a/templates/repo/editor/patch.tmpl +++ b/templates/repo/editor/patch.tmpl @@ -14,7 +14,7 @@ {{.BranchName}} {{ctx.Locale.Tr "repo.editor.or"}} {{ctx.Locale.Tr "repo.editor.cancel_lower"}} - +
    From bf7fb89178f41c712b8a8863667a442ec1e1c5d4 Mon Sep 17 00:00:00 2001 From: "Panagiotis \"Ivory\" Vasilopoulos" Date: Fri, 23 Feb 2024 01:26:17 +0100 Subject: [PATCH 149/271] [UI] Agit: Add AGit label to AGit-created PRs Adds a label to Pull Requests that were created using AGit-Flow, in order to prevent situations where a contributor uses AGit-Flow to push new changes - only to realize that they did not use AGit-Flow in the first place, and that they just opened a new PR accidentally (that was me). Also intended to raise general awareness about the feature. Some additional work, such as adding a tooltip, still needs to be done. A small typo fix for a comment and (exclusively) formatting fixes in the copyright header are also included. Refs: https://codeberg.org/forgejo/forgejo/issues/2433 --- options/locale/locale_en-US.ini | 1 + routers/web/repo/pull.go | 10 ++++++++-- templates/repo/issue/view_title.tmpl | 10 +++++++++- tests/integration/git_test.go | 26 +++++++++++++++++++++++++- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index bdae9a29ac..8253c6ced5 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1505,6 +1505,7 @@ issues.action_check_all = Check/Uncheck all items issues.opened_by = opened %[1]s by %[3]s pulls.merged_by = by %[3]s was merged %[1]s pulls.merged_by_fake = by %[2]s was merged %[1]s +pulls.made_using_agit = AGit issues.closed_by = by %[3]s was closed %[1]s issues.opened_by_fake = opened %[1]s by %[2]s issues.closed_by_fake = by %[2]s was closed %[1]s diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index ab821f8884..ac244b1551 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1,5 +1,6 @@ -// Copyright 2018 The Gitea Authors. -// Copyright 2014 The Gogs Authors. +// Copyright 2014 The Gogs Authors. All rights reserved. +// Copyright 2018 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors. All rights reserved. // All rights reserved. // SPDX-License-Identifier: MIT @@ -381,6 +382,11 @@ func setMergeTarget(ctx *context.Context, pull *issues_model.PullRequest) { } else { ctx.Data["HeadTarget"] = pull.MustHeadUserName(ctx) + "/" + pull.HeadRepo.Name + ":" + pull.HeadBranch } + + if pull.Flow == issues_model.PullRequestFlowAGit { + ctx.Data["MadeUsingAGit"] = true + } + ctx.Data["BaseTarget"] = pull.BaseBranch ctx.Data["HeadBranchLink"] = pull.GetHeadBranchLink(ctx) ctx.Data["BaseBranchLink"] = pull.GetBaseBranchLink(ctx) diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl index 582e9864fb..8a5954681b 100644 --- a/templates/repo/issue/view_title.tmpl +++ b/templates/repo/issue/view_title.tmpl @@ -47,7 +47,9 @@ {{if .HeadBranchLink}} {{$headHref = printf `%s` (.HeadBranchLink | Escape) $headHref}} {{end}} - {{$headHref = printf `%s ` $headHref (ctx.Locale.Tr "copy_branch") (.HeadTarget | Escape) (svg "octicon-copy" 14)}} + {{if not .MadeUsingAGit}} + {{$headHref = printf `%s ` $headHref (ctx.Locale.Tr "copy_branch") (.HeadTarget | Escape) (svg "octicon-copy" 14)}} + {{end}} {{$baseHref := .BaseTarget|Escape}} {{if .BaseBranchLink}} {{$baseHref = printf `%s` (.BaseBranchLink | Escape) $baseHref}} @@ -70,6 +72,12 @@ {{ctx.Locale.Tr "repo.pulls.title_desc" .NumCommits ($headHref|Safe) ($baseHref|Safe)}} {{end}} + {{if .MadeUsingAGit}} + {{/* TODO: Add tooltip and a link to the documentation */}} + + {{ctx.Locale.Tr "repo.pulls.made_using_agit"}} + + {{end}}
    diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index 2a3167f982..ed377e9d18 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -62,55 +62,8 @@ {{if not $.DisableStars}} {{template "repo/star_unstar" $}} {{end}} - {{if and (not .IsEmpty) ($.Permission.CanRead $.UnitTypeCode)}} -
    - - {{svg "octicon-repo-forked"}}{{ctx.Locale.Tr "repo.fork"}} - - - - {{CountFmt .NumForks}} - -
    + {{if not $.DisableForks}} + {{template "repo/header_fork" $}} {{end}}
    {{end}} diff --git a/templates/repo/header_fork.tmpl b/templates/repo/header_fork.tmpl new file mode 100644 index 0000000000..5bce9e0f14 --- /dev/null +++ b/templates/repo/header_fork.tmpl @@ -0,0 +1,50 @@ +{{if and (not .IsEmpty) ($.Permission.CanRead $.UnitTypeCode)}} +
    + + {{svg "octicon-repo-forked"}}{{ctx.Locale.Tr "repo.fork"}} + + + + {{CountFmt .NumForks}} + +
    +{{end}} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 0b330a89ee..18ab544415 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -20565,6 +20565,10 @@ "description": "GeneralRepoSettings contains global repository settings exposed by API", "type": "object", "properties": { + "forks_disabled": { + "type": "boolean", + "x-go-name": "ForksDisabled" + }, "http_git_disabled": { "type": "boolean", "x-go-name": "HTTPGitDisabled" diff --git a/tests/integration/api_fork_test.go b/tests/integration/api_fork_test.go index 7c231415a3..87d2a10152 100644 --- a/tests/integration/api_fork_test.go +++ b/tests/integration/api_fork_test.go @@ -1,13 +1,18 @@ // Copyright 2017 The Gogs Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. // SPDX-License-Identifier: MIT package integration import ( "net/http" + "net/url" "testing" + "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" + "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/routers" "code.gitea.io/gitea/tests" ) @@ -16,3 +21,27 @@ func TestCreateForkNoLogin(t *testing.T) { req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/forks", &api.CreateForkOption{}) MakeRequest(t, req, http.StatusUnauthorized) } + +func TestAPIDisabledForkRepo(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + defer test.MockVariableValue(&setting.Repository.DisableForks, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + t.Run("fork listing", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + req := NewRequest(t, "GET", "/api/v1/repos/user2/repo1/forks") + MakeRequest(t, req, http.StatusNotFound) + }) + + t.Run("forking", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + session := loginUser(t, "user5") + token := getTokenForLoggedInUser(t, session) + + req := NewRequestWithJSON(t, "POST", "/api/v1/repos/user2/repo1/forks", &api.CreateForkOption{}).AddTokenAuth(token) + session.MakeRequest(t, req, http.StatusNotFound) + }) + }) +} diff --git a/tests/integration/repo_fork_test.go b/tests/integration/repo_fork_test.go index c6e3fed7a9..6c0cdc4339 100644 --- a/tests/integration/repo_fork_test.go +++ b/tests/integration/repo_fork_test.go @@ -1,4 +1,5 @@ // Copyright 2017 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. // SPDX-License-Identifier: MIT package integration @@ -14,6 +15,9 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/test" + "code.gitea.io/gitea/routers" repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/tests" @@ -119,6 +123,48 @@ func TestRepoFork(t *testing.T) { session.MakeRequest(t, req, http.StatusNotFound) }) }) + + t.Run("DISABLE_FORKS", func(t *testing.T) { + defer test.MockVariableValue(&setting.Repository.DisableForks, true)() + defer test.MockVariableValue(&testWebRoutes, routers.NormalRoutes())() + + t.Run("fork button not present", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // The "Fork" button should not appear on the repo home + req := NewRequest(t, "GET", "/user2/repo1") + resp := MakeRequest(t, req, http.StatusOK) + htmlDoc := NewHTMLParser(t, resp.Body) + htmlDoc.AssertElement(t, "[href=/user2/repo1/fork]", false) + }) + + t.Run("forking by URL", func(t *testing.T) { + t.Run("by name", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Forking by URL should be Not Found + req := NewRequest(t, "GET", "/user2/repo1/fork") + session.MakeRequest(t, req, http.StatusNotFound) + }) + + t.Run("by legacy URL", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Forking by legacy URL should be Not Found + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) // user2/repo1 + req := NewRequestf(t, "GET", "/repo/fork/%d", repo.ID) + session.MakeRequest(t, req, http.StatusNotFound) + }) + }) + + t.Run("fork listing", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Listing the forks should be Not Found, too + req := NewRequest(t, "GET", "/user2/repo1/forks") + MakeRequest(t, req, http.StatusNotFound) + }) + }) }) } From e34ead7a46a4a92bb8d7ff0e4cdeda5668d96077 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sun, 25 Feb 2024 17:03:09 +0500 Subject: [PATCH 157/271] [I18N] eliminate wrapping quotes --- options/locale/locale_en-US.ini | 206 ++++++++++++++++---------------- 1 file changed, 103 insertions(+), 103 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 0931e86941..dc413f7f68 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -590,14 +590,14 @@ invalid_ssh_key = Cannot verify your SSH key: %s invalid_gpg_key = Cannot verify your GPG key: %s invalid_ssh_principal = Invalid principal: %s must_use_public_key = The key you provided is a private key. Please do not upload your private key anywhere. Use your public key instead. -unable_verify_ssh_key = "Cannot verify the SSH key, double-check it for mistakes." +unable_verify_ssh_key = Cannot verify the SSH key, double-check it for mistakes. auth_failed = Authentication failed: %v -still_own_repo = "Your account owns one or more repositories, delete or transfer them first." -still_has_org = "Your account is a member of one or more organizations, leave them first." -still_own_packages = "Your account owns one or more packages, delete them first." -org_still_own_repo = "This organization still owns one or more repositories, delete or transfer them first." -org_still_own_packages = "This organization still owns one or more packages, delete them first." +still_own_repo = Your account owns one or more repositories, delete or transfer them first. +still_has_org = Your account is a member of one or more organizations, leave them first. +still_own_packages = Your account owns one or more packages, delete them first. +org_still_own_repo = This organization still owns one or more repositories, delete or transfer them first. +org_still_own_packages = This organization still owns one or more packages, delete them first. target_branch_not_exist = Target branch does not exist. @@ -1123,7 +1123,7 @@ migrate.github_token_desc = You can put one or more tokens with comma separated migrate.clone_local_path = or a local server path migrate.permission_denied = You are not allowed to import local repositories. migrate.permission_denied_blocked = You cannot import from disallowed hosts, please ask the admin to check ALLOWED_DOMAINS/ALLOW_LOCALNETWORKS/BLOCKED_DOMAINS settings. -migrate.invalid_local_path = "The local path is invalid. It doesn't exist or is not a directory." +migrate.invalid_local_path = The local path is invalid. It doesn't exist or is not a directory. migrate.invalid_lfs_endpoint = The LFS endpoint is not valid. migrate.failed = Migration failed: %v migrate.migrate_items_options = Access Token is required to migrate additional items @@ -1366,30 +1366,30 @@ projects.edit = Edit Project projects.edit_subheader = Projects organize issues and track progress. projects.modify = Edit Project projects.edit_success = Project "%s" has been updated. -projects.type.none = "None" -projects.type.basic_kanban = "Basic Kanban" -projects.type.bug_triage = "Bug Triage" -projects.template.desc = "Template" -projects.template.desc_helper = "Select a project template to get started" +projects.type.none = None +projects.type.basic_kanban = Basic Kanban +projects.type.bug_triage = Bug Triage +projects.template.desc = Template +projects.template.desc_helper = Select a project template to get started projects.type.uncategorized = Uncategorized -projects.column.edit = "Edit Column" -projects.column.edit_title = "Name" -projects.column.new_title = "Name" -projects.column.new_submit = "Create Column" -projects.column.new = "New Column" -projects.column.set_default = "Set Default" -projects.column.set_default_desc = "Set this column as default for uncategorized issues and pulls" -projects.column.unset_default = "Unset Default" -projects.column.unset_default_desc = "Unset this column as default" -projects.column.delete = "Delete Column" -projects.column.deletion_desc = "Deleting a project column moves all related issues to 'Uncategorized'. Continue?" -projects.column.color = "Color" +projects.column.edit = Edit Column +projects.column.edit_title = Name +projects.column.new_title = Name +projects.column.new_submit = Create Column +projects.column.new = New Column +projects.column.set_default = Set Default +projects.column.set_default_desc = Set this column as default for uncategorized issues and pulls +projects.column.unset_default = Unset Default +projects.column.unset_default_desc = Unset this column as default +projects.column.delete = Delete Column +projects.column.deletion_desc = Deleting a project column moves all related issues to 'Uncategorized'. Continue? +projects.column.color = Color projects.open = Open projects.close = Close projects.column.assigned_to = Assigned to -projects.card_type.desc = "Card Previews" -projects.card_type.images_and_text = "Images and Text" -projects.card_type.text_only = "Text Only" +projects.card_type.desc = Card Previews +projects.card_type.images_and_text = Images and Text +projects.card_type.text_only = Text Only issues.desc = Organize bug reports, tasks and milestones. issues.filter_assignees = Filter Assignee @@ -1591,17 +1591,17 @@ issues.attachment.download = `Click to download "%s"` issues.subscribe = Subscribe issues.unsubscribe = Unsubscribe issues.unpin_issue = Unpin Issue -issues.max_pinned = "You can't pin more issues" -issues.pin_comment = "pinned this %s" -issues.unpin_comment = "unpinned this %s" +issues.max_pinned = You can't pin more issues +issues.pin_comment = pinned this %s +issues.unpin_comment = unpinned this %s issues.lock = Lock conversation issues.unlock = Unlock conversation issues.lock.unknown_reason = Cannot lock an issue with an unknown reason. issues.lock_duplicate = An issue cannot be locked twice. issues.unlock_error = Cannot unlock an issue that is not locked. -issues.lock_with_reason = "locked as %s and limited conversation to collaborators %s" -issues.lock_no_reason = "locked and limited conversation to collaborators %s" -issues.unlock_comment = "unlocked this conversation %s" +issues.lock_with_reason = locked as %s and limited conversation to collaborators %s +issues.lock_no_reason = locked and limited conversation to collaborators %s +issues.unlock_comment = unlocked this conversation %s issues.lock_confirm = Lock issues.unlock_confirm = Unlock issues.lock.notice_1 = - Other users can’t add new comments to this issue. @@ -1638,30 +1638,30 @@ issues.add_time_sum_to_small = No time was entered. issues.time_spent_total = Total Time Spent issues.time_spent_from_all_authors = `Total Time Spent: %s` issues.due_date = Due Date -issues.invalid_due_date_format = "Due date format must be 'yyyy-mm-dd'." -issues.error_modifying_due_date = "Failed to modify the due date." -issues.error_removing_due_date = "Failed to remove the due date." -issues.push_commit_1 = "added %d commit %s" -issues.push_commits_n = "added %d commits %s" +issues.invalid_due_date_format = Due date format must be 'yyyy-mm-dd'. +issues.error_modifying_due_date = Failed to modify the due date. +issues.error_removing_due_date = Failed to remove the due date. +issues.push_commit_1 = added %d commit %s +issues.push_commits_n = added %d commits %s issues.force_push_codes = `force-pushed %[1]s from %[2]s to %[4]s %[6]s` issues.force_push_compare = Compare -issues.due_date_form = "yyyy-mm-dd" -issues.due_date_form_add = "Add due date" -issues.due_date_form_edit = "Edit" -issues.due_date_form_remove = "Remove" -issues.due_date_not_writer = "You need write access to this repository in order to update the due date of an issue." -issues.due_date_not_set = "No due date set." -issues.due_date_added = "added the due date %s %s" -issues.due_date_modified = "modified the due date from %[2]s to %[1]s %[3]s" -issues.due_date_remove = "removed the due date %s %s" -issues.due_date_overdue = "Overdue" -issues.due_date_invalid = "The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'." +issues.due_date_form = yyyy-mm-dd +issues.due_date_form_add = Add due date +issues.due_date_form_edit = Edit +issues.due_date_form_remove = Remove +issues.due_date_not_writer = You need write access to this repository in order to update the due date of an issue. +issues.due_date_not_set = No due date set. +issues.due_date_added = added the due date %s %s +issues.due_date_modified = modified the due date from %[2]s to %[1]s %[3]s +issues.due_date_remove = removed the due date %s %s +issues.due_date_overdue = Overdue +issues.due_date_invalid = The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'. issues.dependency.title = Dependencies issues.dependency.issue_no_dependencies = No dependencies set. issues.dependency.pr_no_dependencies = No dependencies set. -issues.dependency.no_permission_1 = "You do not have permission to read %d dependency" -issues.dependency.no_permission_n = "You do not have permission to read %d dependencies" -issues.dependency.no_permission.can_remove = "You do not have permission to read this dependency but can remove this dependency" +issues.dependency.no_permission_1 = You do not have permission to read %d dependency +issues.dependency.no_permission_n = You do not have permission to read %d dependencies +issues.dependency.no_permission.can_remove = You do not have permission to read this dependency but can remove this dependency issues.dependency.add = Add dependency… issues.dependency.cancel = Cancel issues.dependency.remove = Remove @@ -1673,7 +1673,7 @@ issues.dependency.issue_closing_blockedby = Closing this issue is blocked by the issues.dependency.issue_close_blocks = This issue blocks closing of the following issues issues.dependency.pr_close_blocks = This pull request blocks closing of the following issues issues.dependency.issue_close_blocked = You need to close all issues blocking this issue before you can close it. -issues.dependency.issue_batch_close_blocked = "Cannot batch close chosen issues, because issue #%d still has open dependencies" +issues.dependency.issue_batch_close_blocked = Cannot batch close chosen issues, because issue #%d still has open dependencies issues.dependency.pr_close_blocked = You need to close all issues blocking this pull request before you can merge it. issues.dependency.blocks_short = Blocks issues.dependency.blocked_by_short = Depends on @@ -1689,17 +1689,17 @@ issues.dependency.add_error_cannot_create_circular = You cannot create a depende issues.dependency.add_error_dep_not_same_repo = Both issues must be in the same repository. issues.review.self.approval = You cannot approve your own pull request. issues.review.self.rejection = You cannot request changes on your own pull request. -issues.review.approve = "approved these changes %s" -issues.review.comment = "reviewed %s" -issues.review.dismissed = "dismissed %s’s review %s" +issues.review.approve = approved these changes %s +issues.review.comment = reviewed %s +issues.review.dismissed = dismissed %s’s review %s issues.review.dismissed_label = Dismissed issues.review.left_comment = left a comment issues.review.content.empty = You need to leave a comment indicating the requested change(s). -issues.review.reject = "requested changes %s" -issues.review.wait = "was requested for review %s" -issues.review.add_review_request = "requested review from %s %s" -issues.review.remove_review_request = "removed review request for %s %s" -issues.review.remove_review_request_self = "refused to review %s" +issues.review.reject = requested changes %s +issues.review.wait = was requested for review %s +issues.review.add_review_request = requested review from %s %s +issues.review.remove_review_request = removed review request for %s %s +issues.review.remove_review_request_self = refused to review %s issues.review.pending = Pending issues.review.pending.tooltip = This comment is not currently visible to other users. To submit your pending comments, select "%s" -> "%s/%s/%s" at the top of the page. issues.review.review = Review @@ -1782,30 +1782,30 @@ pulls.add_prefix = Add %s prefix pulls.remove_prefix = Remove %s prefix pulls.data_broken = This pull request is broken due to missing fork information. pulls.files_conflicted = This pull request has changes conflicting with the target branch. -pulls.is_checking = "Merge conflict checking is in progress. Try again in few moments." -pulls.is_ancestor = "This branch is already included in the target branch. There is nothing to merge." -pulls.is_empty = "The changes on this branch are already on the target branch. This will be an empty commit." +pulls.is_checking = Merge conflict checking is in progress. Try again in few moments. +pulls.is_ancestor = This branch is already included in the target branch. There is nothing to merge. +pulls.is_empty = The changes on this branch are already on the target branch. This will be an empty commit. pulls.required_status_check_failed = Some required checks were not successful. pulls.required_status_check_missing = Some required checks are missing. pulls.required_status_check_administrator = As an administrator, you may still merge this pull request. -pulls.blocked_by_approvals = "This pull request doesn't have enough approvals yet. %d of %d approvals granted." -pulls.blocked_by_rejection = "This pull request has changes requested by an official reviewer." -pulls.blocked_by_official_review_requests = "This pull request is blocked because it is missing approval from one or more official reviewers." -pulls.blocked_by_outdated_branch = "This pull request is blocked because it's outdated." -pulls.blocked_by_changed_protected_files_1= "This pull request is blocked because it changes a protected file:" -pulls.blocked_by_changed_protected_files_n= "This pull request is blocked because it changes protected files:" +pulls.blocked_by_approvals = This pull request doesn't have enough approvals yet. %d of %d approvals granted. +pulls.blocked_by_rejection = This pull request has changes requested by an official reviewer. +pulls.blocked_by_official_review_requests = This pull request is blocked because it is missing approval from one or more official reviewers. +pulls.blocked_by_outdated_branch = This pull request is blocked because it's outdated. +pulls.blocked_by_changed_protected_files_1= This pull request is blocked because it changes a protected file: +pulls.blocked_by_changed_protected_files_n= This pull request is blocked because it changes protected files: pulls.can_auto_merge_desc = This pull request can be merged automatically. pulls.cannot_auto_merge_desc = This pull request cannot be merged automatically due to conflicts. pulls.cannot_auto_merge_helper = Merge manually to resolve the conflicts. -pulls.num_conflicting_files_1 = "%d conflicting file" -pulls.num_conflicting_files_n = "%d conflicting files" -pulls.approve_count_1 = "%d approval" -pulls.approve_count_n = "%d approvals" -pulls.reject_count_1 = "%d change request" -pulls.reject_count_n = "%d change requests" -pulls.waiting_count_1 = "%d waiting review" -pulls.waiting_count_n = "%d waiting reviews" -pulls.wrong_commit_id = "commit id must be a commit id on the target branch" +pulls.num_conflicting_files_1 = %d conflicting file +pulls.num_conflicting_files_n = %d conflicting files +pulls.approve_count_1 = %d approval +pulls.approve_count_n = %d approvals +pulls.reject_count_1 = %d change request +pulls.reject_count_n = %d change requests +pulls.waiting_count_1 = %d waiting review +pulls.waiting_count_n = %d waiting reviews +pulls.wrong_commit_id = commit id must be a commit id on the target branch pulls.blocked_by_user = You cannot create a pull request on this repository because you are blocked by the repository owner. pulls.no_merge_desc = This pull request cannot be merged because all repository merge options are disabled. @@ -1895,7 +1895,7 @@ milestones.title = Title milestones.desc = Description milestones.due_date = Due Date (optional) milestones.clear = Clear -milestones.invalid_due_date_format = "Due date format must be 'yyyy-mm-dd'." +milestones.invalid_due_date_format = Due date format must be 'yyyy-mm-dd'. milestones.create_success = The milestone "%s" has been created. milestones.edit = Edit Milestone milestones.edit_subheader = Milestones organize issues and track progress. @@ -2237,7 +2237,7 @@ settings.webhook.body = Body settings.webhook.replay.description = Replay this webhook. settings.webhook.replay.description_disabled = To replay this webhook, activate it. settings.webhook.delivery.success = An event has been added to the delivery queue. It may take few seconds before it shows up in the delivery history. -settings.githooks_desc = "Git Hooks are powered by Git itself. You can edit hook files below to set up custom operations." +settings.githooks_desc = Git Hooks are powered by Git itself. You can edit hook files below to set up custom operations. settings.githook_edit_desc = If the hook is inactive, sample content will be presented. Leaving content to an empty value will disable this hook. settings.githook_name = Hook Name settings.githook_content = Hook Content @@ -2398,12 +2398,12 @@ settings.ignore_stale_approvals_desc = Do not count approvals that were made on settings.require_signed_commits = Require Signed Commits settings.require_signed_commits_desc = Reject pushes to this branch if they are unsigned or unverifiable. settings.protect_branch_name_pattern = Protected Branch Name Pattern -settings.protect_branch_name_pattern_desc = "Protected branch name patterns. See the documentation for pattern syntax. Examples: main, release/**" +settings.protect_branch_name_pattern_desc = Protected branch name patterns. See the documentation for pattern syntax. Examples: main, release/** settings.protect_patterns = Patterns -settings.protect_protected_file_patterns = "Protected file patterns (separated using semicolon ';'):" -settings.protect_protected_file_patterns_desc = "Protected files are not allowed to be changed directly even if user has rights to add, edit, or delete files in this branch. Multiple patterns can be separated using semicolon (';'). See github.com/gobwas/glob documentation for pattern syntax. Examples: .drone.yml, /docs/**/*.txt." -settings.protect_unprotected_file_patterns = "Unprotected file patterns (separated using semicolon ';'):" -settings.protect_unprotected_file_patterns_desc = "Unprotected files that are allowed to be changed directly if user has write access, bypassing push restriction. Multiple patterns can be separated using semicolon (';'). See github.com/gobwas/glob documentation for pattern syntax. Examples: .drone.yml, /docs/**/*.txt." +settings.protect_protected_file_patterns = Protected file patterns (separated using semicolon ';'): +settings.protect_protected_file_patterns_desc = Protected files are not allowed to be changed directly even if user has rights to add, edit, or delete files in this branch. Multiple patterns can be separated using semicolon (';'). See github.com/gobwas/glob documentation for pattern syntax. Examples: .drone.yml, /docs/**/*.txt. +settings.protect_unprotected_file_patterns = Unprotected file patterns (separated using semicolon ';'): +settings.protect_unprotected_file_patterns_desc = Unprotected files that are allowed to be changed directly if user has write access, bypassing push restriction. Multiple patterns can be separated using semicolon (';'). See github.com/gobwas/glob documentation for pattern syntax. Examples: .drone.yml, /docs/**/*.txt. settings.add_protected_branch = Enable protection settings.delete_protected_branch = Disable protection settings.update_protect_branch_success = Branch protection for rule "%s" has been updated. @@ -2766,7 +2766,7 @@ teams.remove_all_repos_title = Remove all team repositories teams.remove_all_repos_desc = This will remove all repositories from the team. teams.add_all_repos_title = Add all repositories teams.add_all_repos_desc = This will add all the organization's repositories to the team. -teams.add_nonexistent_repo = "The repository you're trying to add doesn't exist, please create it first." +teams.add_nonexistent_repo = The repository you're trying to add doesn't exist, please create it first. teams.add_duplicate_users = User is already a team member. teams.repos.none = No repositories could be accessed by this team. teams.members.none = No members on this team. @@ -2823,7 +2823,7 @@ dashboard.cron.error=Error in Cron: %s: %[3]s dashboard.cron.finished=Cron: %[1]s has finished dashboard.delete_inactive_accounts = Delete all unactivated accounts dashboard.delete_inactive_accounts.started = Delete all unactivated accounts task started. -dashboard.delete_repo_archives = "Delete all repositories' archives (ZIP, TAR.GZ, etc..)" +dashboard.delete_repo_archives = Delete all repositories' archives (ZIP, TAR.GZ, etc..) dashboard.delete_repo_archives.started = Delete all repository archives task started. dashboard.delete_missing_repos = Delete all repositories missing their Git files dashboard.delete_missing_repos.started = Delete all repositories missing their Git files task started. @@ -2923,7 +2923,7 @@ users.allow_import_local = May Import Local Repositories users.allow_create_organization = May Create Organizations users.update_profile = Update User Account users.delete_account = Delete User Account -users.cannot_delete_self = "You cannot delete yourself" +users.cannot_delete_self = You cannot delete yourself users.still_own_repo = This user still owns one or more repositories. Delete or transfer these repositories first. users.still_has_org = This user is a member of an organization. Remove the user from any organizations first. users.purge = Purge User @@ -3125,7 +3125,7 @@ config.app_name = Site Title config.app_ver = Forgejo Version config.app_url = Forgejo Base URL config.custom_conf = Configuration File Path -config.custom_file_root_path = "Custom File Root Path" +config.custom_file_root_path = Custom File Root Path config.domain = Server Domain config.offline_mode = Local Mode config.disable_router_log = Disable Router Log @@ -3396,11 +3396,11 @@ default_key=Signed with default key error.extract_sign = Failed to extract signature error.generate_hash = Failed to generate hash of commit error.no_committer_account = No account linked to committer's email address -error.no_gpg_keys_found = "No known key found for this signature in database" -error.not_signed_commit = "Not a signed commit" -error.failed_retrieval_gpg_keys = "Failed to retrieve any key attached to the committer's account" -error.probable_bad_signature = "WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS." -error.probable_bad_default_signature = "WARNING! Although the default key has this ID it does not verify this commit! This commit is SUSPICIOUS." +error.no_gpg_keys_found = No known key found for this signature in database +error.not_signed_commit = Not a signed commit +error.failed_retrieval_gpg_keys = Failed to retrieve any key attached to the committer's account +error.probable_bad_signature = WARNING! Although there is a key with this ID in the database it does not verify this commit! This commit is SUSPICIOUS. +error.probable_bad_default_signature = WARNING! Although the default key has this ID it does not verify this commit! This commit is SUSPICIOUS. [units] unit = Unit @@ -3579,14 +3579,14 @@ actions = Actions unit.desc = Manage actions -status.unknown = "Unknown" -status.waiting = "Waiting" -status.running = "Running" -status.success = "Success" -status.failure = "Failure" -status.cancelled = "Canceled" -status.skipped = "Skipped" -status.blocked = "Blocked" +status.unknown = Unknown +status.waiting = Waiting +status.running = Running +status.success = Success +status.failure = Failure +status.cancelled = Canceled +status.skipped = Skipped +status.blocked = Blocked runners = Runners runners.runner_manage_panel = Runners Management From a748ba70a8654f7b4260438182cff6c404557519 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Sun, 25 Feb 2024 18:13:59 +0500 Subject: [PATCH 158/271] 10-year old images are gone --- public/assets/img/404.png | Bin 4516 -> 0 bytes public/assets/img/500.png | Bin 5230 -> 0 bytes tests/integration/compare_test.go | 2 +- tests/integration/links_test.go | 2 -- 4 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 public/assets/img/404.png delete mode 100644 public/assets/img/500.png diff --git a/public/assets/img/404.png b/public/assets/img/404.png deleted file mode 100644 index 8b66c971f462dcc37c6b3f33d4a9c35c68a8979d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4516 zcmZ`+c{r4B_kYGP$k<25lCcbi5)(*&`hd+9D z$aWGE61MO^2pjwd@^|>h-|F~x$`<&yv!(ynB3tQyQntdsv)I%YzUBWBY{~xM|B(F& zx4j;3vjZdW(Huij5U@;d)=`Zf!oYXk^1cfd< zV!gh;efALL%SMw-zFO8CFNVYEvIK^^-;F`)zoggp0}qK+h28(u@a?7t-(96{^VQ6V zPxn+-gLCBG+ewz5ntZ>VSncs^N`I_w2@%MPTyOR>l0%C|l%tK#ouTW5Xr~;#L3mo< z_Uy7{l!BkN-BoLW%EyTbZY%&06t~0}JK;dz>bXD!4FGsDKKrgt^#-kii@ojtXN0LF z4wk6$>2&c^NA@MYm`Mz|m2rX3#cQfZT4)j)C+m*Z4Rh#t5hzuZxmv!q`F=lg4l4say4uEHk629J%4Mt_2pljy?1eggSVFl{Mje zTT1{cZ)nQfX3D3bXOaMF`i#&o!o9Hf4$!OO&=<|u06j%(kQ1Qhw`*mUMvn>AX%?U zTiNZ_N2oENekig{aQ(6CY7g+GU-_#C?Q}SpDA-jfbTvY_7(`q*!$Wm`XnQzNw&kI2 zngs&SoG{hUhq}GP-49HVsoCgcK=H18G^BNCLZ#?UxG2(v7D#}Y05zx%V!XRB~V_bj?s4zg!Qwf~&i`i8noeI~R(Y{7-%6Tx%)YaE8tXs}M&`-#) z{mBo(RCCdGWsLck4w1s0Ss`n>u#WVqW!t3sJ;bN2pENWzH8Zj*SxeV`ec+{=-|E&= znNxUu;@AvMZ>puN4t?>#_%(0bw>$IY3ykCX(Qy(U7_O1#Y&fqaEZj}2fGnXXb?Ls$ zn>`*!OF42`#A`tx;b~Iml<>Rm-O18sCLT0~obL%3*l)XSk5L6@tJeO=?uqf++C5+-C7`F0x9{fjm(e|=1bYa_SH<))>07y*qe>Si7eT047GC!gBCzuvItiiqkD zT&gTs00U#RV-+(B<^V4X@1dISZWhx#<}eqzzN6jjJGlBY4vKOfA=f00tA7W)T4Z|5 zR)%LU?uXeAzBv+oPCs;5tVXquw?t*0j%`{%^zS2n(;XjjS4S%93z)%{G4mIHc1dK1 zUhW|9ecC1l^cU&WMtQZEE|;y$4ZW0w+PfEAiK&l{#0C6%xWXJTp789ea>VMQ{K z>`yYB91Wrq0=+EM;SQy$8+q31Nkx*L*FxGSLQ@7u>yXtpLKMF@uj>Ln!pq(s&uK%n z%haG|;W5eNHtou9h+I^|`7hC9UQXWLyJlb`D6uXd4N^`?8%o_Z+D=CVuFczRi!g+fpN=t#^G^tX54 zUKbUKQc-ywV}lp{#>uHQ$z+73$T9d|tJ69Qw>RmJRs`KlyOmba4}HiVf9=-Anel50vkG-lx6{t!F1x@Axa+xXBEA~bx_Mb_=)IXSVO1Iwqmd06L$hR; z`BygGTx>q-V|>sDcp2eg{?3haIX&>s23By98T&wI4%N0S9JjG&Y)_G^(ldJgl=Jq3 zy~J;$G1SAS@>{7(%XwW z-tQ%007IorIWS%%%TZ*H%r%=o?@pUrI{75LGiV>)k`<9^b@%Kxm|tBH-o0{@=zvn7i@;$ zi~J5dhSoGzMM^Wk(HYL@vD+_qH$0$Q;;-%_!XhsFd!JtVz|Ks>?f1C)Y$__i(M3^N zwuD(QuQmiP>(HmfS7VQZQr4>{daMT&eSqfDh)mO}LYLNf+Q;UIgg=mJ%Vk{n;UEW36zR>WNe z@XhTcK&>*bL?r}#R=}v0`);3ax)0vk=tmZUV_51 zTtaGR(pY{qYR!efIid-SfCUIaO>hU8nL_xe@Q!=io1Xh)Mll@7m5}=bU}>J=WVAOz zy3+AH2@6w3)zzUAE!K3Gt!bczVz{Y6AnKT%p z6UsGfaSt^c!=YA}IaV^E8!m~!&>BRX&q7r=6%wC6J7hn|VnRvEMo05|E5+k@=GSSd z?9aPc%3!yE&?8t=Z0X@7PT;1EJL7EXbw&AC>1ZTNLb~!GtO(W=QJsDw`sW$lx;q$4 zybx&qJKQvoGe#2qC^xjA*v+Z=2`mKVZIi+ z=kUlS9uvr!cDO^r4E~6J2ewO`SaHREJ9BbEA+{9d5Lgdi6y4z4d3D6E7&XgNI&t>K z&RdMhf-D-h@G7WQ*sDbWH+6J z*JO&@b3Ch#)xwF(nhYR_7)^Vay>Mkhlsjx)Yw$5(IM=+E5wAweSxIdmj*!##Lh^vd_W z$j`&hOs@KR$jh9Hkjf1k;VnT4NrlhbSwbZ;FPk43f;`;urCM<3qo!tKJ*Gq)v{jK% z1@hz14EF* zBRxpS+v6a}o&O!j8qG9?&u2vS&Ki#~#0N&}2GnTLdRSh%R*1qa#Hk%~hg4E$!vMl%mFYO(yLR@gK z+~p43;yRLj>D-|Jw|8MDf_9NVDLG!Na7Pe4T{wvl#gh7)wYO;&f#HQP~YZGFP=Z0b#@# zNZUC-*B(s4jPPvi4afk5s^KP^sJaXd@~oMGa34AZ@L-{yvTK2K-=Zg=_3MaL4#=If z-$G-yunNjM-R$_@HS*9StktG8Z&S+q019!RO}`Kk(;CcSs0@Ky&8JxiuPc`rnpj#`Fug^XLH`Y&jj(k6#-MF3SAPJh>U?W zD@fba*MaR0+JTbHA-|1{r|J8_OXKH{Ed~tMUfSQjnOvpDjJN~sbNOAVO09Cz-yQ1r z@;6nj#ysL)d!;mQv-wc^;QC$xi1bj~W>$?_rJ^Pk#EsSP^@zR`WHrk>bnR!VA$Uo2 zu}R42kOx>*l%c6%D0c!1y5TP1PpkOEDUr#$ai3cDg8&)SF(t)Tjszv0sT6J{BuBC- zel``E2YndXK#Kj!W)9pEenm*uZicoB8aocw1cIQ{kZYRYCDV5>mjlA(pdf!Yc6s&# z$}UDrKJg{!{HDTvbs*r9*)jHhg|wQUAMbkt>W=LHBm8i?Y@au+IQ-KZdo)CLH|Be( zcTT}X_MRI%VapG74}rHQG1*#c_LH0X4uMj1-cVwho@bH+b#BTEGug#ykKXbum^3wa4^v5iT_QCXwVXVEA pyZo-TUKweg-#@s_I_55he7hgeD@zAjLvQ>AeRF zy-IIVrAbHO4c8^l^}oB;J8Ra=K6`)Po-^M$XZEa#(9u$+B4;KCfk0HMDmV2&AP8WB zrpQQuv;0G}HQ=Dq(a={^cGd9p^?muf0rtPP-|>IBCM6~PqWnwjw+jILHvjkf=Z$`O zf5C!+g8mGh*#PFu{$-ryI`f}l&I};*3wCClbL`E%=rua=lRb( z|My0}qGy=1?wRpts$Vt``m_DY`O9;bAL#zS#9x>*7+{RV{^tl-ABCQJTDL%8@OjL= zC%H|7D;p;V8`B-7X+F0uLC<4OM7;85+hg?+2%K8R(s2t%i;w8Db@B$t?X?JIqka4j zsm|>Oq}+_zV>feF6RgQcgadi|vy?V151-8*^SGs(9EBoxd#HMJU>A#(?I{-`{A(?0 z-|Q|(O(Qe5*_=ED8n`r{4$}#iII|UucZfX@AFaA38Qo^0bJhJ3y-H|;`KPH0+xrS+ zDW#f5@chplmFjsxjX}!aU6nx~T1(ZNa{4~t)z6Ax9Eb)aI%alsErfwJCchzW@}C`W zx(j|c73(8mE|kcJx6WS*ILv3Sima4zEbQF&mtE~A@gT_21W(^^=o%X?a~d5SIjQ$s zeP_FuqB&qXe@R(eqAw9<;kzD_B)jCP}r!wO@PI4$G6g=MBegV1uR6=V)pYaBeCYQ+9z#)Gp_J-Dk zPK;k9H>Q6!lwp7V2)}hG>8tShfI%7en(qd@?wnX*OniA?ACEHr7^W3i&3+x|U+a6v zql_agi1@uc$0{D>`!Ka8*_`(}1o|!VaV&*Xp?Q9tGtG&C3r`IVpT$X;+z&qAu5sf% z|Z~`b!VP8 zDrDRYjfB$Vq}+aMXB>WvaRC<^J`6TGHzz+?-<+rHo;MUhsh|iUyR6eCFrZ^!WUbnQDlH-k2lY|dH-o{XGBf&a_h$GfE z&Y*s`f>5!v_aVfJ5hAVlC>=U^5Mm6iH03dFw$C> zq6ycXOXuG zqwM8JMvrD|BG2(o6?B=$%o!o*U9Uyfk*7YrnJr)%0;PA2Nv&XQfirn_lUyGpsq9|S z&g}E>(rb-iC*hsCflZ*G7o8)(-1A`W8AC6;sAo5D`kOkETzD6;yAXr@)oqn0J7yU4 z_ThJ;C2N%`BYcsXTvjJaum$ep_zKW?Z~PoUOt%bklT?h9>;bQ5cwB)OjU(tqtzLy~ zHXn`x+%NH~qSRq3wCWpd{aAUwg)n$H40{CCx0JzC)b6r+F@?2%=t8|u;^0=H*TA?k zEIQF|1ZSKMD2+7CO$8;se`k8tn4ODw>xU%YxuwtUb?hA><{lruwPyq~6eGIQ&jpVJ zax6%5Q^es@b5D6GJ@vCfc^v+K3zEgxQt=X^g_)Gx3)qPv<}S|HqZPmcGvvpsKjbO3 zW|gbn7+`tqRsu{HOKl^XC8P1oFWsk!`BUl6p>(dF)DUxir=uJc_FxKI$O;9!jts|l z(f?5KZaE7{10lH(m>w>+T8$Mw9d-yez#@~nGxg*P#o*MlqH6Z9n>fZsT3*ugB0B*V z9adj$Q9S2U-FFrhT9K)HvlG&3yUluUo?`K!0`?AF6qTga}E2% zRy9(EvUX;?iYj-xYaFdxtmDT@s58QnEEvSkQVCzGN3g{kdT&-h& zN{4kCS?M(27#>Y-nTP)E$W3;V@Jh;7=00bJa(1w}g+!GX`^}XM{u#H6QsfaIxyHtA zJKkdjo5k=UH3JQjNKzvZ#AH7IbqflyxbJi?r+D2rKr{ z)muAxr{LR)8EL!PqSt&zHDAzUJ~~@5w77z4`mDJbeqzL|b!fJj4y#$|RFQ%UnWV8H zOJe3i$G8@fYn(nsOjy=4Pun)v>)@SuXV%U{Ez=y=Lb`pS-uWFL74x{x_m%!yw&o13 z=8f5*@aBn(^4{%_RPXUUL4cwn*C_Ysb zz?v2P9y9JtdxB5uUnt)>q2Nbtf9cJgOSrp1Js@woOzWoFQu|>)ioJYl#V+7kdPj{HRAhJ>$!=GWXMRW2NyIsqWT)5jv-S}e6IBs+h>IDf>!4JL1Qa2Bb)&i} z!QfDFSI3#S+yl)Lvwn0B&NM0~A*!Yd!^{AFG#ltDcuAcwQHTcrlmz))lC^> zBH=XT;#U7W4YxOlK;DH6(j~cUdb(r&7K-?1_F`i9aVa?RmD`P?;3IhRBk&J&JbNkr z^ewV3c3JxhebEQ31xN#P*)17A!BoOjKFpKZ&Y~c#@|D6If)1@D&&G+aIyC>8d4am*=E!j7o z{TA7UZbq8Lu}I36BYMnXrWlcgwf!xz%m-cnXNYFRv<0GkL{@1|`;lxgu*v0sY2%Ng z8X1rb^x7Y4rrca?&d&J+Y*hKvO{MI6ZwhKedbf(yyex4o&11xBFz7rT7I^{d0vWU- zBSxOvDB0N$Z}pQEyln+k7+~PHJV~6t>>AJco=5u2L5b|dd)tJB?gw%B z0D2ltgZ{>kZDWL#7#D@_c{@n3pA5fOT#b4|1KjBT_Yazq?y`zMmLJGHDP`xuy>e+g ziDB46XOe0)n3q^FuXiSQNxuf({gZbPba+M9Eu86M?FTLHRRu$ihS*U_O-h626ug*& zj2cEGJR7K9dxEzBs&tArI=!l#hPNN=A|IJATPX@Q6Fx0PhQ_v2Q0Y=B5oX@01S$2v z1!DbjSm7y8_#3Gf=A3Z}A1N;(LR4X&cdjV=5jXzI4p4*5QTomRjn|>1fwq1= zPJ{p@vEHYlIn~aQ>I*BEL+IwJv2u9YYJ8I>7P+vzsXER6HIjo5neNEn`fkBMK5Mp+ zU@d_~+O5bz((&HLhO_N(CiiDIEFsRXb~$!lwC6zSTIQn5ifT7ju9l_@UAPkgY*O~Y<%mN#DERGVu{ zwI8pei3jwexpaI4ma*WOhpH;;NafQNkncOJZW+q}Q_|XqPW5q^f-=OijUYCl{h-s2 zJ>d8c&Em+|jZx)Z)^!XwA5-|gvEB}p_34fgTH^W)A;s$A$yKI`GI_6b<2Af?iqAl;_| zpEkZ<2;!{0CE)Pzn|%F+^Fa?UKjL=&x`POQqq#Q4!6B`M(-C^5zejypw<=H!Jg{Ib z6L;ja9lbwLj$af0;jzH-wv*B(UgjVkVMC2Cs8k5IGRvk7%-rc5VqoWy zn1u7R>RZn-JJdTBL_)z%5Jf`qMrkuiUh`|~syl(U> z79z+&pRzZP{t#eZJ}bk3A~+^w5#4?Y-G9cT;&uy|Q862SwT6kCaW(}X;rI7CVuDTF z2}9vN3Yw7frrgGmS7w?G)iQC164&TK+h2<8-evHB5$IUyzbH&KFkVsb>przY^BS;b zWq;?YH0)@QhdB%spzc~aFZi6M0fb<|3fjPPL&#cACY1Bm-jNOp`g7*ojR6nX?7 z-iU0ny_+EfH>wcPc&O9@+&CH+C>v0)mQD8hk|!rvb|^Hzp0=2EamT8AhofHXV~GG4 zoyE;mTuKD(;-sQ952g@Iegc^^P>a3%5ZMG}2RcKZNAn`FBP{M=UYeGB_V)x7HS~gNSQkt05vnJ$p~BLMN(IHUFT^Z& z-Y(cnRALrAqTd4pK>1g*T&oN5dORmsYMDn%j<)Aa;!m{ThHf*{i$53$4X4vN*~$!@ z>oONe!adhkwco~|$|cp-SsIu}mB7RQ zVp&6lB=Fi5q2+H)qP$Lbt2RE()9}l!O^r-};PGX@`ztrb)ILvb5 s>_QDa{W}6ZFhLZ_!?)Rg|0+4VA{B)=>#4m7qNg5JMXj3!@^=IO2RKy6S^xk5 diff --git a/tests/integration/compare_test.go b/tests/integration/compare_test.go index cf0bac4c8a..5d5529c36e 100644 --- a/tests/integration/compare_test.go +++ b/tests/integration/compare_test.go @@ -33,7 +33,7 @@ func TestCompareTag(t *testing.T) { req = NewRequest(t, "GET", "/user2/repo1/compare/invalid") resp = session.MakeRequest(t, req, http.StatusNotFound) - assert.False(t, strings.Contains(resp.Body.String(), "/assets/img/500.png"), "expect 404 page not 500") + assert.False(t, strings.Contains(resp.Body.String(), ">500<"), "expect 404 page not 500") } // Compare with inferred default branch (master) diff --git a/tests/integration/links_test.go b/tests/integration/links_test.go index 11e6146d07..6edcbbc71b 100644 --- a/tests/integration/links_test.go +++ b/tests/integration/links_test.go @@ -36,8 +36,6 @@ func TestLinksNoLogin(t *testing.T) { "/user2/repo1/", "/user2/repo1/projects", "/user2/repo1/projects/1", - "/assets/img/404.png", - "/assets/img/500.png", "/.well-known/security.txt", } From 4b8fecd71ee83c96c905866d1ddebd9361aaff79 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sat, 24 Feb 2024 13:00:16 +0100 Subject: [PATCH 159/271] [RELEASE] switch to semantic versioning The release number displayed by the API and the CLI becomes: 7.0.0+1.22.0 instead of 1.22.0 It would otherwise be inconsistent to have different version number depending on the interface. With the current implementation `/api/forgejo/v1/version` would return `7.0.0+1.22.0` while `/api/v1/version` would return `1.22.0`. The release would be announced as `7.0.0+1.22.0` but the web API would display `1.22.0`. It may cause some tools that are Gitea specific to not behave as they should in the future if they expect a Gitea version number and activate some features depending on what it is. They would need to be patched to strip the leading Forgejo version number before asserting the Gitea version. Refs: https://codeberg.org/forgejo/forgejo/issues/2425 --- Makefile | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 194da59ef0..0f6c82fd78 100644 --- a/Makefile +++ b/Makefile @@ -85,19 +85,18 @@ endif STORED_VERSION_FILE := VERSION HUGO_VERSION ?= 0.111.3 +GITEA_COMPATIBILITY ?= 1.22.0 STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null) ifneq ($(STORED_VERSION),) FORGEJO_VERSION ?= $(STORED_VERSION) else - FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/-/+/' | sed 's/^v//') + FORGEJO_VERSION ?= $(shell git describe --exclude '*-test' --tags --always | sed 's/^v//')+${GITEA_COMPATIBILITY} endif RELEASE_VERSION ?= ${FORGEJO_VERSION} VERSION ?= ${RELEASE_VERSION} -GITEA_VERSION ?= 1.22.0 - -LDFLAGS := $(LDFLAGS) -X "main.ReleaseVersion=$(RELEASE_VERSION)" -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(GITEA_VERSION)" -X "main.Tags=$(TAGS)" -X "main.ForgejoVersion=$(FORGEJO_VERSION)" +LDFLAGS := $(LDFLAGS) -X "main.ReleaseVersion=$(RELEASE_VERSION)" -X "main.MakeVersion=$(MAKE_VERSION)" -X "main.Version=$(FORGEJO_VERSION)" -X "main.Tags=$(TAGS)" -X "main.ForgejoVersion=$(FORGEJO_VERSION)" LINUX_ARCHS ?= linux/amd64,linux/386,linux/arm-5,linux/arm-6,linux/arm64 From 1bab4358accee4e4ba455424bd2265dd7f6384e8 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 25 Feb 2024 19:50:46 +0100 Subject: [PATCH 160/271] [BUG] Don't overwrite protected branch accidentally - If a user tries to create another protected branching rule that specifies a set of branches already used by another rule, do not allow it. - Update the translation accordingly. - Adds integration test. - Resolves #2455 --- options/locale/locale_en-US.ini | 2 +- routers/web/repo/setting/protected_branch.go | 11 +++-- tests/integration/git_test.go | 7 ++++ tests/integration/repo_settings_test.go | 42 ++++++++++++++++++++ 4 files changed, 57 insertions(+), 5 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 0931e86941..c47a7ba664 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -2424,7 +2424,7 @@ settings.choose_branch = Choose a branch… settings.no_protected_branch = There are no protected branches. settings.edit_protected_branch = Edit settings.protected_branch_required_rule_name = Required rule name -settings.protected_branch_duplicate_rule_name = Duplicate rule name +settings.protected_branch_duplicate_rule_name = There is already a rule for this set of branches settings.protected_branch_required_approvals_min = Required approvals cannot be negative. settings.tags = Tags settings.tags.protection = Tag Protection diff --git a/routers/web/repo/setting/protected_branch.go b/routers/web/repo/setting/protected_branch.go index 85068f0ab2..827ebea7f8 100644 --- a/routers/web/repo/setting/protected_branch.go +++ b/routers/web/repo/setting/protected_branch.go @@ -132,12 +132,15 @@ func SettingsProtectedBranchPost(ctx *context.Context) { } } } else { - // FIXME: If a new ProtectBranch has a duplicate RuleName, an error should be returned. - // Currently, if a new ProtectBranch with a duplicate RuleName is created, the existing ProtectBranch will be updated. - // But we cannot modify this logic now because many unit tests rely on it. + // Check if a rule already exists with this rulename, if so redirect to it. protectBranch, err = git_model.GetProtectedBranchRuleByName(ctx, ctx.Repo.Repository.ID, f.RuleName) if err != nil { - ctx.ServerError("GetProtectBranchOfRepoByName", err) + ctx.ServerError("GetProtectedBranchRuleByName", err) + return + } + if protectBranch != nil { + ctx.Flash.Error(ctx.Tr("repo.settings.protected_branch_duplicate_rule_name")) + ctx.Redirect(fmt.Sprintf("%s/settings/branches/edit?rule_name=%s", ctx.Repo.RepoLink, protectBranch.RuleName)) return } } diff --git a/tests/integration/git_test.go b/tests/integration/git_test.go index d02427ffcf..2163c67cb5 100644 --- a/tests/integration/git_test.go +++ b/tests/integration/git_test.go @@ -18,6 +18,7 @@ import ( auth_model "code.gitea.io/gitea/models/auth" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" issues_model "code.gitea.io/gitea/models/issues" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" @@ -413,12 +414,17 @@ func doBranchProtectPRMerge(baseCtx *APITestContext, dstPath string) func(t *tes func doProtectBranch(ctx APITestContext, branch, userToWhitelist, unprotectedFilePatterns string) func(t *testing.T) { // We are going to just use the owner to set the protection. return func(t *testing.T) { + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{Name: ctx.Reponame, OwnerName: ctx.Username}) + rule := &git_model.ProtectedBranch{RuleName: branch, RepoID: repo.ID} + unittest.LoadBeanIfExists(rule) + csrf := GetCSRF(t, ctx.Session, fmt.Sprintf("/%s/%s/settings/branches", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame))) if userToWhitelist == "" { // Change branch to protected req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/edit", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), map[string]string{ "_csrf": csrf, + "rule_id": strconv.FormatInt(rule.ID, 10), "rule_name": branch, "unprotected_file_patterns": unprotectedFilePatterns, }) @@ -430,6 +436,7 @@ func doProtectBranch(ctx APITestContext, branch, userToWhitelist, unprotectedFil req := NewRequestWithValues(t, "POST", fmt.Sprintf("/%s/%s/settings/branches/edit", url.PathEscape(ctx.Username), url.PathEscape(ctx.Reponame)), map[string]string{ "_csrf": csrf, "rule_name": branch, + "rule_id": strconv.FormatInt(rule.ID, 10), "enable_push": "whitelist", "enable_whitelist": "on", "whitelist_users": strconv.FormatInt(user.ID, 10), diff --git a/tests/integration/repo_settings_test.go b/tests/integration/repo_settings_test.go index 16e0bb3d7d..3076148156 100644 --- a/tests/integration/repo_settings_test.go +++ b/tests/integration/repo_settings_test.go @@ -9,10 +9,12 @@ import ( "testing" "code.gitea.io/gitea/models/db" + git_model "code.gitea.io/gitea/models/git" repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" user_model "code.gitea.io/gitea/models/user" + gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/setting" repo_service "code.gitea.io/gitea/services/repository" "code.gitea.io/gitea/tests" @@ -128,3 +130,43 @@ func TestRepoAddMoreUnits(t *testing.T) { assertAddMore(t, false) }) } + +func TestProtectedBranch(t *testing.T) { + defer tests.PrepareTestEnv(t)() + user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1, OwnerID: user.ID}) + session := loginUser(t, user.Name) + + t.Run("Add", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + link := fmt.Sprintf("/%s/settings/branches/edit", repo.FullName()) + + req := NewRequestWithValues(t, "POST", link, map[string]string{ + "_csrf": GetCSRF(t, session, link), + "rule_name": "master", + "enable_push": "true", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + + // Verify it was added. + unittest.AssertExistsIf(t, true, &git_model.ProtectedBranch{RuleName: "master", RepoID: repo.ID}) + }) + + t.Run("Add duplicate", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + link := fmt.Sprintf("/%s/settings/branches/edit", repo.FullName()) + + req := NewRequestWithValues(t, "POST", link, map[string]string{ + "_csrf": GetCSRF(t, session, link), + "rule_name": "master", + "require_signed_": "true", + }) + session.MakeRequest(t, req, http.StatusSeeOther) + flashCookie := session.GetCookie(gitea_context.CookieNameFlash) + assert.NotNil(t, flashCookie) + assert.EqualValues(t, "error%3DThere%2Bis%2Balready%2Ba%2Brule%2Bfor%2Bthis%2Bset%2Bof%2Bbranches", flashCookie.Value) + + // Verify it wasn't added. + unittest.AssertCount(t, &git_model.ProtectedBranch{RuleName: "master", RepoID: repo.ID}, 1) + }) +} From 1272ac4f6925f900be0666c83272f91e4e266d54 Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 25 Feb 2024 21:24:12 +0100 Subject: [PATCH 161/271] [BUG] Use correct logout URL - If a `logout` event is send the user should be redirected to the homepage, there are three mechanism that can do this. The response of `/user/logout` and the event listener of notifications or stopwatch. It's essentially a race for what's processed first to determine which mechanism takes care of redirecting the user. - Fix that the redirection mechanism of the notification and stopwatch event listener redirects to an absolute URL. - Ref: #2135 --- web_src/js/features/notification.js | 2 +- web_src/js/features/stopwatch.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/web_src/js/features/notification.js b/web_src/js/features/notification.js index 4dcf02d2dc..d325148413 100644 --- a/web_src/js/features/notification.js +++ b/web_src/js/features/notification.js @@ -112,7 +112,7 @@ export function initNotificationCount() { type: 'close', }); worker.port.close(); - window.location.href = appSubUrl; + window.location.href = `${window.location.origin}${appSubUrl}/`; } else if (event.data.type === 'close') { worker.port.postMessage({ type: 'close', diff --git a/web_src/js/features/stopwatch.js b/web_src/js/features/stopwatch.js index f43014fec5..e20a983e60 100644 --- a/web_src/js/features/stopwatch.js +++ b/web_src/js/features/stopwatch.js @@ -74,7 +74,7 @@ export function initStopwatch() { type: 'close', }); worker.port.close(); - window.location.href = appSubUrl; + window.location.href = `${window.location.origin}${appSubUrl}/`; } else if (event.data.type === 'close') { worker.port.postMessage({ type: 'close', From 5d2c4706d0a5d4690a25605818c6d4edd8f0a0d6 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 25 Feb 2024 23:08:09 +0100 Subject: [PATCH 162/271] [CI] run frontend checks --- .forgejo/workflows/testing.yml | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/.forgejo/workflows/testing.yml b/.forgejo/workflows/testing.yml index a4b54c4da5..80fd87152e 100644 --- a/.forgejo/workflows/testing.yml +++ b/.forgejo/workflows/testing.yml @@ -23,10 +23,22 @@ jobs: - run: make --always-make -j$(nproc) lint-backend checks-backend # ensure the "go-licenses" make target runs env: TAGS: bindata sqlite sqlite_unlock_notify + frontend-checks: + if: ${{ !startsWith(vars.ROLE, 'forgejo-') }} + runs-on: docker + container: + image: 'docker.io/node:20-bookworm' + steps: + - uses: https://code.forgejo.org/actions/checkout@v3 + - run: make deps-frontend + - run: make lint-frontend + - run: make checks-frontend + - run: make test-frontend + - run: make frontend test-unit: if: ${{ !startsWith(vars.ROLE, 'forgejo-') }} runs-on: docker - needs: [backend-checks] + needs: [backend-checks, frontend-checks] container: image: 'docker.io/node:20-bookworm' services: @@ -67,7 +79,7 @@ jobs: test-mysql: if: ${{ !startsWith(vars.ROLE, 'forgejo-') }} runs-on: docker - needs: [backend-checks] + needs: [backend-checks, frontend-checks] container: image: 'docker.io/node:20-bookworm' services: @@ -113,7 +125,7 @@ jobs: test-pgsql: if: ${{ !startsWith(vars.ROLE, 'forgejo-') }} runs-on: docker - needs: [backend-checks] + needs: [backend-checks, frontend-checks] container: image: 'docker.io/node:20-bookworm' services: @@ -161,7 +173,7 @@ jobs: test-sqlite: if: ${{ !startsWith(vars.ROLE, 'forgejo-') }} runs-on: docker - needs: [backend-checks] + needs: [backend-checks, frontend-checks] container: image: 'docker.io/node:20-bookworm' steps: From 1424df9b71371f7083f2ec3da7e8ecac8f00c653 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 25 Feb 2024 23:40:54 +0100 Subject: [PATCH 163/271] [FRONTEND] package-lock.json is named as forgejo --- package-lock.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index f1f8cc4705..23dcb84da6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,5 @@ { - "name": "gitea", + "name": "forgejo", "lockfileVersion": 3, "requires": true, "packages": { From 9560492a84ce85acae6de880076196316db3f095 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 25 Feb 2024 23:41:33 +0100 Subject: [PATCH 164/271] [FRONTEND] move the gitea svg to web_src/svg and run make svg to update the forgejo svg as well --- build/generate-svg.js | 1 - public/assets/img/svg/gitea-forgejo.svg | 10 +--------- public/assets/img/svg/gitea-gitea.svg | 2 +- web_src/svg/gitea-gitea.svg | 1 + 4 files changed, 3 insertions(+), 11 deletions(-) create mode 100644 web_src/svg/gitea-gitea.svg diff --git a/build/generate-svg.js b/build/generate-svg.js index 2c0a5e37ba..1d92bc0b19 100755 --- a/build/generate-svg.js +++ b/build/generate-svg.js @@ -59,7 +59,6 @@ async function main() { await Promise.all([ ...processFiles('node_modules/@primer/octicons/build/svg/*-16.svg', {prefix: 'octicon'}), ...processFiles('web_src/svg/*.svg'), - ...processFiles('public/assets/img/gitea.svg', {fullName: 'gitea-gitea'}), ]); } diff --git a/public/assets/img/svg/gitea-forgejo.svg b/public/assets/img/svg/gitea-forgejo.svg index ef617c00f3..22ae790357 100644 --- a/public/assets/img/svg/gitea-forgejo.svg +++ b/public/assets/img/svg/gitea-forgejo.svg @@ -1,9 +1 @@ - - - - - - - - - + \ No newline at end of file diff --git a/public/assets/img/svg/gitea-gitea.svg b/public/assets/img/svg/gitea-gitea.svg index 5d89fa1da9..61ef37074e 100644 --- a/public/assets/img/svg/gitea-gitea.svg +++ b/public/assets/img/svg/gitea-gitea.svg @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/web_src/svg/gitea-gitea.svg b/web_src/svg/gitea-gitea.svg new file mode 100644 index 0000000000..5d89fa1da9 --- /dev/null +++ b/web_src/svg/gitea-gitea.svg @@ -0,0 +1 @@ + \ No newline at end of file From c4a92ec9da059fb8cf4a435ff36a7f1df5a08c0a Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 25 Feb 2024 23:39:07 +0100 Subject: [PATCH 165/271] [FRONTEND] fix javascript linting errors --- web_src/js/features/repo-release.js | 4 ++-- web_src/js/standalone/forgejo-swagger.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/web_src/js/features/repo-release.js b/web_src/js/features/repo-release.js index efa1f1ad40..3784bed2b1 100644 --- a/web_src/js/features/repo-release.js +++ b/web_src/js/features/repo-release.js @@ -2,14 +2,14 @@ import {hideElem, showElem} from '../utils/dom.js'; import {initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js'; export function initRepoRelease() { - [...document.querySelectorAll('.remove-rel-attach')].forEach((el) => { + for (const el of document.querySelectorAll('.remove-rel-attach')) { el.addEventListener('click', (e) => { const uuid = e.target.getAttribute('data-uuid'); const id = e.target.getAttribute('data-id'); document.querySelector(`input[name='attachment-del-${uuid}']`).value = 'true'; hideElem(`#attachment-${id}`); }); - }); + } } export function initRepoReleaseNew() { diff --git a/web_src/js/standalone/forgejo-swagger.js b/web_src/js/standalone/forgejo-swagger.js index b565827b30..2419bdb3c6 100644 --- a/web_src/js/standalone/forgejo-swagger.js +++ b/web_src/js/standalone/forgejo-swagger.js @@ -5,7 +5,7 @@ window.addEventListener('load', async () => { const url = document.getElementById('swagger-ui').getAttribute('data-source'); const ui = SwaggerUI({ - url: url, + url, dom_id: '#swagger-ui', deepLinking: true, docExpansion: 'none', From dc825acb3358b85f05d4fa1b01fbfea9f9d5bc5d Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Sun, 25 Feb 2024 22:50:12 +0000 Subject: [PATCH 166/271] [I18N] Translations update from Weblate (#2428) Translations update from [Weblate](https://translate.codeberg.org) for [Forgejo/forgejo](https://translate.codeberg.org/projects/forgejo/forgejo/). Current translation status: ![Weblate translation status](https://translate.codeberg.org/widget/forgejo/forgejo/horizontal-auto.svg) Co-authored-by: earl-warren Co-authored-by: 0ko <0ko@users.noreply.translate.codeberg.org> Co-authored-by: mondstern Co-authored-by: Wuzzy Co-authored-by: Application-Maker Co-authored-by: Gusted Co-authored-by: Dirk Co-authored-by: Squeljur Co-authored-by: nebras Co-authored-by: Salif Mehmed Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2428 Co-authored-by: Codeberg Translate Co-committed-by: Codeberg Translate --- options/locale/locale_ar.ini | 7 +- options/locale/locale_bg.ini | 24 ++- options/locale/locale_de-DE.ini | 243 +++++++++++------------ options/locale/locale_fr-FR.ini | 35 +++- options/locale/locale_nl-NL.ini | 334 +++++++++++++++++++++++++------- options/locale/locale_ru-RU.ini | 197 ++++++++++--------- options/locale/locale_sl.ini | 67 +++++++ 7 files changed, 617 insertions(+), 290 deletions(-) diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index ca1127d624..7f0f9791c7 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -125,6 +125,7 @@ copy_hash = انسخ البصمة remove = أزل remove_all = أزل الكل remove_label_str = أزل العنصر "%s" +confirm_delete_artifact = هل أنت متأكد أنك تريد حذف العنصر '%s'؟ [install] db_name = اسم قاعدة البيانات @@ -573,7 +574,7 @@ settings.add_collaborator_blocked_our = لا يمكن إضافة المشترك commits.browse_further = تصفح أكثر settings.ignore_stale_approvals = تجاهل الطلبات الراكدة rss.must_be_on_branch = يجب أن تكون على فرع لتحصل على موجز RSS. -clone_in_vscodium = إستنسخ في VS Codium +clone_in_vscodium = إستنسخ في VSCodium admin.enabled_flags = العلامات المفعلة لهذا المستودع: settings.new_owner_blocked_doer = المالك الجديد حظرك. issues.comment.blocked_by_user = لا يمكنك أن ترسل تعليقاً على هذه المسألة لأنك محظور من قبل مالك المستودع أو مرسل المسألة. @@ -1728,6 +1729,10 @@ invalid_ssh_key = فشل تحقق مفتاح الـSSH: %s AuthName = اسم الأذن SSPISeparatorReplacement = الفاصلة openid_been_used = عنوان الـOpenID "%s" مُستخدم بالفعل. +git_ref_name_error = `يجب أن يكون اسمًا مرجعيًا جيدًا لـ Git.` +include_error = ` يجب أن يحتوي على سلسلة فرعية "%s".` +size_error = `يجب أن يكون بالحجم %s.' +glob_pattern_error = `النمط الشامل غير صالح: %s.` [home] filter = تصفيات أخرى diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 2cd88b4a62..08554a5b8d 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -8,7 +8,7 @@ applications = Приложения visibility = Видимост на потребителя location = Местоположение password = Парола -appearance = Външен вид +appearance = Облик new_password = Нова Парола oauth2_application_edit = Редактиране repos = Хранилища @@ -36,7 +36,7 @@ link_account = Свързване на акаунт add_new_gpg_key = Добавяне на GPG ключ manage_gpg_keys = Управление на GPG ключовете manage_ssh_keys = Управление на SSH ключовете -old_password = Текуща Парола +old_password = Текуща парола public_profile = Публичен Профил full_name = Пълно Име security = Сигурност @@ -89,7 +89,7 @@ create_new = Създаване… preview = Преглеждане disabled = Изключено licenses = Лицензи -sign_in = Влизане +sign_in = Вход copy_content = Копиране на съдържанието user_profile_and_more = Профил и Настройки… view = Преглед @@ -111,7 +111,7 @@ forks = Разклонения concept_user_organization = Организация link_account = Свързване на акаунт your_profile = Профил -sign_out = Излизане +sign_out = Изход settings = Настройки locked = Заключено error = Грешка @@ -222,6 +222,12 @@ issues.previous = Предишна create_repo = Създаване на хранилище template_helper = Хранилището да е шаблон repo_name = Име на хранилището +issues.label.filter_sort.alphabetically = По азбучен ред +settings.event_repository = Хранилище +issues.label.filter_sort.reverse_alphabetically = По низходящ азбучен ред +issues.filter_sort.oldest = Най-стари +issues.filter_sort = Сортиране +issues.filter_sort.latest = Най-нови [modal] confirm = Потвърждаване @@ -285,6 +291,11 @@ password = Парола host = Хост ssl_mode = SSL install = Инсталация +install_btn_confirm = Инсталиране на Forgejo +app_name = Заглавие на сайта +admin_name = Потребителско име за администратор +confirm_password = Потвърждение на паролата +title = Първоначална Конфигурация [filter] string.asc = А - Я @@ -361,6 +372,9 @@ users.edit = Редактиране config.db_user = Потребителско име config.db_name = Име first_page = Първа +config.app_name = Заглавие на сайта +packages.repository = Хранилище +notices.type_1 = Хранилище [error] not_found = Целта не може да бъде намерена. @@ -399,6 +413,8 @@ create_new_account = Регистриране на акаунт active_your_account = Активирайте акаунта си register_helper_msg = Вече имате акаунт? Влезте сега! reset_password = Възстановяване на акаунта +disable_register_prompt = Регистрирането е изключено. Моля, свържете се с вашия администратор на сайта. +remember_me = Запомни ме [aria] footer.software = Относно Софтуера diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index e57bf94277..796bd41adf 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -12,7 +12,7 @@ sign_up=Registrieren link_account=Account verbinden register=Registrieren version=Version -powered_by=Powered by %s +powered_by=Betrieben mit %s page=Seite template=Vorlage language=Sprache @@ -52,10 +52,10 @@ webauthn_reload=Neu laden repository=Repository organization=Organisation -mirror=Mirror +mirror=Spiegel new_repo=Neues Repository new_migrate=Neue Migration -new_mirror=Neuer Mirror +new_mirror=Neuer Spiegel new_fork=Neuer Fork new_org=Neue Organisation new_project=Neues Projekt @@ -70,7 +70,7 @@ your_settings=Einstellungen all=Alle sources=Quellen -mirrors=Mirrors +mirrors=Spiegel collaborative=Kollaborativ forks=Forks @@ -141,6 +141,7 @@ name=Name value=Wert view = Ansehen tracked_time_summary = Zusammenfassung erfasster Zeit, basierend auf Filtern der Issue-Liste +confirm_delete_artifact = Bist du sicher, das Artefakt '%s' löschen zu wollen? [aria] navbar=Navigationsleiste @@ -211,8 +212,8 @@ 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_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 Mirrors 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 Repositories und die authorized_keys-Datei manuell neu synchronisierst. Du bestätigst, dass du sicherstellst, dass die Repository- und Mirror-Einstellungen korrekt sind. +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 Repositories 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. no_admin_and_disable_registration=Du kannst Selbst-Registrierungen nicht deaktivieren, ohne ein Administratorkonto zu erstellen. @@ -315,7 +316,7 @@ my_repos=Repositorys show_more_repos=Zeige mehr Repositorys … collaborative_repos=Gemeinschaftliche Repositorys my_orgs=Meine Organisationen -my_mirrors=Meine Mirrors +my_mirrors=Meine Spiegel view_home=%s ansehen search_repos=Finde ein Repository … filter=Andere Filter @@ -587,14 +588,14 @@ invalid_ssh_key=Dein SSH-Key kann nicht überprüft werden: %s invalid_gpg_key=Dein GPG-Key kann nicht überprüft werden: %s invalid_ssh_principal=Ungültige Identität: %s must_use_public_key=Der von Dir bereitgestellte Key ist ein privater Key. Bitte lade Deinen privaten Key nirgendwo hoch. Verwende stattdessen Deinen öffentlichen Key. -unable_verify_ssh_key=„Der SSH-Key kann nicht verifiziert werden, überprüfe ihn auf Fehler.“ +unable_verify_ssh_key=Der SSH-Key kann nicht verifiziert werden, überprüfe ihn auf Fehler. auth_failed=Authentifizierung fehlgeschlagen: %v -still_own_repo=„Dein Konto besitzt ein oder mehrere Repositorys. Diese müssen erst gelöscht oder übertragen werden.“ -still_has_org=„Dein Konto ist Mitglied einer oder mehrerer Organisationen, verlasse diese zuerst.“ -still_own_packages=„Dein Konto besitzt ein oder mehrere Pakete, lösche diese zuerst.“ -org_still_own_repo=„Diese Organisation besitzt noch ein oder mehrere Repositorys. Diese müssen zuerst gelöscht oder übertragen werden.“ -org_still_own_packages=„Diese Organisation besitzt noch ein oder mehrere Pakete, lösche diese zuerst.“ +still_own_repo=Dein Konto besitzt ein oder mehrere Repositorys. Diese müssen erst gelöscht oder übertragen werden. +still_has_org=Dein Konto ist Mitglied einer oder mehrerer Organisationen, verlasse diese zuerst. +still_own_packages=Dein Konto besitzt ein oder mehrere Pakete, lösche diese zuerst. +org_still_own_repo=Diese Organisation besitzt noch ein oder mehrere Repositorys. Diese müssen zuerst gelöscht oder übertragen werden. +org_still_own_packages=Diese Organisation besitzt noch ein oder mehrere Pakete, lösche diese zuerst. target_branch_not_exist=Der Ziel-Branch existiert nicht. username_error_no_dots = ` darf nur alphanumerische Zeichen („0-9“, „a-z“, „A-Z“), Bindestriche („-“), Unterstriche („_“) enthalten. Es kann nicht mit nicht-alphanumerischen Zeichen beginnen oder enden und aufeinanderfolgende nicht-alphanumerische Zeichen sind ebenfalls verboten.` @@ -755,7 +756,7 @@ manage_gpg_keys=GPG-Schlüssel verwalten add_key=Schlüssel hinzufügen ssh_desc=Diese öffentlichen SSH-Keys sind mit deinem Account verbunden. Der dazugehörigen privaten SSH-Keys geben dir vollen Zugriff auf deine Repositorys. Verifizierte SSH-Key können verwendet werden, um SSH-signierte Git-Commits zu signieren. principal_desc=Diese SSH-Zertifikat-Identitäten sind mit deinem Konto verknüpft und erlauben den vollen Zugriff auf deine Repositories. -gpg_desc=Diese öffentlichen GPG-Keys sind mit deinem Account verbunden. Halte die dazugehörigen privaten GPG-Keys geheim, da diese deine Commits signieren. +gpg_desc=Diese öffentlichen GPG-Keys sind mit deinem Account verbunden und werden benutzt um deine Commits zu verifizieren. Halte die dazugehörigen privaten GPG-Keys geheim, da diese deine Commits signieren. ssh_helper=Brauchst du Hilfe? Hier ist GitHubs Anleitung zum Erzeugen von SSH-Schlüsseln oder zum Lösen einfacher SSH-Probleme. gpg_helper=Brauchst du Hilfe? Hier ist GitHubs Anleitung über GPG. add_new_key=SSH-Schlüssel hinzufügen @@ -878,7 +879,7 @@ oauth2_application_remove_description=Das Entfernen einer OAuth2-Anwendung hat z oauth2_application_locked=Wenn es in der Konfiguration aktiviert ist, registriert Forgejo einige OAuth2-Anwendungen beim Starten vor. Um unerwartetes Verhalten zu verhindern, können diese weder bearbeitet noch entfernt werden. Weitere Informationen findest Du in der OAuth2-Dokumentation. authorized_oauth2_applications=Autorisierte OAuth2-Anwendungen -authorized_oauth2_applications_description=Den folgenden Drittanbieter-Apps hast du Zugriff auf deinen persönlichen Forgejo-Account gewährt. Bitte widerrufe die Autorisierung für Apps, die du nicht mehr nutzt. +authorized_oauth2_applications_description=Den folgenden Drittanbieter-Apps hast du Zugriff auf deinen persönlichen Forgejo-Account gewährt. Bitte widerrufe die Autorisierung für Apps, die nicht länger verwendet werden. revoke_key=Widerrufen revoke_oauth2_grant=Autorisierung widerrufen revoke_oauth2_grant_description=Wenn du die Autorisierung widerrufst, kann die Anwendung nicht mehr auf deine Daten zugreifen. Bist du dir sicher? @@ -944,11 +945,12 @@ visibility.private_tooltip=Sichtbar nur für Mitglieder von Organisationen, dene user_block_success = Dieser Benutzer wurde erfolgreich blockiert. twofa_recovery_tip = Falls du dein Gerät verlierst, wirst du in der Lage sein, einen einmalig verwendbaren Key zu benutzen, um den auf dein Konto wiederherzustellen. webauthn_alternative_tip = Du möchtest vielleicht eine zusätzliche Authentifizierungsmethode einrichten. -blocked_users_none = Du hast keine Benutzer blockiert. +blocked_users_none = Keine Benutzer blockiert. webauthn_key_loss_warning = Falls du deine Security-Keys verlierst, wirst du Zugang zu deinem Konto verlieren. user_unblock_success = Die Blockierung dieses Benutzers wurde erfolgreich zurückgenommen. blocked_users = Blockierte Benutzer blocked_since = Blockiert seit %s +change_password = Passwort ändern [repo] owner=Besitzer @@ -984,7 +986,7 @@ generate_from=Erstelle aus repo_desc=Beschreibung repo_desc_helper=Gib eine kurze Beschreibung an (optional) repo_lang=Sprache -repo_gitignore_helper=Wähle eine .gitignore-Vorlage aus. +repo_gitignore_helper=.gitignore-Vorlagen auswählen. repo_gitignore_helper_desc=Wähle aus einer Liste an Vorlagen für bekannte Sprachen, welche Dateien ignoriert werden sollen. Typische Artefakte, die durch die Build-Tools der gewählten Sprache generiert werden, sind standardmäßig Bestandteil der .gitignore. issue_labels=Issue-Labels issue_labels_helper=Wähle eine Issue-Label-Sammlung. @@ -1005,16 +1007,16 @@ default_branch=Standardbranch default_branch_label=Standard default_branch_helper=Der default-Branch ist der Basisbranch für Pull-Requests und Commits. mirror_prune=Entfernen -mirror_prune_desc=Entferne veraltete remote-tracking-Referenzen -mirror_interval=Mirror-Intervall (gültige Zeiteinheiten sind „h“, „m“, „s“). 0 deaktiviert die regelmäßige Synchronisation. (Minimales Intervall: %s) +mirror_prune_desc=Entferne veraltete Remote-Tracking-Referenzen +mirror_interval=Spiegelintervall (gültige Zeiteinheiten sind „h“, „m“, „s“). 0 deaktiviert die regelmäßige Synchronisation. (Minimales Intervall: %s) mirror_interval_invalid=Das Spiegel-Intervall ist ungültig. mirror_sync_on_commit=Synchronisieren, wenn Commits gepusht wurden mirror_address=Klonen via URL mirror_address_desc=Gib alle erforderlichen Anmeldedaten im Abschnitt „Authentifizierung“ ein. mirror_address_url_invalid=Die angegebene URL ist ungültig. Achte darauf, alle Komponenten der URL korrekt zu maskieren. -mirror_address_protocol_invalid=Die angegebene URL ist ungültig. Nur URLs beginnend mit http(s):// oder git:// sind möglich. +mirror_address_protocol_invalid=Die angegebene URL ist ungültig. Nur Orte mit „http(s)://“ oder „git://“ können fürs Spiegeln benutzt werden. mirror_lfs=Großdatei-Speicher (LFS) -mirror_lfs_desc=Mirroring von LFS-Dateien aktivieren. +mirror_lfs_desc=Spiegeln von LFS-Dateien aktivieren. mirror_lfs_endpoint=LFS-Endpunkt mirror_lfs_endpoint_desc=Sync wird versuchen, die Klon-URL zu verwenden, um den LFS-Server zu bestimmen. Du kannst auch einen eigenen Endpunkt angeben, wenn die LFS-Dateien woanders gespeichert werden. mirror_last_synced=Zuletzt synchronisiert @@ -1104,7 +1106,7 @@ migrate.github_token_desc=Du kannst hier ein oder mehrere Token durch Komma getr 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_local_path=Der lokale Pfad ist ungültig. Er existiert nicht oder ist kein Verzeichnis. migrate.invalid_lfs_endpoint=Ungültiger LFS Endpunkt. migrate.failed=Fehler bei der Migration: %v migrate.migrate_items_options=Zugangs-Token wird benötigt, um zusätzliche Elemente zu migrieren @@ -1133,7 +1135,7 @@ migrate.migrating_pulls=Pull-Requests werden migriert migrate.cancel_migrating_title=Migration abbrechen migrate.cancel_migrating_confirm=Möchtest du diese Migration abbrechen? -mirror_from=Mirror von +mirror_from=Spiegel von forked_from=geforkt von generated_from=erzeugt von fork_from_self=Du kannst kein Repository forken, das dir bereits gehört. @@ -1292,10 +1294,10 @@ editor.revert=%s zurücksetzen auf: commits.desc=Durchsuche die Quellcode-Änderungshistorie. commits.commits=Commits -commits.no_commits=Keine gemeinsamen Commits. "%s" und "%s" haben vollständig unterschiedliche Historien. +commits.no_commits=Keine gemeinsamen Commits. „%s“ und „%s“ haben vollständig unterschiedliche Historien. commits.nothing_to_compare=Diese Branches sind auf demselben Stand. -commits.search=Commits durchsuchen… -commits.search.tooltip=Du kannst Suchbegriffen "author:", " committer:", "after:", oder " before:" voranstellen, z.B. "revert author:Alice before:2019-04-01". +commits.search=Commits durchsuchen … +commits.search.tooltip=Du kannst Suchbegriffen „author:“, „committer:“, „after:“, oder „before:“ voranstellen, z.B. „revert author:Alice before:2019-01-13“. commits.find=Suchen commits.search_all=Alle Branches commits.author=Autor @@ -1334,18 +1336,18 @@ projects.create=Projekt erstellen projects.title=Titel projects.new=Neues Projekt projects.new_subheader=Koordiniere, verfolge und aktualisiere deine Arbeit an einem Ort, so dass Projekte transparent und planmäßig bleiben. -projects.create_success=Das Projekt "%s" wurde erstellt. +projects.create_success=Das Projekt „%s“ wurde erstellt. projects.deletion=Projekt löschen projects.deletion_desc=Das Löschen eines Projekts entfernt es von allen damit zusammenhängenden Issues. Fortfahren? projects.deletion_success=Das Projekt wurde gelöscht. projects.edit=Projekte bearbeiten projects.edit_subheader=Benutze Projekte, um Issues zu organisieren und den Fortschritt darzustellen. projects.modify=Projekt aktualisieren -projects.edit_success=Projekt "%s" wurde aktualisiert. +projects.edit_success=Projekt „%s“ wurde aktualisiert. projects.type.none=Ohne projects.type.basic_kanban=Einfaches Kanban -projects.type.bug_triage=Bug Triage -projects.template.desc=Projektvorlage +projects.type.bug_triage=Bug-Triage +projects.template.desc=Vorlage projects.template.desc_helper=Wähle eine Projektvorlage aus, um loszulegen projects.type.uncategorized=Nicht kategorisiert projects.column.edit=Spalte bearbeiten @@ -1354,11 +1356,11 @@ projects.column.new_title=Name projects.column.new_submit=Spalte erstellen projects.column.new=Neue Spalte projects.column.set_default=Als Standard verwenden -projects.column.set_default_desc=Diese Spalte als Standard für unkategorisierte Issues und Pull Requests festlegen +projects.column.set_default_desc=Diese Spalte als Standard für nicht kategorisierte Issues und Pull Requests festlegen projects.column.unset_default=Standard entfernen -projects.column.unset_default_desc=Diese Spalte als Standard entfernen +projects.column.unset_default_desc=Diese Spalte nicht als Standard verwenden projects.column.delete=Spalte löschen -projects.column.deletion_desc=Beim Löschen einer Projektspalte werden alle dazugehörigen Issues nach 'Nicht kategorisiert' verschoben. Fortfahren? +projects.column.deletion_desc=Beim Löschen einer Projektspalte werden alle dazugehörigen Issues nach ‚Nicht kategorisiert‘ verschoben. Fortfahren? projects.column.color=Farbe projects.open=Öffnen projects.close=Schließen @@ -1375,9 +1377,9 @@ issues.filter_labels=Label filtern issues.filter_reviewers=Reviewer filtern issues.new=Neues Issue issues.new.title_empty=Der Titel kann nicht leer sein -issues.new.labels=Label +issues.new.labels=Labels issues.new.no_label=Kein Label -issues.new.clear_labels=Label entfernen +issues.new.clear_labels=Labels entfernen issues.new.projects=Projekte issues.new.clear_projects=Projekte löschen issues.new.no_projects=Kein Projekt @@ -1396,7 +1398,7 @@ issues.new.no_reviewers=Keine Reviewer issues.choose.get_started=Los geht's issues.choose.open_external_link=Öffnen issues.choose.blank=Standard -issues.choose.blank_about=Erstelle einen Issue aus dem Standardtemplate. +issues.choose.blank_about=Erstelle ein Issue aus der Standardvorlage. issues.choose.ignore_invalid_templates=Ungültige Vorlagen wurden ignoriert issues.choose.invalid_templates=%v ungültige Vorlage(n) gefunden issues.choose.invalid_config=Die Issue-Konfiguration enthält Fehler: @@ -1406,11 +1408,11 @@ issues.new_label=Neues Label issues.new_label_placeholder=Labelname issues.new_label_desc_placeholder=Beschreibung issues.create_label=Label erstellen -issues.label_templates.title=Lade vordefinierte Label -issues.label_templates.info=Es existieren noch keine Label. Erstelle ein neues Label („Neues Label“) oder verwende das Standard-Label-Set: +issues.label_templates.title=Eine vordefinierte Label-Sammung laden +issues.label_templates.info=Es existieren noch keine Labels. Erstelle ein neues Label („Neues Label“) oder verwende die Standard-Label-Sammlung: issues.label_templates.helper=Wähle ein Label-Set issues.label_templates.use=Label-Set verwenden -issues.label_templates.fail_to_load_file=Fehler beim Laden der Label-Template-Datei "%s": %v +issues.label_templates.fail_to_load_file=Fehler beim Laden der Label-Vorlagen-Datei „%s“: %v issues.add_label=hat das Label %s %s hinzugefügt issues.add_labels=hat die Labels %s %s hinzugefügt issues.remove_label=hat das Label %s %s entfernt @@ -1434,8 +1436,8 @@ issues.remove_ref_at=`hat die Referenz %s entfernt %s` issues.add_ref_at=`hat die Referenz %s hinzugefügt %s` issues.delete_branch_at=`löschte die Branch %s %s` issues.filter_label=Label -issues.filter_label_exclude=„Alt + Klick/Enter verwenden, um Label auszuschließen” -issues.filter_label_no_select=Alle Label +issues.filter_label_exclude=`Alt + Klick/Enter verwenden, um Labels auszuschließen` +issues.filter_label_no_select=Alle Labels issues.filter_label_select_no_label=Kein Label issues.filter_milestone=Meilenstein issues.filter_milestone_all=Alle Meilensteine @@ -1512,9 +1514,9 @@ issues.closed_at=`hat diesen Issue %[2]s geschlo issues.reopened_at=`hat diesen Issue %[2]s wieder geöffnet` issues.commit_ref_at=`hat dieses Issue %[2]s aus einem Commit referenziert` issues.ref_issue_from=`hat %[2]s auf dieses Issue verwiesen %[4]s` -issues.ref_pull_from=`hat %[2]s auf diesen Pull Request verwiesen %[4]s` -issues.ref_closing_from=`hat %[2]s auf einen Pull Request %[4]s verwiesen, welcher das Issue schließen wird` -issues.ref_reopening_from=`hat auf einen Pull Request %[4]s verwiesen, welcher das Issue %[2]s erneut öffnen wird` +issues.ref_pull_from=`hat %[2]s auf diesen Pull-Request verwiesen %[4]s` +issues.ref_closing_from=`hat %[2]s auf einen Pull-Request %[4]s verwiesen, welcher das Issue schließen wird` +issues.ref_reopening_from=`hat auf einen Pull-Request %[4]s verwiesen, welcher das Issue %[2]s erneut öffnen wird` issues.ref_closed_from=`hat dieses Issue %[4]s geschlossen %[2]s` issues.ref_reopened_from=`hat dieses Issue %[4]s %[2]s wieder geöffnet` issues.ref_from=`von %[1]s` @@ -1546,10 +1548,10 @@ issues.label_color=Labelfarbe issues.label_exclusive=Exklusiv issues.label_archive=Label archivieren issues.label_archived_filter=Archivierte Labels anzeigen -issues.label_archive_tooltip=Archivierte Labels werden bei der Suche nach Label standardmäßig von den Vorschlägen ausgeschlossen. +issues.label_archive_tooltip=Archivierte Labels werden bei der Suche nach Labels standardmäßig von den Vorschlägen ausgeschlossen. issues.label_exclusive_desc=Nenne das Label Bereich/Element um es gegenseitig ausschließend mit anderen Bereich/ Labels zu machen. issues.label_exclusive_warning=Alle im Konflikt stehenden Labels werden beim Bearbeiten der Labels eines Issues oder eines Pull-Requests entfernt. -issues.label_count=%d Label +issues.label_count=%d Labels issues.label_open_issues=%d offene Issues issues.label_edit=Bearbeiten issues.label_delete=Löschen @@ -1595,7 +1597,7 @@ issues.delete.text=Möchtest du dieses Issue wirklich löschen? (Dadurch wird de issues.tracker=Zeiterfassung issues.start_tracking_short=Zeiterfassung starten issues.start_tracking=Zeiterfassung starten -issues.start_tracking_history=hat die Zeiterfassung %s gestartet +issues.start_tracking_history=`hat die Zeiterfassung %s gestartet` issues.tracker_auto_close=Der Timer wird automatisch gestoppt, wenn dieser Issue geschlossen wird issues.tracking_already_started=`Du hast die Zeiterfassung bereits in diesem Issue gestartet!` issues.stop_tracking=Zeiterfassung stoppen @@ -1614,7 +1616,7 @@ issues.add_time_sum_to_small=Es wurde keine Zeit eingegeben. issues.time_spent_total=Zeitaufwand insgesamt issues.time_spent_from_all_authors=`Aufgewendete Zeit: %s` issues.due_date=Fällig am -issues.invalid_due_date_format=Das Fälligkeitsdatum muss das Format „JJJJ-MM-TT“ haben. +issues.invalid_due_date_format=Das Fälligkeitsdatum muss das Format ‚JJJJ-MM-TT‘ haben. issues.error_modifying_due_date=Fehler beim Ändern des Fälligkeitsdatums. issues.error_removing_due_date=Fehler beim Entfernen des Fälligkeitsdatums. issues.push_commit_1=hat %d Commit %s hinzugefügt @@ -1638,7 +1640,7 @@ issues.dependency.pr_no_dependencies=Keine Abhängigkeiten gesetzt. issues.dependency.no_permission_1=Du bist nicht berechtigt %d Abhängigkeit zu lesen issues.dependency.no_permission_n=Du bist nicht berechtigt %d Abhängigkeiten zu lesen issues.dependency.no_permission.can_remove=Du hast keine Berechtigung diese Abhängigkeit zu lesen, kannst diese Abhängigkeit aber entfernen -issues.dependency.add=Abhängigkeit hinzufügen… +issues.dependency.add=Abhängigkeit hinzufügen … issues.dependency.cancel=Abbrechen issues.dependency.remove=Entfernen issues.dependency.remove_info=Abhängigkeit löschen @@ -1649,7 +1651,7 @@ issues.dependency.issue_closing_blockedby=Das Schließen dieses Issues wird von issues.dependency.issue_close_blocks=Dieses Issue blockiert die Schließung der folgenden Issues issues.dependency.pr_close_blocks=Dieser Pull-Request blockiert die Schließung der folgenden Issues issues.dependency.issue_close_blocked=Du musst alle Issues, die dieses Issue blockieren, schließen, bevor du es schließen kannst. -issues.dependency.issue_batch_close_blocked=Das Schließen der ausgewählten Issues ist nicht möglich, da das Issue #%d noch offene Abhängigkeiten hat +issues.dependency.issue_batch_close_blocked=Das massenweise Schließen der ausgewählten Issues ist nicht möglich, da das Issue #%d noch offene Abhängigkeiten hat issues.dependency.pr_close_blocked=Du musst alle Issues, die diesen Pull-Request blockieren, schließen, bevor du ihn mergen kannst. issues.dependency.blocks_short=Blockiert issues.dependency.blocked_by_short=Abhängig von @@ -1677,7 +1679,7 @@ issues.review.add_review_request=hat ein Review von %s %s angefragt issues.review.remove_review_request=hat die Aufforderung zum Review an %s %s entfernt issues.review.remove_review_request_self=hat das Review verweigert %s 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.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.outdated=Veraltet @@ -1688,8 +1690,8 @@ issues.review.show_outdated=Veraltete anzeigen issues.review.hide_outdated=Veraltete ausblenden issues.review.show_resolved=Gelöste anzeigen issues.review.hide_resolved=Gelöste ausblenden -issues.review.resolve_conversation=Diskussion als "erledigt" markieren -issues.review.un_resolve_conversation=Diskussion als "nicht-erledigt" markieren +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.assignee.error=Aufgrund eines unerwarteten Fehlers konnten nicht alle Beauftragten hinzugefügt werden. issues.reference_issue.body=Beschreibung @@ -1713,7 +1715,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 Ziel- und Quellbranch aus. pulls.has_viewed_file=Gesehen -pulls.has_changed_since_last_review=Inzwischen geändert +pulls.has_changed_since_last_review=Nach deinem letzten Review geändert pulls.viewed_files_label=%[1]d / %[2]d Dateien reviewed pulls.expand_files=Alle Dateien ausklappen pulls.collapse_files=Alle Dateien einklappen @@ -1748,25 +1750,25 @@ pulls.closed=Pull-Request geschlossen pulls.manually_merged=Manuell gemergt pulls.merged_info_text=Der Branch %s kann jetzt gelöscht werden. pulls.is_closed=Der Pull-Request wurde geschlossen. -pulls.title_wip_desc=`Beginne den Titel mit %s um zu verhindern, dass der Pull Request versehentlich gemergt wird.` -pulls.cannot_merge_work_in_progress=Dieser Pull Request ist als Work in Progress markiert. +pulls.title_wip_desc=`Beginne den Titel mit %s, um zu verhindern, dass der Pull-Request versehentlich gemergt wird.` +pulls.cannot_merge_work_in_progress=Dieser Pull Request ist als „Work in Progress“ (in Bearbeitung) markiert. pulls.still_in_progress=Noch in Bearbeitung? pulls.add_prefix=%s Präfix hinzufügen pulls.remove_prefix=%s Präfix entfernen pulls.data_broken=Dieser Pull-Requests ist kaputt, da Fork-Informationen gelöscht wurden. pulls.files_conflicted=Dieser Pull-Request hat Änderungen, die im Widerspruch zum Ziel-Branch stehen. -pulls.is_checking=Die Konfliktprüfung läuft noch. Bitte aktualisiere die Seite in wenigen Augenblicken. +pulls.is_checking=Die Merge-Konfliktprüfung läuft noch. Bitte aktualisiere die Seite in wenigen Augenblicken. pulls.is_ancestor=Dieser Branch ist bereits im Zielbranch enthalten. Es gibt nichts zu mergen. pulls.is_empty=Die Änderungen an diesem Branch sind bereits auf dem Zielbranch. Dies wird ein leerer Commit sein. pulls.required_status_check_failed=Einige erforderliche Prüfungen waren nicht erfolgreich. pulls.required_status_check_missing=Einige erforderliche Prüfungen fehlen. pulls.required_status_check_administrator=Als Administrator kannst du diesen Pull-Request weiterhin mergen. -pulls.blocked_by_approvals=Dieser Pull-Request hat noch nicht genügend Zustimmungen. %d von %d Zustimmungen erteilt. +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_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: +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_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: pulls.can_auto_merge_desc=Dieser Pull-Request kann automatisch gemergt werden. pulls.cannot_auto_merge_desc=Dieser Pull-Request kann nicht automatisch gemergt werden, da es Konflikte gibt. pulls.cannot_auto_merge_helper=Bitte manuell mergen, um die Konflikte zu beheben. @@ -1780,15 +1782,15 @@ pulls.waiting_count_1=%d wartendes Review pulls.waiting_count_n=%d wartende Reviews pulls.wrong_commit_id=die Commit ID muss eine Commit ID auf dem Zielbranch sein -pulls.no_merge_desc=Dieser Pull-Request kann nicht gemerged werden, da keine Mergeoptionen aktiviert sind. +pulls.no_merge_desc=Dieser Pull-Request kann nicht gemergt werden, da alle Repository-Merge-Optionen deaktiviert sind. pulls.no_merge_helper=Aktiviere Mergeoptionen in den Repositoryeinstellungen oder merge den Pull-Request manuell. -pulls.no_merge_wip=Dieser Pull Request kann nicht gemergt werden, da er als Work In Progress gekennzeichnet ist. +pulls.no_merge_wip=Dieser Pull-Request kann nicht gemergt werden, da er als „Work in Progress“ (in Bearbeitung) markiert ist. pulls.no_merge_not_ready=Dieser Pull-Request kann nicht gemergt werden, überprüfe den Reviewstatus und die Statusprüfungen. pulls.no_merge_access=Du bist nicht berechtigt, diesen Pull-Request zu mergen. -pulls.merge_pull_request=Merge Commit erstellen +pulls.merge_pull_request=Merge-Commit erstellen pulls.rebase_merge_pull_request=Rebasen und dann fast-forwarden pulls.rebase_merge_commit_pull_request=Rebasen und dann mergen -pulls.squash_merge_pull_request=Squash Commit erstellen +pulls.squash_merge_pull_request=Squash-Commit erstellen pulls.merge_manually=Manuell mergen pulls.merge_commit_id=Der Mergecommit ID pulls.require_signed_wont_sign=Der Branch erfordert einen signierten Commit, aber dieser Merge wird nicht signiert @@ -1822,7 +1824,7 @@ pulls.close=Pull-Request schließen pulls.closed_at=`hat diesen Pull-Request %[2]s geschlossen` pulls.reopened_at=`hat diesen Pull-Request %[2]s wieder geöffnet` pulls.clear_merge_message=Merge-Nachricht löschen -pulls.clear_merge_message_hint=Das Löschen der Merge-Nachricht wird nur den Inhalt der Commit-Nachricht entfernen und generierte Git-Trailer wie "Co-Authored-By …" erhalten. +pulls.clear_merge_message_hint=Das Löschen der Merge-Nachricht wird nur den Inhalt der Commit-Nachricht entfernen und generierte Git-Trailer wie „Co-Authored-By …“ erhalten. pulls.auto_merge_button_when_succeed=(Wenn die Checks erfolgreich sind) pulls.auto_merge_when_succeed=Automergen, sobald alle Checks erfüllt sind @@ -1830,7 +1832,7 @@ pulls.auto_merge_newly_scheduled=Der Pull-Request wird automatisch gemergt, wenn pulls.auto_merge_has_pending_schedule=%[1]s hat einen Automerge für diesen Pull-Request %[2]s geplant. pulls.auto_merge_cancel_schedule=Automerge abbrechen -pulls.auto_merge_not_scheduled=Dieser Pull Request hat keinen geplanten Automerge. +pulls.auto_merge_not_scheduled=Dieser Pull-Request hat kein geplantes Auto-Merge. pulls.auto_merge_canceled_schedule=Der Automerge dieses Pull-Requests wurde abgebrochen. pulls.auto_merge_newly_scheduled_comment=`hat einen Automerge für diesen Pull-Request %[1]s geplant` @@ -1839,7 +1841,7 @@ pulls.auto_merge_canceled_schedule_comment=`hat den Automerge für diesen Pull-R pulls.delete.title=Diesen Pull-Request löschen? pulls.delete.text=Willst du diesen Pull-Request wirklich löschen? (Dies wird den Inhalt unwiderruflich löschen. Überlege, ob du ihn nicht lieber schließen willst, um ihn zu archivieren) -pulls.recently_pushed_new_branches=Du hast auf den Branch %[1]s %[2]s gepusht +pulls.recently_pushed_new_branches=Du hast auf den Branch %[1]s %[2]s gepusht pull.deleted_branch=(gelöscht):%s @@ -1850,19 +1852,19 @@ milestones.no_due_date=Kein Fälligkeitsdatum milestones.open=Öffnen milestones.close=Schließen milestones.new_subheader=Benutze Meilensteine, um Issues zu organisieren und den Fortschritt darzustellen. -milestones.completeness=%d%% abgeschlossen +milestones.completeness=%d%% abgeschlossen milestones.create=Meilenstein erstellen milestones.title=Titel milestones.desc=Beschreibung milestones.due_date=Fälligkeitsdatum (optional) milestones.clear=Feld leeren milestones.invalid_due_date_format=Das Fälligkeitsdatum muss das Format „JJJJ-MM-TT“ haben. -milestones.create_success=Der Meilenstein "%s" wurde erstellt. +milestones.create_success=Der Meilenstein „%s“ wurde erstellt. milestones.edit=Meilenstein bearbeiten milestones.edit_subheader=Benutze Meilensteine, um Issues zu organisieren und den Fortschritt darzustellen. milestones.cancel=Abbrechen milestones.modify=Meilenstein bearbeiten -milestones.edit_success=Meilenstein "%s" wurde aktualisiert. +milestones.edit_success=Meilenstein „%s“ wurde aktualisiert. milestones.deletion=Meilenstein löschen milestones.deletion_desc=Das Löschen des Meilensteins entfernt ihn von allen Issues. Fortfahren? milestones.deletion_success=Der Meilenstein wurde gelöscht. @@ -1873,7 +1875,7 @@ milestones.filter_sort.most_complete=Vollständigste milestones.filter_sort.most_issues=Meiste Issues milestones.filter_sort.least_issues=Wenigste Issues -signing.will_sign=Dieser Commit wird mit dem Key "%s" signiert werden. +signing.will_sign=Dieser Commit wird mit dem Key „%s“ signiert werden. signing.wont_sign.error=Es gab einen Fehler bei der Prüfung, ob der Commit signiert werden kann. signing.wont_sign.nokey=Es ist kein Schlüssel zum Signieren dieses Commits verfügbar. signing.wont_sign.never=Commits werden nie signiert. @@ -1884,7 +1886,7 @@ signing.wont_sign.parentsigned=Der Commit wird nicht signiert werden, da der vor signing.wont_sign.basesigned=Der Merge Commit wird nicht signiert werden, da der Basis-Commit nicht signiert ist. signing.wont_sign.headsigned=Der Merge Commit wird nicht signiert werden, da der Head-Commit nicht signiert ist. signing.wont_sign.commitssigned=Der Merge Commit wird nicht signiert werden, da alle zugehörigen Commits nicht signiert sind. -signing.wont_sign.approved=Der Merge Commit wird nicht signiert werden, da der Pull Request nicht genehmigt wurde. +signing.wont_sign.approved=Der Merge-Commit wird nicht signiert werden, da der Pull-Request nicht genehmigt wurde. signing.wont_sign.not_signed_in=Du bist nicht eingeloggt. ext_wiki=Zugriff auf externes Wiki @@ -1902,19 +1904,19 @@ wiki.page_title=Seitentitel wiki.page_content=Seiteninhalt wiki.default_commit_message=Beschreibe diese Änderung (optional). wiki.save_page=Seite speichern -wiki.last_commit_info=%s hat diese Seite bearbeitet %s +wiki.last_commit_info=%s hat diese Seite %s bearbeitet wiki.edit_page_button=Bearbeiten wiki.new_page_button=Neue Seite wiki.file_revision=Seitenversion wiki.wiki_page_revisions=Wiki Änderungsverlauf wiki.back_to_wiki=Zurück zur Wiki-Seite wiki.delete_page_button=Seite löschen -wiki.delete_page_notice_1=Das Löschen der Wiki-Seite "%s" kann nicht rückgängig gemacht werden. Fortfahren? +wiki.delete_page_notice_1=Das Löschen der Wiki-Seite „%s“ kann nicht rückgängig gemacht werden. Fortfahren? wiki.page_already_exists=Eine Wiki-Seite mit dem gleichen Namen existiert bereits. -wiki.reserved_page=Der Wiki-Seitenname "%s" ist reserviert. +wiki.reserved_page=Der Wiki-Seitenname „%s“ ist reserviert. wiki.pages=Seiten wiki.last_updated=Zuletzt aktualisiert %s -wiki.page_name_desc=Gib einen Namen für diese Wiki-Seite ein. Spezielle Namen sind: 'Home', '_Sidebar' und '_Footer'. +wiki.page_name_desc=Gib einen Namen für diese Wiki-Seite ein. Einige spezielle Namen lauten: „Home“, „_Sidebar“ und „_Footer“. wiki.original_git_entry_tooltip=Originale Git-Datei anstatt eines benutzerfreundlichen Links anzeigen. activity=Aktivität @@ -1991,11 +1993,11 @@ search.fuzzy.tooltip=Zeige auch Ergebnisse, die dem Suchbegriff ähneln search.match=Genau search.match.tooltip=Zeige nur Ergebnisse, die exakt mit dem Suchbegriff übereinstimmen search.results=Suchergebnisse für „%s“ in %s -search.code_no_results=Es konnte kein passender Code für deinen Suchbegriff gefunden werden. +search.code_no_results=Es konnte kein passender Quellcode für deinen Suchbegriff gefunden werden. search.code_search_unavailable=Derzeit ist die Code-Suche nicht verfügbar. Bitte wende dich an den Website-Administrator. settings=Einstellungen -settings.desc=In den Einstellungen kannst du die Einstellungen des Repositories anpassen +settings.desc=In den Einstellungen kannst du die Einstellungen des Repositorys anpassen settings.options=Repository settings.collaboration=Mitarbeiter settings.collaboration.admin=Administrator @@ -2006,35 +2008,35 @@ settings.collaboration.undefined=Nicht definiert settings.hooks=Webhooks settings.githooks=Git-Hooks settings.basic_settings=Grundeinstellungen -settings.mirror_settings=Mirror-Einstellungen -settings.mirror_settings.docs=Richte Dein Repository so ein, dass es automatisch Commits, Tags und Branches mit einem anderen Repository synchronisieren kann. -settings.mirror_settings.docs.disabled_pull_mirror.instructions=Richte Dein Projekt so ein, dass es automatisch Commits, Tags und Branches in ein anderes Repository pusht. Pull-Mirrors wurden von Deinem Website-Administrator deaktiviert. -settings.mirror_settings.docs.disabled_push_mirror.instructions=Richte Dein Repository so ein, dass es automatisch Commits, Tags und Branches aus einem anderen Repository pullen kann. -settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning=Im Moment ist dies nur im Menü "Neue Migration" möglich. Für weitere Informationen konsultiere bitte: -settings.mirror_settings.docs.disabled_push_mirror.info=Push-Mirrors wurden von Ihrem Administrator deaktiviert. -settings.mirror_settings.docs.no_new_mirrors=Dein Repository spiegelt Änderungen von oder zu einem anderen Repository. Bitte beachte, dass du gerade keine neuen Mirror anlegen kannst. -settings.mirror_settings.docs.can_still_use=Obwohl du existierende Mirrors gerade nicht bearbeiten oder neu anlegen kannst, sind bestehende Mirrors weiterhin nutzbar. -settings.mirror_settings.docs.pull_mirror_instructions=Um einen Pull-Mirror einzurichten, konsultiere bitte: -settings.mirror_settings.docs.more_information_if_disabled=Hier kannst du mehr über Push- und Pull-Mirrors erfahren: -settings.mirror_settings.docs.doc_link_title=Wie spiegele ich Repositories? -settings.mirror_settings.docs.doc_link_pull_section=den Abschnitt "Von einem entfernten Repository pullen" in der Dokumentation. +settings.mirror_settings=Spiegeleinstellungen +settings.mirror_settings.docs=Richte dein Repository so ein, dass es automatisch Commits, Tags und Branches mit einem anderen Repository synchronisieren kann. +settings.mirror_settings.docs.disabled_pull_mirror.instructions=Richte dein Projekt so ein, dass es automatisch Commits, Tags und Branches in ein anderes Repository pusht. Pull-Spiegel wurden von deinem Website-Administrator deaktiviert. +settings.mirror_settings.docs.disabled_push_mirror.instructions=Richte dein Repository so ein, dass es automatisch Commits, Tags und Branches aus einem anderen Repository pullen kann. +settings.mirror_settings.docs.disabled_push_mirror.pull_mirror_warning=Im Moment ist dies nur im Menü „Neue Migration“ möglich. Für weitere Informationen konsultiere bitte: +settings.mirror_settings.docs.disabled_push_mirror.info=Push-Spiegel wurden von deinem Administrator deaktiviert. +settings.mirror_settings.docs.no_new_mirrors=Dein Repository spiegelt Änderungen von oder zu einem anderen Repository. Bitte beachte, dass du gerade keine neuen Spiegel anlegen kannst. +settings.mirror_settings.docs.can_still_use=Obwohl du existierende Spiegel gerade nicht bearbeiten oder neu anlegen kannst, sind bestehende Spiegel weiterhin nutzbar. +settings.mirror_settings.docs.pull_mirror_instructions=Um einen Pull-Spiegel einzurichten, konsultiere bitte: +settings.mirror_settings.docs.more_information_if_disabled=Hier kannst du mehr über Push- und Pull-Spiegel erfahren: +settings.mirror_settings.docs.doc_link_title=Wie spiegele ich Repositorys? +settings.mirror_settings.docs.doc_link_pull_section=den Abschnitt „Aus einem Remote-Repository pullen“ in der Dokumentation. settings.mirror_settings.docs.pulling_remote_title=Aus einem Remote-Repository pullen settings.mirror_settings.mirrored_repository=Gespiegeltes Repository settings.mirror_settings.direction=Richtung settings.mirror_settings.direction.pull=Pull settings.mirror_settings.direction.push=Push settings.mirror_settings.last_update=Letzte Aktualisierung -settings.mirror_settings.push_mirror.none=Keine Push-Mirrors konfiguriert +settings.mirror_settings.push_mirror.none=Keine Push-Spiegel konfiguriert settings.mirror_settings.push_mirror.remote_url=URL zum Git-Remote-Repository -settings.mirror_settings.push_mirror.add=Push-Mirror hinzufügen -settings.mirror_settings.push_mirror.edit_sync_time=Mirror-Sync-Intervall anpassen +settings.mirror_settings.push_mirror.add=Push-Spiegel hinzufügen +settings.mirror_settings.push_mirror.edit_sync_time=Spiegel-Sync-Intervall anpassen settings.sync_mirror=Jetzt synchronisieren settings.pull_mirror_sync_in_progress=Aktuell werden Änderungen von %s gepullt. settings.push_mirror_sync_in_progress=Aktuell werden Änderungen auf %s gepusht. settings.site=Webseite settings.update_settings=Einstellungen speichern -settings.update_mirror_settings=Mirror-Einstellungen aktualisieren +settings.update_mirror_settings=Spiegeleinstellungen aktualisieren settings.branches.switch_default_branch=Standardbranch wechseln settings.branches.update_default_branch=Standardbranch aktualisieren settings.branches.add_new_rule=Neue Regel hinzufügen @@ -2059,13 +2061,13 @@ 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_url_format_desc=Du kannst die Platzhalter {user}, {repo}, {index} für den Benutzernamen, den Namen des Repositories und die Issue-Nummer verwenden. +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 settings.pulls_desc=Repository-Pull-Requests aktivieren settings.pulls.ignore_whitespace=Bei Konflikten Leerzeichen ignorieren settings.pulls.enable_autodetect_manual_merge=Autoerkennung von manuellen Merges aktivieren (in Ausnahmefällen können Fehleinschätzungen auftreten) -settings.pulls.allow_rebase_update=Update von Pull Request Branches per Rebase erlauben +settings.pulls.allow_rebase_update=Update von Pull-Request-Branches per Rebase erlauben settings.pulls.default_delete_branch_after_merge=Standardmäßig bei Pull-Requests den Branch nach dem Mergen löschen settings.pulls.default_allow_edits_from_maintainers=Änderungen von Maintainern standardmäßig erlauben settings.releases_desc=Repository-Releases aktivieren @@ -2082,12 +2084,12 @@ settings.reindex_button=Zur Warteschlange für erneutes Indexieren hinzufügen settings.reindex_requested=Erneutes Indexieren angefordert settings.admin_enable_close_issues_via_commit_in_any_branch=Einen Issue mit einem Commit auf einem nicht-Standard-Branch schließen settings.danger_zone=Gefahrenzone -settings.new_owner_has_same_repo=Der neue Eigentümer hat bereits ein Repository mit dem gleichen Namen. Bitte wähle einen anderen Namen. +settings.new_owner_has_same_repo=Der neue Besitzer hat bereits ein Repository mit dem gleichen Namen. Bitte wähle einen anderen Namen. settings.convert=In ein normales Repository umwandeln -settings.convert_desc=Dieser Mirror kann in ein normales Repository umgewandelt werden. Dies kann nicht rückgängig gemacht werden. -settings.convert_notices_1=Dieser Vorgang wandelt das Mirror-Repository in ein normales Repository um. Dies kann nicht rückgängig gemacht werden. +settings.convert_desc=Dieser Spiegel kann in ein normales Repository umgewandelt werden. Dies kann nicht rückgängig gemacht werden. +settings.convert_notices_1=Dieser Vorgang wandelt das Spiegel-Repository in ein normales Repository um. Dies kann nicht rückgängig gemacht werden. settings.convert_confirm=Repository umwandeln -settings.convert_succeed=Das Mirror-Repository wurde erfolgreich in ein normales Repository umgewandelt. +settings.convert_succeed=Das Spiegel-Repository wurde erfolgreich in ein normales Repository umgewandelt. settings.convert_fork=In ein normales Repository umwandeln settings.convert_fork_desc=Du kannst diesen Fork in ein normales Repository umwandeln. Dies kann nicht rückgängig gemacht werden. settings.convert_fork_notices_1=Dieser Vorgang konvertiert den Fork in ein normales Repository und kann nicht rückgängig gemacht werden. @@ -2107,7 +2109,7 @@ settings.transfer_notices_2=– Du wirst weiterhin Zugriff haben, wenn der neue settings.transfer_notices_3=- Wenn das Repository privat ist und an einen einzelnen Benutzer übertragen wird, wird sichergestellt, dass der Benutzer mindestens Leserechte hat (und die Berechtigungen werden gegebenenfalls ändert). settings.transfer_owner=Neuer Besitzer settings.transfer_perform=Übertragung durchführen -settings.transfer_started=`Für dieses Repository wurde eine Übertragung eingeleitet und wartet nun auf die Bestätigung von "%s"` +settings.transfer_started=Für dieses Repository wurde eine Übertragung eingeleitet und wartet nun auf die Bestätigung von „%s“ settings.transfer_succeed=Das Repository wurde transferiert. settings.signing_settings=Signaturüberprüfungseinstellungen settings.trust_model=Signaturvertrauensmodell @@ -2115,13 +2117,13 @@ settings.trust_model.default=Standardvertrauensmodell settings.trust_model.default.desc=Verwende das Standardvertrauensmodell für diese Installation. 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, unabhängig ob die Signatur mit dem Committer übereinstimmt oder nicht. +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.desc=Gültige Signaturen von Mitwirkenden werden 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 auf einen User in der Datenbank zeigen. +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 als "nicht übereinstimmend". Dies zwingt Forgejo als Committer bei signierten Commits mit dem tatsächlichen Committer als Co-Authored-By: und Co-Committed-By: Trailer im Commit. Der Standard-Forgejo-Schlüssel muss mit einem Benutzer in der Datenbank ü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 als „nicht übereinstimmend“ margiert. 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. @@ -2131,7 +2133,7 @@ settings.delete=Dieses Repository löschen settings.delete_desc=Wenn dieses Repository gelöscht wurde, gibt es keinen Weg zurück. Bitte sei vorsichtig. settings.delete_notices_1=– Diese Operation KANN NICHT rückgängig gemacht werden. settings.delete_notices_2=– Die Operation wird das %s-Repository dauerhaft löschen, inklusive der Dateien, Issues, Kommentare und Zugriffseinstellungen. -settings.delete_notices_fork_1=- Forks dieses Repositories werden nach dem Löschen unabhängig. +settings.delete_notices_fork_1=– Forks dieses Repositorys werden nach dem Löschen unabhängig. settings.deletion_success=Das Repository wurde gelöscht. settings.update_settings_success=Repository-Einstellungen wurden aktualisiert. settings.update_settings_no_unit=Das Repository sollte mindestens eine Art der Interaktion erlauben. @@ -2145,7 +2147,7 @@ settings.delete_collaborator=Entfernen settings.collaborator_deletion=Mitarbeiter entfernen settings.collaborator_deletion_desc=Nach dem Löschen wird dieser Mitarbeiter keinen Zugriff mehr auf dieses Repository haben. Fortfahren? settings.remove_collaborator_success=Der Mitarbeiter wurde entfernt. -settings.search_user_placeholder=Benutzer suchen… +settings.search_user_placeholder=Benutzer suchen … settings.org_not_allowed_to_be_collaborator=Organisationen können nicht als Mitarbeiter hinzugefügt werden. settings.change_team_access_not_allowed=Nur der Besitzer der Organisation kann die Zugangsrechte des Teams ändern settings.team_not_in_organization=Das Team ist nicht in der gleichen Organisation wie das Repository @@ -2153,9 +2155,9 @@ settings.teams=Teams settings.add_team=Team hinzufügen settings.add_team_duplicate=Das Team ist dem Repository schon zugeordnet settings.add_team_success=Das Team hat nun Zugriff auf das Repository. -settings.search_team=Team suchen… +settings.search_team=Team suchen … settings.change_team_permission_tip=Die Team-Berechtigung ist auf der Team-Einstellungsseite festgelegt und kann nicht für ein Repository geändert werden -settings.delete_team_tip=Dieses Team hat Zugriff auf alle Repositories und kann nicht entfernt werden +settings.delete_team_tip=Dieses Team hat Zugriff auf alle Repositorys und kann nicht entfernt werden settings.remove_team_success=Der Zugriff des Teams auf das Repository wurde zurückgezogen. settings.add_webhook=Webhook hinzufügen settings.add_webhook.invalid_channel_name=Der Name des Webhook-Kanals darf nicht leer sein und darf nicht nur das Zeichen # enthalten. @@ -2192,7 +2194,7 @@ settings.discord_icon_url=Icon-URL settings.event_desc=Auslösen bei: settings.event_push_only=Push-Events settings.event_send_everything=Alle Events -settings.event_choose=Benutzerdefinierte Events… +settings.event_choose=Benutzerdefinierte Events … settings.event_header_repository=Repository-Ereignisse settings.event_create=Erstellen settings.event_create_desc=Branch oder Tag erstellt. @@ -2208,7 +2210,7 @@ settings.event_push=Push settings.event_push_desc=Git push in ein Repository. settings.event_repository=Repository settings.event_repository_desc=Repository erstellt oder gelöscht. -settings.event_header_issue=Issue Ereignisse +settings.event_header_issue=Issue-Ereignisse settings.event_issues=Issues settings.event_issues_desc=Issue geöffnet, geschlossen, wieder geöffnet oder bearbeitet. settings.event_issue_assign=Issue zugewiesen @@ -2241,7 +2243,7 @@ settings.event_pull_request_merge=Pull-Request-Merge settings.event_package=Paket settings.event_package_desc=Paket wurde in einem Repository erstellt oder gelöscht. settings.branch_filter=Branch-Filter -settings.branch_filter_desc=Whitelist für Branches für Push-, Erzeugungs- und Löschevents, als glob Pattern beschrieben. Es werden Events für alle Branches gemeldet, falls das Pattern * ist, oder falls es leer ist. Siehe die github.com/gobwas/glob Dokumentation für die Syntax (Englisch). Beispiele: master, {master,release*}. +settings.branch_filter_desc=Whitelist für Branches für Push-, Erzeugungs- und Löschevents, als glob-Pattern beschrieben. Es werden Events für alle Branches gemeldet, falls das Pattern * ist, oder falls es leer ist. Siehe die github.com/gobwas/glob-Dokumentation für die Syntax (Englisch). Beispiele: master, {master,release*}. settings.authorization_header=Authorization-Header settings.authorization_header_desc=Wird, falls vorhanden, als Authorization-Header mitgesendet. Beispiele: %s. settings.active=Aktiv @@ -2273,7 +2275,7 @@ settings.web_hook_name_packagist=Packagist settings.packagist_username=Benutzername für Packagist settings.packagist_api_token=API-Token settings.packagist_package_url=Paket-URL -settings.deploy_keys=Deploy-Schlüssel +settings.deploy_keys=Deploy-Keys settings.add_deploy_key=Deploy-Schlüssel hinzufügen settings.deploy_key_desc=Deploy-Keys haben nur Lesezugriff auf das Repository. settings.is_writable=Erlaube Schreibzugriff @@ -2334,9 +2336,9 @@ settings.require_signed_commits=Signierte Commits erforderlich settings.require_signed_commits_desc=Pushes auf diesen Branch ablehnen, wenn Commits nicht signiert oder nicht überprüfbar sind. settings.protect_branch_name_pattern=Muster für geschützte Branchnamen settings.protect_patterns=Muster -settings.protect_protected_file_patterns=Geschützte Dateimuster (durch Semikolon ';' getrennt): +settings.protect_protected_file_patterns=Geschützte Dateimuster (durch Semikolon „;“ getrennt): settings.protect_protected_file_patterns_desc=Geschützte Dateien dürfen nicht direkt geändert werden, auch wenn der Benutzer Rechte hat, Dateien in diesem Branch hinzuzufügen, zu bearbeiten oder zu löschen. Mehrere Muster können mit Semikolon (';') getrennt werden. Siehe github.com/gobwas/glob Dokumentation zur Mustersyntax. Beispiele: .drone.yml, /docs/**/*.txt. -settings.protect_unprotected_file_patterns=Ungeschützte Dateimuster (durch Semikolon ';' getrennt): +settings.protect_unprotected_file_patterns=Ungeschützte Dateimuster (durch Semikolon „;“ getrennt): settings.protect_unprotected_file_patterns_desc=Ungeschützte Dateien, die direkt geändert werden dürfen, wenn der Benutzer Schreibzugriff hat, können die Push-Beschränkung umgehen. Mehrere Muster können mit Semikolon (';') getrennt werden. Siehe github.com/gobwas/glob Dokumentation zur Mustersyntax. Beispiele: .drone.yml, /docs/**/*.txt. settings.add_protected_branch=Schutz aktivieren settings.delete_protected_branch=Schutz deaktivieren @@ -2620,7 +2622,7 @@ pulls.cmd_instruction_hint = `Anweisungen für die K pulls.cmd_instruction_checkout_title = Auschecken 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.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. pulls.commit_ref_at = `hat sich auf diesen Pull-Request von einem Commit %[2]s bezogen` @@ -2639,6 +2641,9 @@ contributors.contribution_type.commits = Commits contributors.contribution_type.deletions = Löschungen contributors.contribution_type.additions = Einfügungen contributors.contribution_type.filter_label = Art des Beitrags: +vendored = Vendored +activity.navbar.pulse = Puls +pulls.made_using_agit = AGit [org] org_name_holder=Name der Organisation @@ -3104,7 +3109,7 @@ config.app_name=Seitentitel config.app_ver=Forgejo-Version config.app_url=Forgejo-Basis-URL config.custom_conf=Konfigurations-Datei-Pfad -config.custom_file_root_path=Benutzerdefinierter Root Pfad +config.custom_file_root_path=Benutzerdefinierter Root-Pfad config.domain=Server-Domain config.offline_mode=Lokaler Modus config.disable_router_log=Router-Log deaktivieren @@ -3518,7 +3523,7 @@ owner.settings.cargo.rebuild.success=Der Cargo-Index wurde erfolgreich neu erste owner.settings.cleanuprules.title=Bereinigungsregeln verwalten owner.settings.cleanuprules.add=Bereinigungsregel hinzufügen owner.settings.cleanuprules.edit=Bereinigungsregel bearbeiten -owner.settings.cleanuprules.none=Keine Bereinigungs-Regeln verfügbar. Bitte konsultiere die Dokumentation. +owner.settings.cleanuprules.none=Es bestehen derzeit keine Bereinigungsregeln. owner.settings.cleanuprules.preview=Vorschau der Bereinigungsregel owner.settings.cleanuprules.preview.overview=%d Pakete sollen entfernt werden. owner.settings.cleanuprules.preview.none=Bereinigungsregel stimmt mit keinem Paket überein. diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index 7c41c3e4e4..90e1a062a8 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -88,8 +88,8 @@ rerun_all=Relancer toutes les tâches save=Enregistrer add=Ajouter add_all=Tout Ajouter -remove=Retirer -remove_all=Tout Retirer +remove=Supprimer +remove_all=Tout Supprimer remove_label_str=Supprimer l’élément « %s » edit=Éditer view=Voir @@ -429,6 +429,7 @@ change_unconfirmed_email_error = Le courriel %v n'a pu être modifié change_unconfirmed_email = Si vous avez donné un courriel incorrect à l'inscription, vous pouvez le changer ci-dessous. La confirmation sera envoyée à cette nouvelle adresse. change_unconfirmed_email_summary = Modifier l'adresse à laquelle le courriel d'activation est envoyé. last_admin = Vous ne pouvez pas supprimer le dernier compte administrateur. Il doit exister au moins un compte administrateur. +remember_me.compromised = Le jeton de login n'est plus valide ce qui pourrait indiquer une compromission de compte. Veuillez vérifier d'éventuelles activités inhabituelles. [mail] view_it_on=Voir sur %s @@ -752,7 +753,7 @@ manage_ssh_keys=Gérer les clés SSH manage_ssh_principals=Gérer les certificats principaux SSH manage_gpg_keys=Gérer les clés GPG add_key=Ajouter une clé -ssh_desc=Ces clefs SSH publiques sont associées à votre compte. Les clefs privées correspondantes permettent l'accès complet à vos repos. +ssh_desc=Ces clefs SSH publiques sont associées à votre compte. Les clefs privées correspondantes permettent l'accès complet à vos repos. Les clés SSH qui ont été vérifiées peuvent aussi être utilisées pour vérifier des commits Git signés par SSH. principal_desc=Ces Principaux de certificats SSH sont associés à votre compte et permettent un accès complet à vos dépôts. gpg_desc=Ces clés GPG sont associées à votre compte. Conservez-les en lieu sûr, car elles permettent de vérifier vos révisions. ssh_helper=Besoin d'aide ? Consultez le guide de GitHub pour créer vos propres clés SSH ou résoudre les problèmes courants que vous pourriez rencontrer en utilisant SSH. @@ -899,7 +900,7 @@ scan_this_image=Scannez cette image avec votre application d'authentification : or_enter_secret=Ou saisissez le code %s then_enter_passcode=Et entrez le code de passe s'affichant dans l'application : passcode_invalid=Le mot de passe est invalide. Réessayez. -twofa_enrolled=L'authentification à deux facteurs a été activée pour votre compte. Gardez votre jeton de secours (%s) en lieu sûr, car il ne vous sera montré qu'une seule fois ! +twofa_enrolled=L'authentification à deux facteurs a été activée pour votre compte. Gardez votre jeton de secours (%s) en lieu sûr, car il ne vous sera montré qu'une seule fois. twofa_failed_get_secret=Impossible d'obtenir le secret. webauthn_desc=Les clefs de sécurité sont des dispositifs matériels contenant des clefs cryptographiques. Elles peuvent être utilisées pour l'authentification à deux facteurs. La clef de sécurité doit supporter le standard WebAuthn Authenticator. @@ -950,14 +951,14 @@ user_unblock_success = Cet utilisateur a été débloqué avec succès. user_block_success = Cet utilisateur a été bloqué avec succès. [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. +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 owner_helper=Certaines organisations peuvent ne pas apparaître dans la liste déroulante en raison d'une limite maximale du nombre de dépôts. repo_name=Nom du dépôt repo_name_helper=Idéalement, le nom d'un dépôt devrait être court, mémorisable et unique. repo_size=Taille du dépôt template=Modèle -template_select=Répliquer un modèle +template_select=Sélectionner un modèle template_helper=Faire de ce dépôt un modèle template_description=Les référentiels de modèles permettent aux utilisateurs de générer de nouveaux référentiels avec la même structure de répertoire, fichiers et paramètres optionnels. visibility=Visibilité @@ -984,13 +985,13 @@ generate_from=Générer depuis repo_desc=Description repo_desc_helper=Décrire brièvement votre dépôt repo_lang=Langue -repo_gitignore_helper=Sélectionner quelques .gitignore prédéfinies +repo_gitignore_helper=Sélectionner quelques .gitignore prédéfinies. repo_gitignore_helper_desc=De nombreux outils et compilateurs génèrent des fichiers résiduels qui n'ont pas besoin d'être supervisés par git. Composez un .gitignore à l’aide de cette liste des languages de programmation courants. issue_labels=Jeu de labels pour les tickets issue_labels_helper=Sélectionner un jeu de label. license=Licence -license_helper=Sélectionner une licence -license_helper_desc=Une licence réglemente ce que les autres peuvent ou ne peuvent pas faire avec votre code. Vous ne savez pas laquelle est la bonne pour votre projet ? Comment choisir une licence. +license_helper=Sélectionner une licence. +license_helper_desc=Une licence réglemente ce que les autres peuvent ou ne peuvent pas faire avec votre code. Vous ne savez pas laquelle est la bonne pour votre projet ? Comment choisir une licence. readme=LISEZMOI readme_helper=Choisissez un modèle de fichier LISEZMOI. readme_helper_desc=Le README est l'endroit idéal pour décrire votre projet et accueillir des contributeurs. @@ -2594,7 +2595,7 @@ admin.failed_to_replace_flags = Échec de remplacement des drapeaux du dépôt admin.flags_replaced = Drapeaux du dépôt remplacés rss.must_be_on_branch = Vous devez vous trouver sur une branche pour obtenir un flux RSS. admin.manage_flags = Gérer les drapeaux -admin.enabled_flags = Drapeaux actifs pour le dépôt: +admin.enabled_flags = Drapeaux actifs pour le dépôt : clone_in_vscodium = Clone dans VSCodium object_format_helper = Format des objets d'un dépôt. Ne peut pas être changé. SHA1 est le plus compatible. mirror_sync = synchronisé @@ -2634,6 +2635,12 @@ pulls.fast_forward_only_merge_pull_request = Fast-forward uniquement pulls.reopen_failed.base_branch = La pull request ne peut pas être re-ouverte car la branche de destination n'existe plus. settings.units.overview = Vue générale settings.units.add_more = Ajouter en plus... +activity.navbar.pulse = Pouls +activity.navbar.contributors = Contributeurs +contributors.contribution_type.commits = Commits +contributors.contribution_type.additions = Ajouts +contributors.contribution_type.filter_label = Type de contributeur : +contributors.contribution_type.deletions = Suppressions [org] org_name_holder=Nom de l'organisation @@ -3658,3 +3665,11 @@ executable_file=Fichier exécutable symbolic_link=Lien symbolique submodule=Sous-module + + +[graphs] +component_loading_info = Cela peut prendre du temps… +component_failed_to_load = Une erreur inattendue s'est produite. +contributors.what = contributions +component_loading = Chargement %s... +component_loading_failed = Échec de chargement de %s \ No newline at end of file diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 4d7a5b3024..27f1273fe8 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -141,6 +141,7 @@ copy_type_unsupported = Dit bestandstype kan niet worden gekopieerd pin = Vastpinnen unpin = Ontpinnen remove_label_str = Verwijder punt "%s" +confirm_delete_artifact = Weet je zeker dat je het artefact '%s' wilt verwijderen? [aria] navbar = Navigatiebalk @@ -589,16 +590,16 @@ openid_been_used = De OpenID-adres "%s" is al in gebruik. username_has_not_been_changed = Gebruikersnaam is niet veranderd duplicate_invite_to_team = De gebruiker heeft al een uitnodiging ontvangen om deel te nemen aan deze team. organization_leave_success = U heeft de organisatie %s succesvol verlaten. -still_own_packages = "Uw account is eigenaar van één of meer pakketten, verwijder deze eerst." -still_has_org = "Uw account is eigenaar van één of meer organisaties, verlaat deze eerst." +still_own_packages = Uw account is eigenaar van één of meer pakketten, verwijder deze eerst. +still_has_org = Uw account is eigenaar van één of meer organisaties, verlaat deze eerst. must_use_public_key = De sleutel die u heeft aangeboden is een privésleutel. Alstublieft, upload nergens uw privésleutel. Gebruik in plaats daarvan uw publieke sleutel. -unable_verify_ssh_key = "Kan de SSH-sleutel niet verifiëren, controleer deze voor fouten." -still_own_repo = "Uw account is eigenaar van één of meer repositories, verwijder of draag deze eerst over." +unable_verify_ssh_key = Kan de SSH-sleutel niet verifiëren, controleer deze voor fouten. +still_own_repo = Uw account is eigenaar van één of meer repositories, verwijder of draag deze eerst over. admin_cannot_delete_self = U kan uzelf niet verwijderen als u een beheerder bent. Verwijder eerst uw beheerdersrechten. username_error_no_dots = ` kan alleen alfanumerieke karakters ('0-9','a-z','A-Z'), streepje ('-') en liggend streepje ('_') bevatten. Niet-alfanumerieke karakters aan het begin of eind zijn verboden en aaneenvolgende niet alfanumerieke karakters zijn ook verboden.` invalid_group_team_map_error = ` mapping is ongeldig: %s" -org_still_own_repo = "Deze organisatie is eigenaar van één of meer repositories, verwijder of draag deze eerst over." -org_still_own_packages = "Deze organisatie is eigenaar van één of meer pakketten, verwijder deze eerst." +org_still_own_repo = Deze organisatie is eigenaar van één of meer repositories, verwijder of draag deze eerst over. +org_still_own_packages = Deze organisatie is eigenaar van één of meer pakketten, verwijder deze eerst. [user] change_avatar=Wijzig je profielfoto… @@ -738,7 +739,7 @@ manage_gpg_keys=Beheer GPG sleutels add_key=Sleutel toevoegen ssh_desc=Deze publieke SSH sleutels worden geassocieerd met uw account. De bijbehorende private sleutels geven volledige toegang toe tot je repositories. SSH sleutels die geverifieerd zijn kunnen gebruikt worden om SSH-ondertekende Git commits te verifiëren. principal_desc=Deze SSH-certificaatverantwoordelijken zijn gekoppeld aan uw account en geven volledige toegang tot uw repositories. -gpg_desc=Deze publieke GPG-sleutels zijn verbonden met je account. Houd je privé-sleutels veilig, omdat hiermee commits kunnen worden ondertekend. +gpg_desc=Deze publieke GPG-sleutels zijn gekoppeld aan je account en worden gebruikt om je commits te verifiëren. Bewaar je privésleutels veilig, omdat ze het mogelijk maken om commits met jouw identiteit te ondertekenen. ssh_helper=Weet u niet hoe? Lees dan onze handleiding voor het genereren van SSH sleutels of voor algemene SSH problemen. gpg_helper=Hulp nodig? Neem een kijkje op de GitHub handleiding over GPG. add_new_key=SSH sleutel toevoegen @@ -901,7 +902,7 @@ location_placeholder = Deel uw grove locatie met anderen hidden_comment_types_description = Reactie-types hieronder aangevinkt zullen niet worden weergegeven in de issue-pagina's. Bijvoorbeeld het aanvinken van "Label" zal alle reacties in de vorm van " voegt toe/verwijderd - {{ctx.Locale.Tr "repo.issues.filter_label_exclude" | Safe}} + {{ctx.Locale.Tr "repo.issues.filter_label_exclude"}}
    {{ctx.Locale.Tr "repo.issues.filter_label_no_select"}} {{ctx.Locale.Tr "repo.issues.filter_label_select_no_label"}} diff --git a/templates/repo/issue/labels/edit_delete_label.tmpl b/templates/repo/issue/labels/edit_delete_label.tmpl index f41b4ee2c6..7ddc38a387 100644 --- a/templates/repo/issue/labels/edit_delete_label.tmpl +++ b/templates/repo/issue/labels/edit_delete_label.tmpl @@ -29,9 +29,9 @@
    - {{ctx.Locale.Tr "repo.issues.label_exclusive_desc" | Safe}} + {{ctx.Locale.Tr "repo.issues.label_exclusive_desc"}}
    - {{svg "octicon-alert"}} {{ctx.Locale.Tr "repo.issues.label_exclusive_warning" | Safe}} + {{svg "octicon-alert"}} {{ctx.Locale.Tr "repo.issues.label_exclusive_warning"}}

    diff --git a/templates/repo/issue/labels/label_new.tmpl b/templates/repo/issue/labels/label_new.tmpl index e7fb1e5ff6..2b2b2336c4 100644 --- a/templates/repo/issue/labels/label_new.tmpl +++ b/templates/repo/issue/labels/label_new.tmpl @@ -17,7 +17,7 @@
    - {{ctx.Locale.Tr "repo.issues.label_exclusive_desc" | Safe}} + {{ctx.Locale.Tr "repo.issues.label_exclusive_desc"}}
    diff --git a/templates/repo/issue/milestone_issues.tmpl b/templates/repo/issue/milestone_issues.tmpl index ea19518efa..d9495d9b77 100644 --- a/templates/repo/issue/milestone_issues.tmpl +++ b/templates/repo/issue/milestone_issues.tmpl @@ -31,7 +31,7 @@
    {{$closedDate:= TimeSinceUnix .Milestone.ClosedDateUnix ctx.Locale}} {{if .IsClosed}} - {{svg "octicon-clock"}} {{ctx.Locale.Tr "repo.milestones.closed" $closedDate | Safe}} + {{svg "octicon-clock"}} {{ctx.Locale.Tr "repo.milestones.closed" $closedDate}} {{else}} {{if .Milestone.DeadlineString}} @@ -45,7 +45,7 @@ {{end}} {{end}}
    -
    {{ctx.Locale.Tr "repo.milestones.completeness" .Milestone.Completeness | Safe}}
    +
    {{ctx.Locale.Tr "repo.milestones.completeness" .Milestone.Completeness}}
    {{if .TotalTrackedTime}}
    {{svg "octicon-clock"}} diff --git a/templates/repo/issue/milestones.tmpl b/templates/repo/issue/milestones.tmpl index 3d4bbfd8b1..698e3fffba 100644 --- a/templates/repo/issue/milestones.tmpl +++ b/templates/repo/issue/milestones.tmpl @@ -47,14 +47,14 @@ {{if .UpdatedUnix}}
    {{svg "octicon-clock"}} - {{ctx.Locale.Tr "repo.milestones.update_ago" (TimeSinceUnix .UpdatedUnix ctx.Locale) | Safe}} + {{ctx.Locale.Tr "repo.milestones.update_ago" (TimeSinceUnix .UpdatedUnix ctx.Locale)}}
    {{end}}
    {{if .IsClosed}} {{$closedDate:= TimeSinceUnix .ClosedDateUnix ctx.Locale}} {{svg "octicon-clock" 14}} - {{ctx.Locale.Tr "repo.milestones.closed" $closedDate | Safe}} + {{ctx.Locale.Tr "repo.milestones.closed" $closedDate}} {{else}} {{if .DeadlineString}} diff --git a/templates/repo/issue/new_form.tmpl b/templates/repo/issue/new_form.tmpl index 04ae8456bb..d1cbba6873 100644 --- a/templates/repo/issue/new_form.tmpl +++ b/templates/repo/issue/new_form.tmpl @@ -13,7 +13,7 @@
    {{if .PageIsComparePull}} -
    {{ctx.Locale.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0| Escape) | Safe}}
    +
    {{ctx.Locale.Tr "repo.pulls.title_wip_desc" (index .PullRequestWorkInProgressPrefixes 0| Escape)}}
    {{end}}
    {{if .Fields}} diff --git a/templates/repo/issue/view_content.tmpl b/templates/repo/issue/view_content.tmpl index ed444f6dce..793772ecd0 100644 --- a/templates/repo/issue/view_content.tmpl +++ b/templates/repo/issue/view_content.tmpl @@ -28,10 +28,10 @@ {{.Issue.OriginalAuthor}}
    - {{ctx.Locale.Tr "repo.issues.commented_at" (.Issue.HashTag|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.commented_at" (.Issue.HashTag|Escape) $createdStr}} - {{if .Repository.OriginalURL}} ({{ctx.Locale.Tr "repo.migrated_from" (.Repository.OriginalURL|Escape) (.Repository.GetOriginalURLHostname|Escape) | Safe}}){{end}} + {{if .Repository.OriginalURL}} ({{ctx.Locale.Tr "repo.migrated_from" (.Repository.OriginalURL|Escape) (.Repository.GetOriginalURLHostname|Escape)}}){{end}} {{else}} @@ -39,7 +39,7 @@ {{template "shared/user/authorlink" .Issue.Poster}} - {{ctx.Locale.Tr "repo.issues.commented_at" (.Issue.HashTag|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.commented_at" (.Issue.HashTag|Escape) $createdStr}} {{end}}
    @@ -133,7 +133,7 @@
    {{else}}
    - {{ctx.Locale.Tr "repo.issues.sign_in_require_desc" (.SignInLink|Escape) | Safe}} + {{ctx.Locale.Tr "repo.issues.sign_in_require_desc" (.SignInLink|Escape)}}
    {{end}} {{end}}{{/* end if: .IsSigned */}} diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index 43e059d6c1..cf9df4dbda 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -33,10 +33,10 @@ {{.OriginalAuthor}} - {{ctx.Locale.Tr "repo.issues.commented_at" (.HashTag|Escape) $createdStr | Safe}} {{if $.Repository.OriginalURL}} + {{ctx.Locale.Tr "repo.issues.commented_at" (.HashTag|Escape) $createdStr}} {{if $.Repository.OriginalURL}} - ({{ctx.Locale.Tr "repo.migrated_from" ($.Repository.OriginalURL|Escape) ($.Repository.GetOriginalURLHostname|Escape) | Safe}}){{end}} + ({{ctx.Locale.Tr "repo.migrated_from" ($.Repository.OriginalURL|Escape) ($.Repository.GetOriginalURLHostname|Escape)}}){{end}} {{else}} {{if gt .Poster.ID 0}} @@ -46,7 +46,7 @@ {{end}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.commented_at" (.HashTag|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.commented_at" (.HashTag|Escape) $createdStr}} {{end}}
    @@ -85,9 +85,9 @@ {{template "shared/user/authorlink" .Poster}} {{if .Issue.IsPull}} - {{ctx.Locale.Tr "repo.pulls.reopened_at" .EventTag $createdStr | Safe}} + {{ctx.Locale.Tr "repo.pulls.reopened_at" .EventTag $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.reopened_at" .EventTag $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.reopened_at" .EventTag $createdStr}} {{end}} @@ -98,9 +98,9 @@ {{template "shared/user/authorlink" .Poster}} {{if .Issue.IsPull}} - {{ctx.Locale.Tr "repo.pulls.closed_at" .EventTag $createdStr | Safe}} + {{ctx.Locale.Tr "repo.pulls.closed_at" .EventTag $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.closed_at" .EventTag $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.closed_at" .EventTag $createdStr}} {{end}} @@ -138,7 +138,7 @@ {{if eq .RefAction 3}}{{end}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr $refTr (.EventTag|Escape) $createdStr ((.RefCommentLink ctx)|Escape) $refFrom | Safe}} + {{ctx.Locale.Tr $refTr (.EventTag|Escape) $createdStr ((.RefCommentLink ctx)|Escape) $refFrom}} {{if eq .RefAction 3}}{{end}} @@ -153,9 +153,9 @@ {{template "shared/user/authorlink" .Poster}} {{if .Issue.IsPull}} - {{ctx.Locale.Tr "repo.pulls.commit_ref_at" .EventTag $createdStr | Safe}} + {{ctx.Locale.Tr "repo.pulls.commit_ref_at" .EventTag $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.commit_ref_at" .EventTag $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.commit_ref_at" .EventTag $createdStr}} {{end}}
    @@ -171,11 +171,11 @@ {{template "shared/user/authorlink" .Poster}} {{if and .AddedLabels (not .RemovedLabels)}} - {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context .AddedLabels $.RepoLink) $createdStr | Safe}} + {{ctx.Locale.TrN (len .AddedLabels) "repo.issues.add_label" "repo.issues.add_labels" (RenderLabels $.Context .AddedLabels $.RepoLink) $createdStr}} {{else if and (not .AddedLabels) .RemovedLabels}} - {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context .RemovedLabels $.RepoLink) $createdStr | Safe}} + {{ctx.Locale.TrN (len .RemovedLabels) "repo.issues.remove_label" "repo.issues.remove_labels" (RenderLabels $.Context .RemovedLabels $.RepoLink) $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context .AddedLabels $.RepoLink) (RenderLabels $.Context .RemovedLabels $.RepoLink) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.add_remove_labels" (RenderLabels $.Context .AddedLabels $.RepoLink) (RenderLabels $.Context .RemovedLabels $.RepoLink) $createdStr}} {{end}}
    @@ -186,7 +186,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{ctx.Locale.Tr "repo.issues.change_milestone_at" (.OldMilestone.Name|Escape) (.Milestone.Name|Escape) $createdStr | Safe}}{{else}}{{ctx.Locale.Tr "repo.issues.remove_milestone_at" (.OldMilestone.Name|Escape) $createdStr | Safe}}{{end}}{{else if gt .MilestoneID 0}}{{ctx.Locale.Tr "repo.issues.add_milestone_at" (.Milestone.Name|Escape) $createdStr | Safe}}{{end}} + {{if gt .OldMilestoneID 0}}{{if gt .MilestoneID 0}}{{ctx.Locale.Tr "repo.issues.change_milestone_at" (.OldMilestone.Name|Escape) (.Milestone.Name|Escape) $createdStr}}{{else}}{{ctx.Locale.Tr "repo.issues.remove_milestone_at" (.OldMilestone.Name|Escape) $createdStr}}{{end}}{{else if gt .MilestoneID 0}}{{ctx.Locale.Tr "repo.issues.add_milestone_at" (.Milestone.Name|Escape) $createdStr}}{{end}} {{else if and (eq .Type 9) (gt .AssigneeID 0)}} @@ -197,9 +197,9 @@ {{template "shared/user/authorlink" .Assignee}} {{if eq .Poster.ID .Assignee.ID}} - {{ctx.Locale.Tr "repo.issues.remove_self_assignment" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.remove_self_assignment" $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.remove_assignee_at" (.Poster.GetDisplayName|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.remove_assignee_at" (.Poster.GetDisplayName|Escape) $createdStr}} {{end}} {{else}} @@ -207,9 +207,9 @@ {{template "shared/user/authorlink" .Assignee}} {{if eq .Poster.ID .AssigneeID}} - {{ctx.Locale.Tr "repo.issues.self_assign_at" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.self_assign_at" $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.add_assignee_at" (.Poster.GetDisplayName|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.add_assignee_at" (.Poster.GetDisplayName|Escape) $createdStr}} {{end}} {{end}} @@ -220,7 +220,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.change_title_at" (.OldTitle|RenderEmoji $.Context) (.NewTitle|RenderEmoji $.Context) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.change_title_at" (.OldTitle|RenderEmoji $.Context) (.NewTitle|RenderEmoji $.Context) $createdStr}} {{else if eq .Type 11}} @@ -229,7 +229,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.delete_branch_at" (.OldRef|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.delete_branch_at" (.OldRef|Escape) $createdStr}} {{else if eq .Type 12}} @@ -238,7 +238,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.start_tracking_history" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.start_tracking_history" $createdStr}} {{else if eq .Type 13}} @@ -247,7 +247,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.stop_tracking_history" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.stop_tracking_history" $createdStr}} {{template "repo/issue/view_content/comments_delete_time" dict "ctxData" $ "comment" .}}
    @@ -266,7 +266,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.add_time_history" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.add_time_history" $createdStr}} {{template "repo/issue/view_content/comments_delete_time" dict "ctxData" $ "comment" .}}
    @@ -285,7 +285,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.cancel_tracking_history" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.cancel_tracking_history" $createdStr}}
    {{else if eq .Type 16}} @@ -294,7 +294,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.due_date_added" (DateTime "long" .Content) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.due_date_added" (DateTime "long" .Content) $createdStr}}
    {{else if eq .Type 17}} @@ -307,7 +307,7 @@ {{if eq (len $parsedDeadline) 2}} {{$from := DateTime "long" (index $parsedDeadline 1)}} {{$to := DateTime "long" (index $parsedDeadline 0)}} - {{ctx.Locale.Tr "repo.issues.due_date_modified" $to $from $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.due_date_modified" $to $from $createdStr}} {{end}} @@ -317,7 +317,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.due_date_remove" (DateTime "long" .Content) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.due_date_remove" (DateTime "long" .Content) $createdStr}} {{else if eq .Type 19}} @@ -326,7 +326,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.dependency.added_dependency" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.dependency.added_dependency" $createdStr}} {{if .DependentIssue}}
    @@ -349,7 +349,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.dependency.removed_dependency" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.dependency.removed_dependency" $createdStr}} {{if .DependentIssue}}
    @@ -393,13 +393,13 @@ {{end}} {{if eq .Review.Type 1}} - {{ctx.Locale.Tr "repo.issues.review.approve" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.review.approve" $createdStr}} {{else if eq .Review.Type 2}} - {{ctx.Locale.Tr "repo.issues.review.comment" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.review.comment" $createdStr}} {{else if eq .Review.Type 3}} - {{ctx.Locale.Tr "repo.issues.review.reject" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.review.reject" $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.review.comment" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.review.comment" $createdStr}} {{end}} {{if .Review.Dismissed}}
    {{ctx.Locale.Tr "repo.issues.review.dismissed_label"}}
    @@ -423,12 +423,12 @@ {{.OriginalAuthor}} {{if $.Repository.OriginalURL}} - ({{ctx.Locale.Tr "repo.migrated_from" ($.Repository.OriginalURL|Escape) ($.Repository.GetOriginalURLHostname|Escape) | Safe}}){{end}} + ({{ctx.Locale.Tr "repo.migrated_from" ($.Repository.OriginalURL|Escape) ($.Repository.GetOriginalURLHostname|Escape)}}){{end}} {{else}} {{template "shared/user/authorlink" .Poster}} {{end}} - {{ctx.Locale.Tr "repo.issues.review.left_comment" | Safe}} + {{ctx.Locale.Tr "repo.issues.review.left_comment"}}
    @@ -478,12 +478,12 @@ {{if .Content}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.lock_with_reason" .Content $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.lock_with_reason" .Content $createdStr}} {{else}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.lock_no_reason" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.lock_no_reason" $createdStr}} {{end}}
    @@ -493,7 +493,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.unlock_comment" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.unlock_comment" $createdStr}}
    {{else if eq .Type 25}} @@ -502,7 +502,7 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{.Poster.Name}} - {{ctx.Locale.Tr "repo.pulls.change_target_branch_at" (.OldRef|Escape) (.NewRef|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.pulls.change_target_branch_at" (.OldRef|Escape) (.NewRef|Escape) $createdStr}} {{else if eq .Type 26}} @@ -512,7 +512,7 @@ {{template "shared/user/authorlink" .Poster}} - {{ctx.Locale.Tr "repo.issues.del_time_history" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.del_time_history" $createdStr}}
    {{svg "octicon-clock"}} @@ -533,12 +533,12 @@ {{if (gt .AssigneeID 0)}} {{if .RemovedAssignee}} {{if eq .PosterID .AssigneeID}} - {{ctx.Locale.Tr "repo.issues.review.remove_review_request_self" $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.review.remove_review_request_self" $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.review.remove_review_request" (.Assignee.GetDisplayName|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.review.remove_review_request" (.Assignee.GetDisplayName|Escape) $createdStr}} {{end}} {{else}} - {{ctx.Locale.Tr "repo.issues.review.add_review_request" (.Assignee.GetDisplayName|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.review.add_review_request" (.Assignee.GetDisplayName|Escape) $createdStr}} {{end}} {{else}} @@ -547,9 +547,9 @@ {{$teamName = .AssigneeTeam.Name}} {{end}} {{if .RemovedAssignee}} - {{ctx.Locale.Tr "repo.issues.review.remove_review_request" ($teamName|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.review.remove_review_request" ($teamName|Escape) $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.review.add_review_request" ($teamName|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.review.add_review_request" ($teamName|Escape) $createdStr}} {{end}} {{end}} @@ -564,9 +564,9 @@ {{template "shared/user/authorlink" .Poster}} {{if .IsForcePush}} - {{ctx.Locale.Tr "repo.issues.force_push_codes" ($.Issue.PullRequest.HeadBranch|Escape) (ShortSha .OldCommit) (($.Issue.Repo.CommitLink .OldCommit)|Escape) (ShortSha .NewCommit) (($.Issue.Repo.CommitLink .NewCommit)|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.force_push_codes" ($.Issue.PullRequest.HeadBranch|Escape) (ShortSha .OldCommit) (($.Issue.Repo.CommitLink .OldCommit)|Escape) (ShortSha .NewCommit) (($.Issue.Repo.CommitLink .NewCommit)|Escape) $createdStr}} {{else}} - {{ctx.Locale.TrN (len .Commits) "repo.issues.push_commit_1" "repo.issues.push_commits_n" (len .Commits) $createdStr | Safe}} + {{ctx.Locale.TrN (len .Commits) "repo.issues.push_commit_1" "repo.issues.push_commits_n" (len .Commits) $createdStr}} {{end}} {{if and .IsForcePush $.Issue.PullRequest.BaseRepo.Name}} @@ -620,7 +620,7 @@ {{else}} {{$reviewerName = .Review.OriginalAuthor}} {{end}} - {{ctx.Locale.Tr "repo.issues.review.dismissed" $reviewerName $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.review.dismissed" $reviewerName $createdStr}}
    {{if .Content}} @@ -656,11 +656,11 @@ {{template "shared/user/authorlink" .Poster}} {{if and .OldRef .NewRef}} - {{ctx.Locale.Tr "repo.issues.change_ref_at" (.OldRef|Escape) (.NewRef|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.change_ref_at" (.OldRef|Escape) (.NewRef|Escape) $createdStr}} {{else if .OldRef}} - {{ctx.Locale.Tr "repo.issues.remove_ref_at" (.OldRef|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.remove_ref_at" (.OldRef|Escape) $createdStr}} {{else}} - {{ctx.Locale.Tr "repo.issues.add_ref_at" (.NewRef|Escape) $createdStr | Safe}} + {{ctx.Locale.Tr "repo.issues.add_ref_at" (.NewRef|Escape) $createdStr}} {{end}} @@ -679,8 +679,8 @@ {{else}} {{template "shared/user/authorlink" .Poster}} {{end}} - {{if eq .Type 34}}{{ctx.Locale.Tr "repo.pulls.auto_merge_newly_scheduled_comment" $createdStr | Safe}} - {{else}}{{ctx.Locale.Tr "repo.pulls.auto_merge_canceled_schedule_comment" $createdStr | Safe}}{{end}} + {{if eq .Type 34}}{{ctx.Locale.Tr "repo.pulls.auto_merge_newly_scheduled_comment" $createdStr}} + {{else}}{{ctx.Locale.Tr "repo.pulls.auto_merge_canceled_schedule_comment" $createdStr}}{{end}} {{else if or (eq .Type 36) (eq .Type 37)}} @@ -689,8 +689,8 @@ {{template "shared/user/avatarlink" dict "user" .Poster}} {{template "shared/user/authorlink" .Poster}} - {{if eq .Type 36}}{{ctx.Locale.Tr "repo.issues.pin_comment" $createdStr | Safe}} - {{else}}{{ctx.Locale.Tr "repo.issues.unpin_comment" $createdStr | Safe}}{{end}} + {{if eq .Type 36}}{{ctx.Locale.Tr "repo.issues.pin_comment" $createdStr}} + {{else}}{{ctx.Locale.Tr "repo.issues.unpin_comment" $createdStr}}{{end}} {{end}} diff --git a/templates/repo/issue/view_content/conversation.tmpl b/templates/repo/issue/view_content/conversation.tmpl index fc1d9865f5..1bc850d8cf 100644 --- a/templates/repo/issue/view_content/conversation.tmpl +++ b/templates/repo/issue/view_content/conversation.tmpl @@ -73,7 +73,7 @@ {{else}} {{template "shared/user/authorlink" .Poster}} {{end}} - {{ctx.Locale.Tr "repo.issues.commented_at" (.HashTag|Escape) $createdSubStr | Safe}} + {{ctx.Locale.Tr "repo.issues.commented_at" (.HashTag|Escape) $createdSubStr}}
    diff --git a/templates/repo/issue/view_content/pull.tmpl b/templates/repo/issue/view_content/pull.tmpl index e86deb8915..13d49b61b7 100644 --- a/templates/repo/issue/view_content/pull.tmpl +++ b/templates/repo/issue/view_content/pull.tmpl @@ -88,7 +88,7 @@
    {{if or .HasIssuesOrPullsWritePermission .IsIssuePoster}} {{end}} @@ -127,7 +127,7 @@ {{else if .IsBlockedByChangedProtectedFiles}}
    {{svg "octicon-x"}} - {{ctx.Locale.TrN $.ChangedProtectedFilesNum "repo.pulls.blocked_by_changed_protected_files_1" "repo.pulls.blocked_by_changed_protected_files_n" | Safe}} + {{ctx.Locale.TrN $.ChangedProtectedFilesNum "repo.pulls.blocked_by_changed_protected_files_1" "repo.pulls.blocked_by_changed_protected_files_n"}}
      {{range .ChangedProtectedFiles}} @@ -334,7 +334,7 @@ {{else if .IsBlockedByChangedProtectedFiles}}
      {{svg "octicon-x"}} - {{ctx.Locale.TrN $.ChangedProtectedFilesNum "repo.pulls.blocked_by_changed_protected_files_1" "repo.pulls.blocked_by_changed_protected_files_n" | Safe}} + {{ctx.Locale.TrN $.ChangedProtectedFilesNum "repo.pulls.blocked_by_changed_protected_files_1" "repo.pulls.blocked_by_changed_protected_files_n"}}
        {{range .ChangedProtectedFiles}} diff --git a/templates/repo/issue/view_content/pull_merge_instruction.tmpl b/templates/repo/issue/view_content/pull_merge_instruction.tmpl index a214f29786..a2269feeaf 100644 --- a/templates/repo/issue/view_content/pull_merge_instruction.tmpl +++ b/templates/repo/issue/view_content/pull_merge_instruction.tmpl @@ -1,5 +1,5 @@
        -
        {{ctx.Locale.Tr "repo.pulls.cmd_instruction_hint" | Safe}}
        +
        {{ctx.Locale.Tr "repo.pulls.cmd_instruction_hint"}}

        {{ctx.Locale.Tr "repo.pulls.cmd_instruction_checkout_title"}}

        {{ctx.Locale.Tr "repo.pulls.cmd_instruction_checkout_desc"}}
        {{$localBranch := .PullRequest.HeadBranch}} diff --git a/templates/repo/issue/view_content/sidebar.tmpl b/templates/repo/issue/view_content/sidebar.tmpl index 22f67ade7b..bb45b07421 100644 --- a/templates/repo/issue/view_content/sidebar.tmpl +++ b/templates/repo/issue/view_content/sidebar.tmpl @@ -101,7 +101,7 @@ {{range .OriginalReviews}}
        - + {{svg (MigrationIcon $.Repository.GetOriginalURLHostname) 20 "gt-mr-3"}} {{.OriginalAuthor}} @@ -116,7 +116,7 @@ {{if and (or .HasIssuesOrPullsWritePermission .IsIssuePoster) (not .HasMerged) (not .Issue.IsClosed) (not .IsPullWorkInProgress)}} {{end}} @@ -300,7 +300,7 @@ {{else}} {{if .HasUserStopwatch}}
        - {{ctx.Locale.Tr "repo.issues.tracking_already_started" (.OtherStopwatchURL|Escape) | Safe}} + {{ctx.Locale.Tr "repo.issues.tracking_already_started" (.OtherStopwatchURL|Escape)}}
        {{end}}
        diff --git a/templates/repo/migrate/gitbucket.tmpl b/templates/repo/migrate/gitbucket.tmpl index d07351e727..d1f1db99ba 100644 --- a/templates/repo/migrate/gitbucket.tmpl +++ b/templates/repo/migrate/gitbucket.tmpl @@ -34,7 +34,7 @@
        - +
        @@ -44,29 +44,29 @@
        - +
        - +
        - +
        - +
        - +
        @@ -106,10 +106,10 @@
        {{if .IsForcedPrivate}} - + {{else}} - + {{end}}
        diff --git a/templates/repo/migrate/gitea.tmpl b/templates/repo/migrate/gitea.tmpl index a40886b7a5..143f220449 100644 --- a/templates/repo/migrate/gitea.tmpl +++ b/templates/repo/migrate/gitea.tmpl @@ -30,7 +30,7 @@
        - +
        @@ -40,29 +40,29 @@
        - +
        - +
        - +
        - +
        - +
        @@ -102,10 +102,10 @@
        {{if .IsForcedPrivate}} - + {{else}} - + {{end}}
        diff --git a/templates/repo/migrate/github.tmpl b/templates/repo/migrate/github.tmpl index 07f8216fcb..dfb2b4bc46 100644 --- a/templates/repo/migrate/github.tmpl +++ b/templates/repo/migrate/github.tmpl @@ -33,7 +33,7 @@
        - +
        @@ -42,29 +42,29 @@
        - +
        - +
        - +
        - +
        - +
        @@ -104,10 +104,10 @@
        {{if .IsForcedPrivate}} - + {{else}} - + {{end}}
        diff --git a/templates/repo/migrate/gitlab.tmpl b/templates/repo/migrate/gitlab.tmpl index 623822df11..76c2828257 100644 --- a/templates/repo/migrate/gitlab.tmpl +++ b/templates/repo/migrate/gitlab.tmpl @@ -30,7 +30,7 @@
        - +
        @@ -39,29 +39,29 @@
        - +
        - +
        - +
        - +
        - +
        @@ -101,10 +101,10 @@
        {{if .IsForcedPrivate}} - + {{else}} - + {{end}}
        diff --git a/templates/repo/migrate/gogs.tmpl b/templates/repo/migrate/gogs.tmpl index 095efd5d60..b01d0eeb67 100644 --- a/templates/repo/migrate/gogs.tmpl +++ b/templates/repo/migrate/gogs.tmpl @@ -30,7 +30,7 @@
        - +
        @@ -40,18 +40,18 @@
        - +
        - +
        - +
        @@ -104,10 +104,10 @@
        {{if .IsForcedPrivate}} - + {{else}} - + {{end}}
        diff --git a/templates/repo/migrate/migrating.tmpl b/templates/repo/migrate/migrating.tmpl index 46cf997b68..bf8ea0ef7a 100644 --- a/templates/repo/migrate/migrating.tmpl +++ b/templates/repo/migrate/migrating.tmpl @@ -21,14 +21,14 @@
        -

        {{ctx.Locale.Tr "repo.migrate.migrating" .CloneAddr | Safe}}

        +

        {{ctx.Locale.Tr "repo.migrate.migrating" .CloneAddr}}

        {{if .CloneAddr}} -

        {{ctx.Locale.Tr "repo.migrate.migrating_failed" .CloneAddr | Safe}}

        +

        {{ctx.Locale.Tr "repo.migrate.migrating_failed" .CloneAddr}}

        {{else}} -

        {{ctx.Locale.Tr "repo.migrate.migrating_failed_no_addr" | Safe}}

        +

        {{ctx.Locale.Tr "repo.migrate.migrating_failed_no_addr"}}

        {{end}}

        @@ -57,8 +57,8 @@
        - {{ctx.Locale.Tr "repo.settings.delete_notices_1" | Safe}}
        - {{ctx.Locale.Tr "repo.settings.delete_notices_2" .Repository.FullName | Safe}} + {{ctx.Locale.Tr "repo.settings.delete_notices_1"}}
        + {{ctx.Locale.Tr "repo.settings.delete_notices_2" .Repository.FullName}} {{if .Repository.NumForks}}
        {{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}} {{end}} diff --git a/templates/repo/migrate/onedev.tmpl b/templates/repo/migrate/onedev.tmpl index b06e6929a1..8b2a2d8730 100644 --- a/templates/repo/migrate/onedev.tmpl +++ b/templates/repo/migrate/onedev.tmpl @@ -35,22 +35,22 @@
        - +
        - +
        - +
        - +
        @@ -90,10 +90,10 @@
        {{if .IsForcedPrivate}} - + {{else}} - + {{end}}
        diff --git a/templates/repo/pulls/fork.tmpl b/templates/repo/pulls/fork.tmpl index 94de4d78eb..f0907f409b 100644 --- a/templates/repo/pulls/fork.tmpl +++ b/templates/repo/pulls/fork.tmpl @@ -47,7 +47,7 @@
        - +
        {{ctx.Locale.Tr "repo.fork_visibility_helper"}} diff --git a/templates/repo/pulse.tmpl b/templates/repo/pulse.tmpl index ccd7ebf6b5..e6a59ea8c6 100644 --- a/templates/repo/pulse.tmpl +++ b/templates/repo/pulse.tmpl @@ -33,7 +33,7 @@ {{end}} - {{ctx.Locale.TrN .Activity.ActivePRCount "repo.activity.active_prs_count_1" "repo.activity.active_prs_count_n" .Activity.ActivePRCount | Safe}} + {{ctx.Locale.TrN .Activity.ActivePRCount "repo.activity.active_prs_count_1" "repo.activity.active_prs_count_n" .Activity.ActivePRCount}} {{end}} {{if .Permission.CanRead $.UnitTypeIssues}} @@ -48,7 +48,7 @@ {{end}} - {{ctx.Locale.TrN .Activity.ActiveIssueCount "repo.activity.active_issues_count_1" "repo.activity.active_issues_count_n" .Activity.ActiveIssueCount | Safe}} + {{ctx.Locale.TrN .Activity.ActiveIssueCount "repo.activity.active_issues_count_1" "repo.activity.active_issues_count_n" .Activity.ActiveIssueCount}} {{end}} diff --git a/templates/repo/settings/deploy_keys.tmpl b/templates/repo/settings/deploy_keys.tmpl index a283150c60..3ea854ef88 100644 --- a/templates/repo/settings/deploy_keys.tmpl +++ b/templates/repo/settings/deploy_keys.tmpl @@ -55,7 +55,7 @@ {{.Fingerprint}}
        - {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix) | Safe}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateTime "short" .UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}} - {{ctx.Locale.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{ctx.Locale.Tr "settings.can_write_info"}} {{end}} + {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateTime "short" .UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}} - {{ctx.Locale.Tr "settings.can_read_info"}}{{if not .IsReadOnly}} / {{ctx.Locale.Tr "settings.can_write_info"}} {{end}}
        diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 6cfef31060..128f553d3d 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -32,7 +32,7 @@ {{else}} {{end}} - +
        {{end}} @@ -624,8 +624,8 @@
        - {{ctx.Locale.Tr "repo.settings.delete_notices_1" | Safe}}
        - {{ctx.Locale.Tr "repo.settings.delete_notices_2" .Repository.FullName | Safe}} + {{ctx.Locale.Tr "repo.settings.delete_notices_1"}}
        + {{ctx.Locale.Tr "repo.settings.delete_notices_2" .Repository.FullName}} {{if .Repository.NumForks}}
        {{ctx.Locale.Tr "repo.settings.delete_notices_fork_1"}} {{end}} @@ -659,8 +659,8 @@
        - {{ctx.Locale.Tr "repo.settings.delete_notices_1" | Safe}}
        - {{ctx.Locale.Tr "repo.settings.wiki_delete_notices_1" .Repository.Name | Safe}} + {{ctx.Locale.Tr "repo.settings.delete_notices_1"}}
        + {{ctx.Locale.Tr "repo.settings.wiki_delete_notices_1" .Repository.Name}}
        {{.CsrfTokenHtml}} diff --git a/templates/repo/settings/protected_branch.tmpl b/templates/repo/settings/protected_branch.tmpl index 9c0fbddf06..e2f841f758 100644 --- a/templates/repo/settings/protected_branch.tmpl +++ b/templates/repo/settings/protected_branch.tmpl @@ -10,17 +10,17 @@ -

        {{ctx.Locale.Tr "repo.settings.protect_branch_name_pattern_desc" | Safe}}

        +

        {{ctx.Locale.Tr "repo.settings.protect_branch_name_pattern_desc"}}

        -

        {{ctx.Locale.Tr "repo.settings.protect_protected_file_patterns_desc" | Safe}}

        +

        {{ctx.Locale.Tr "repo.settings.protect_protected_file_patterns_desc"}}

        -

        {{ctx.Locale.Tr "repo.settings.protect_unprotected_file_patterns_desc" | Safe}}

        +

        {{ctx.Locale.Tr "repo.settings.protect_unprotected_file_patterns_desc"}}

        {{.CsrfTokenHtml}} diff --git a/templates/repo/settings/tags.tmpl b/templates/repo/settings/tags.tmpl index ed7762acc5..e4fcf2ee6b 100644 --- a/templates/repo/settings/tags.tmpl +++ b/templates/repo/settings/tags.tmpl @@ -21,7 +21,7 @@
        -
        {{ctx.Locale.Tr "repo.settings.tags.protection.pattern.description" | Safe}}
        +
        {{ctx.Locale.Tr "repo.settings.tags.protection.pattern.description"}}
        diff --git a/templates/repo/user_cards.tmpl b/templates/repo/user_cards.tmpl index 12fb23f067..5accc2c7af 100644 --- a/templates/repo/user_cards.tmpl +++ b/templates/repo/user_cards.tmpl @@ -18,7 +18,7 @@ {{else if .Location}} {{svg "octicon-location"}} {{.Location}} {{else}} - {{svg "octicon-calendar"}} {{ctx.Locale.Tr "user.joined_on" (DateTime "short" .CreatedUnix) | Safe}} + {{svg "octicon-calendar"}} {{ctx.Locale.Tr "user.joined_on" (DateTime "short" .CreatedUnix)}} {{end}}
        diff --git a/templates/repo/wiki/pages.tmpl b/templates/repo/wiki/pages.tmpl index a1bf13287c..22eb2619f9 100644 --- a/templates/repo/wiki/pages.tmpl +++ b/templates/repo/wiki/pages.tmpl @@ -20,7 +20,7 @@ {{svg "octicon-chevron-right"}} {{$timeSince := TimeSinceUnix .UpdatedUnix ctx.Locale}} - {{ctx.Locale.Tr "repo.wiki.last_updated" $timeSince | Safe}} + {{ctx.Locale.Tr "repo.wiki.last_updated" $timeSince}} {{end}} diff --git a/templates/repo/wiki/revision.tmpl b/templates/repo/wiki/revision.tmpl index 95b3cd0920..647c331d55 100644 --- a/templates/repo/wiki/revision.tmpl +++ b/templates/repo/wiki/revision.tmpl @@ -10,7 +10,7 @@ {{$title}}
        {{$timeSince := TimeSince .Author.When ctx.Locale}} - {{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince | Safe}} + {{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince}}
        diff --git a/templates/repo/wiki/view.tmpl b/templates/repo/wiki/view.tmpl index 039ff3f179..5b296dc2af 100644 --- a/templates/repo/wiki/view.tmpl +++ b/templates/repo/wiki/view.tmpl @@ -40,7 +40,7 @@ {{$title}}
        {{$timeSince := TimeSince .Author.When ctx.Locale}} - {{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince | Safe}} + {{ctx.Locale.Tr "repo.wiki.last_commit_info" .Author.Name $timeSince}}
        @@ -107,7 +107,7 @@ {{ctx.Locale.Tr "repo.wiki.delete_page_button"}}
        -

        {{ctx.Locale.Tr "repo.wiki.delete_page_notice_1" ($title|Escape) | Safe}}

        +

        {{ctx.Locale.Tr "repo.wiki.delete_page_notice_1" ($title|Escape)}}

        {{template "base/modal_actions_confirm" .}} diff --git a/templates/shared/actions/runner_edit.tmpl b/templates/shared/actions/runner_edit.tmpl index c10901501d..fbc730b288 100644 --- a/templates/shared/actions/runner_edit.tmpl +++ b/templates/shared/actions/runner_edit.tmpl @@ -89,7 +89,7 @@ {{ctx.Locale.Tr "actions.runners.delete_runner_header"}}
        -

        {{ctx.Locale.Tr "actions.runners.delete_runner_notice" | Safe}}

        +

        {{ctx.Locale.Tr "actions.runners.delete_runner_notice"}}

        {{template "base/modal_actions_confirm" .}} diff --git a/templates/shared/issuelist.tmpl b/templates/shared/issuelist.tmpl index 8fe5aadf2b..7940234ccc 100644 --- a/templates/shared/issuelist.tmpl +++ b/templates/shared/issuelist.tmpl @@ -62,11 +62,11 @@ {{$timeStr := TimeSinceUnix .GetLastEventTimestamp ctx.Locale}} {{if .OriginalAuthor}} - {{ctx.Locale.Tr .GetLastEventLabelFake $timeStr (.OriginalAuthor|Escape) | Safe}} + {{ctx.Locale.Tr .GetLastEventLabelFake $timeStr (.OriginalAuthor|Escape)}} {{else if gt .Poster.ID 0}} - {{ctx.Locale.Tr .GetLastEventLabel $timeStr (.Poster.HomeLink|Escape) (.Poster.GetDisplayName | Escape) | Safe}} + {{ctx.Locale.Tr .GetLastEventLabel $timeStr (.Poster.HomeLink|Escape) (.Poster.GetDisplayName | Escape)}} {{else}} - {{ctx.Locale.Tr .GetLastEventLabelFake $timeStr (.Poster.GetDisplayName | Escape) | Safe}} + {{ctx.Locale.Tr .GetLastEventLabelFake $timeStr (.Poster.GetDisplayName | Escape)}} {{end}} {{if .IsPull}}
        diff --git a/templates/shared/searchbottom.tmpl b/templates/shared/searchbottom.tmpl index 55b6cb2909..b123b497c7 100644 --- a/templates/shared/searchbottom.tmpl +++ b/templates/shared/searchbottom.tmpl @@ -6,7 +6,7 @@
        {{if not .result.UpdatedUnix.IsZero}} - {{ctx.Locale.Tr "explore.code_last_indexed_at" (TimeSinceUnix .result.UpdatedUnix ctx.Locale) | Safe}} + {{ctx.Locale.Tr "explore.code_last_indexed_at" (TimeSinceUnix .result.UpdatedUnix ctx.Locale)}} {{end}}
        diff --git a/templates/shared/secrets/add_list.tmpl b/templates/shared/secrets/add_list.tmpl index 7192f31fb2..4fbd8ddcfd 100644 --- a/templates/shared/secrets/add_list.tmpl +++ b/templates/shared/secrets/add_list.tmpl @@ -28,7 +28,7 @@
        - {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix) | Safe}} + {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}}
        - {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix) | Safe}} + {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}}
      - {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix) | Safe}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateTime "short" .UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}} + {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateTime "short" .UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}
      diff --git a/templates/user/settings/grants_oauth2.tmpl b/templates/user/settings/grants_oauth2.tmpl index 3c4c6e80d4..92fea1306f 100644 --- a/templates/user/settings/grants_oauth2.tmpl +++ b/templates/user/settings/grants_oauth2.tmpl @@ -14,7 +14,7 @@
      {{.Application.Name}}
      - {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix) | Safe}} + {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}}
      diff --git a/templates/user/settings/keys_gpg.tmpl b/templates/user/settings/keys_gpg.tmpl index e8bba69f6d..981cfd8106 100644 --- a/templates/user/settings/keys_gpg.tmpl +++ b/templates/user/settings/keys_gpg.tmpl @@ -63,9 +63,9 @@ {{ctx.Locale.Tr "settings.subkeys"}}: {{range .SubsKey}} {{.PaddedKeyID}} {{end}}
      - {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .AddedUnix) | Safe}} + {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .AddedUnix)}} - - {{if not .ExpiredUnix.IsZero}}{{ctx.Locale.Tr "settings.valid_until_date" (DateTime "short" .ExpiredUnix) | Safe}}{{else}}{{ctx.Locale.Tr "settings.valid_forever"}}{{end}} + {{if not .ExpiredUnix.IsZero}}{{ctx.Locale.Tr "settings.valid_until_date" (DateTime "short" .ExpiredUnix)}}{{else}}{{ctx.Locale.Tr "settings.valid_forever"}}{{end}}
      diff --git a/templates/user/settings/keys_principal.tmpl b/templates/user/settings/keys_principal.tmpl index a7ab12dd78..b6acb63c5e 100644 --- a/templates/user/settings/keys_principal.tmpl +++ b/templates/user/settings/keys_principal.tmpl @@ -22,7 +22,7 @@
      {{.Name}}
      - {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix) | Safe}} — {{svg "octicon-info" 16}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateTime "short" .UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}} + {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}} — {{svg "octicon-info" 16}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateTime "short" .UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}
      diff --git a/templates/user/settings/keys_ssh.tmpl b/templates/user/settings/keys_ssh.tmpl index 9a49cc4e8b..dc3179fdd0 100644 --- a/templates/user/settings/keys_ssh.tmpl +++ b/templates/user/settings/keys_ssh.tmpl @@ -53,7 +53,7 @@ {{.Fingerprint}}
      - {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix) | Safe}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateTime "short" .UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}} + {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}} — {{svg "octicon-info"}} {{if .HasUsed}}{{ctx.Locale.Tr "settings.last_used"}} {{DateTime "short" .UpdatedUnix}}{{else}}{{ctx.Locale.Tr "settings.no_activity"}}{{end}}
      diff --git a/templates/user/settings/packages.tmpl b/templates/user/settings/packages.tmpl index 43a0f9eca0..bd7d69b259 100644 --- a/templates/user/settings/packages.tmpl +++ b/templates/user/settings/packages.tmpl @@ -16,7 +16,7 @@
      - +
      diff --git a/templates/user/settings/security/webauthn.tmpl b/templates/user/settings/security/webauthn.tmpl index da6e5977c6..e582b801da 100644 --- a/templates/user/settings/security/webauthn.tmpl +++ b/templates/user/settings/security/webauthn.tmpl @@ -12,7 +12,7 @@
      {{.Name}}
      - {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix) | Safe}} + {{ctx.Locale.Tr "settings.added_on" (DateTime "short" .CreatedUnix)}}
      From 6db296f1cbd217067336d45a8ab38629f0c8ba83 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Thu, 22 Feb 2024 19:13:25 +0200 Subject: [PATCH 202/271] Remove jQuery from the repo commit functions (#29230) - Switched to plain JavaScript - Tested the commit ellipsis button functionality and it works as before - Tested the commits statuses tippy functionality and it works as before - Tested the last commit loader functionality and it works as before # Demo using JavaScript without jQuery ![action](https://github.com/go-gitea/gitea/assets/20454870/465516f8-0ff3-438c-a17e-26cbab82750b) ![action](https://github.com/go-gitea/gitea/assets/20454870/968da210-9382-4b50-a4c2-09419dc86e07) --------- Signed-off-by: Yarden Shoham Co-authored-by: silverwind (cherry picked from commit eaede2de98fbe0ac2156c9f4cd8b5899d2c7cbbf) --- web_src/js/features/repo-commit.js | 96 +++++++++++++++--------------- 1 file changed, 48 insertions(+), 48 deletions(-) diff --git a/web_src/js/features/repo-commit.js b/web_src/js/features/repo-commit.js index fc70ba41e4..7e2f6fa58e 100644 --- a/web_src/js/features/repo-commit.js +++ b/web_src/js/features/repo-commit.js @@ -1,70 +1,70 @@ -import $ from 'jquery'; import {createTippy} from '../modules/tippy.js'; import {toggleElem} from '../utils/dom.js'; - -const {csrfToken} = window.config; +import {parseDom} from '../utils.js'; +import {POST} from '../modules/fetch.js'; export function initRepoEllipsisButton() { - $('.js-toggle-commit-body').on('click', function (e) { - e.preventDefault(); - const expanded = $(this).attr('aria-expanded') === 'true'; - toggleElem($(this).parent().find('.commit-body')); - $(this).attr('aria-expanded', String(!expanded)); - }); + for (const button of document.querySelectorAll('.js-toggle-commit-body')) { + button.addEventListener('click', function (e) { + e.preventDefault(); + const expanded = this.getAttribute('aria-expanded') === 'true'; + toggleElem(this.parentElement.querySelector('.commit-body')); + this.setAttribute('aria-expanded', String(!expanded)); + }); + } } -export function initRepoCommitLastCommitLoader() { - const notReadyEls = document.querySelectorAll('table#repo-files-table tr.notready'); - if (!notReadyEls.length) return; - +export async function initRepoCommitLastCommitLoader() { const entryMap = {}; - const entries = []; - for (const el of notReadyEls) { - const entryname = el.getAttribute('data-entryname'); - entryMap[entryname] = $(el); - entries.push(entryname); - } - const lastCommitLoaderURL = $('table#repo-files-table').data('lastCommitLoaderUrl'); + const entries = Array.from(document.querySelectorAll('table#repo-files-table tr.notready'), (el) => { + const entryName = el.getAttribute('data-entryname'); + entryMap[entryName] = el; + return entryName; + }); - if (entries.length > 200) { - $.post(lastCommitLoaderURL, { - _csrf: csrfToken, - }, (data) => { - $('table#repo-files-table').replaceWith(data); - }); + if (entries.length === 0) { return; } - $.post(lastCommitLoaderURL, { - _csrf: csrfToken, - 'f': entries, - }, (data) => { - $(data).find('tr').each((_, row) => { - if (row.className === 'commit-list') { - $('table#repo-files-table .commit-list').replaceWith(row); - return; - } - // there are other rows in response (eg: ) - // at the moment only the "data-entryname" rows should be processed - const entryName = $(row).attr('data-entryname'); - if (entryName) { - entryMap[entryName].replaceWith(row); - } - }); - }); + const lastCommitLoaderURL = document.querySelector('table#repo-files-table').getAttribute('data-last-commit-loader-url'); + + if (entries.length > 200) { + // For more than 200 entries, replace the entire table + const response = await POST(lastCommitLoaderURL); + const data = await response.text(); + document.querySelector('table#repo-files-table').outerHTML = data; + return; + } + + // For fewer entries, update individual rows + const response = await POST(lastCommitLoaderURL, {data: {'f': entries}}); + const data = await response.text(); + const doc = parseDom(data, 'text/html'); + for (const row of doc.querySelectorAll('tr')) { + if (row.className === 'commit-list') { + document.querySelector('table#repo-files-table .commit-list')?.replaceWith(row); + continue; + } + // there are other rows in response (eg: ) + // at the moment only the "data-entryname" rows should be processed + const entryName = row.getAttribute('data-entryname'); + if (entryName) { + entryMap[entryName]?.replaceWith(row); + } + } } export function initCommitStatuses() { - $('[data-tippy="commit-statuses"]').each(function () { - const top = $('.repository.file.list').length > 0 || $('.repository.diff').length > 0; + for (const element of document.querySelectorAll('[data-tippy="commit-statuses"]')) { + const top = document.querySelector('.repository.file.list') || document.querySelector('.repository.diff'); - createTippy(this, { - content: this.nextElementSibling, + createTippy(element, { + content: element.nextElementSibling, placement: top ? 'top-start' : 'bottom-start', interactive: true, role: 'dialog', theme: 'box-with-header', }); - }); + } } From 44f45d8b7210941bad9ede8707125a0f1eb63eca Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 23 Feb 2024 01:40:53 +0800 Subject: [PATCH 203/271] Ignore the linux anchor point to avoid linux migrate failure (#29295) Fix #28843 This PR will bypass the pushUpdateTag to database failure when syncAllTags. An error log will be recorded. --------- Co-authored-by: wxiaoguang (cherry picked from commit 5ed17d9895bf678374ef5227ca37870c1c170802) --- modules/repository/repo.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/repository/repo.go b/modules/repository/repo.go index 65b50b2e45..2f076c5286 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -376,7 +376,9 @@ func SyncReleasesWithTags(ctx context.Context, repo *repo_model.Repository, gitR } if err := PushUpdateAddTag(ctx, repo, gitRepo, tagName, sha1, refname); err != nil { - return fmt.Errorf("unable to PushUpdateAddTag: %q to Repo[%d:%s/%s]: %w", tagName, repo.ID, repo.OwnerName, repo.Name, err) + // sometimes, some tags will be sync failed. i.e. https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tag/?h=v2.6.11 + // this is a tree object, not a tag object which created before git + log.Error("unable to PushUpdateAddTag: %q to Repo[%d:%s/%s]: %v", tagName, repo.ID, repo.OwnerName, repo.Name, err) } return nil From c432f141bb78ea6eac1c7ee99ba7afc7a59fba23 Mon Sep 17 00:00:00 2001 From: silverwind Date: Thu, 22 Feb 2024 22:21:43 +0100 Subject: [PATCH 204/271] Don't show third-party JS errors in production builds (#29303) So we don't get issues like https://github.com/go-gitea/gitea/issues/29080 and https://github.com/go-gitea/gitea/issues/29273 any more. Only active in [production builds](https://webpack.js.org/guides/production/#specify-the-mode), in non-production the errors will still show. (cherry picked from commit 532da5ed5ee3edb45d2ee63c6ab0fad53473691f) --- web_src/js/bootstrap.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/web_src/js/bootstrap.js b/web_src/js/bootstrap.js index f8d0c0cac0..e46c91e5e6 100644 --- a/web_src/js/bootstrap.js +++ b/web_src/js/bootstrap.js @@ -29,17 +29,26 @@ export function showGlobalErrorMessage(msg) { * @param {ErrorEvent} e */ function processWindowErrorEvent(e) { + const err = e.error ?? e.reason; + const assetBaseUrl = String(new URL(__webpack_public_path__, window.location.origin)); + + // error is likely from browser extension or inline script. Do not show these in production builds. + if (!err.stack?.includes(assetBaseUrl) && window.config?.runModeIsProd) return; + + let message; if (e.type === 'unhandledrejection') { - showGlobalErrorMessage(`JavaScript promise rejection: ${e.reason}. Open browser console to see more details.`); - return; + message = `JavaScript promise rejection: ${err.message}.`; + } else { + message = `JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}).`; } + if (!e.error && e.lineno === 0 && e.colno === 0 && e.filename === '' && window.navigator.userAgent.includes('FxiOS/')) { // At the moment, Firefox (iOS) (10x) has an engine bug. See https://github.com/go-gitea/gitea/issues/20240 // If a script inserts a newly created (and content changed) element into DOM, there will be a nonsense error event reporting: Script error: line 0, col 0. return; // ignore such nonsense error event } - showGlobalErrorMessage(`JavaScript error: ${e.message} (${e.filename} @ ${e.lineno}:${e.colno}). Open browser console to see more details.`); + showGlobalErrorMessage(`${message} Open browser console to see more details.`); } function initGlobalErrorHandler() { From 643d66ee0efb2a88b4bf41ed1f042ba43f55bb60 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 23 Feb 2024 00:31:24 +0100 Subject: [PATCH 205/271] Upgrade to fabric 6 (#29334) Upgrade fabric to latest v6 beta. It works for our use case, even thought it does not fix the upstream issue https://github.com/fabricjs/fabric.js/issues/9679 that https://github.com/go-gitea/gitea/issues/29326 relates to. (cherry picked from commit c4b0cb4d0d527793296cf801e611f77666f86551) Conflicts: public/assets/img/favicon.svg public/assets/img/logo.svg --- Makefile | 2 +- build/generate-images.js | 29 +++++++++++------------------ 2 files changed, 12 insertions(+), 19 deletions(-) diff --git a/Makefile b/Makefile index 72a48038f4..602f42fe4b 100644 --- a/Makefile +++ b/Makefile @@ -1009,7 +1009,7 @@ generate-gitignore: .PHONY: generate-images generate-images: | node_modules - npm install --no-save --no-package-lock fabric@5 imagemin-zopfli@7 + npm install --no-save fabric@6.0.0-beta19 imagemin-zopfli@7 node build/generate-images.js $(TAGS) .PHONY: generate-manpage diff --git a/build/generate-images.js b/build/generate-images.js index 09e3e068af..db31d19e2a 100755 --- a/build/generate-images.js +++ b/build/generate-images.js @@ -1,20 +1,13 @@ #!/usr/bin/env node import imageminZopfli from 'imagemin-zopfli'; import {optimize} from 'svgo'; -import {fabric} from 'fabric'; +import {loadSVGFromString, Canvas, Rect, util} from 'fabric/node'; import {readFile, writeFile} from 'node:fs/promises'; +import {argv, exit} from 'node:process'; -function exit(err) { +function doExit(err) { if (err) console.error(err); - process.exit(err ? 1 : 0); -} - -function loadSvg(svg) { - return new Promise((resolve) => { - fabric.loadSVGFromString(svg, (objects, options) => { - resolve({objects, options}); - }); - }); + exit(err ? 1 : 0); } async function generate(svg, path, {size, bg}) { @@ -35,14 +28,14 @@ async function generate(svg, path, {size, bg}) { return; } - const {objects, options} = await loadSvg(svg); - const canvas = new fabric.Canvas(); + const {objects, options} = await loadSVGFromString(svg); + const canvas = new Canvas(); canvas.setDimensions({width: size, height: size}); const ctx = canvas.getContext('2d'); ctx.scale(options.width ? (size / options.width) : 1, options.height ? (size / options.height) : 1); if (bg) { - canvas.add(new fabric.Rect({ + canvas.add(new Rect({ left: 0, top: 0, height: size * (1 / (size / options.height)), @@ -51,7 +44,7 @@ async function generate(svg, path, {size, bg}) { })); } - canvas.add(fabric.util.groupSVGElements(objects, options)); + canvas.add(util.groupSVGElements(objects, options)); canvas.renderAll(); let png = Buffer.from([]); @@ -64,7 +57,7 @@ async function generate(svg, path, {size, bg}) { } async function main() { - const gitea = process.argv.slice(2).includes('gitea'); + const gitea = argv.slice(2).includes('gitea'); const logoSvg = await readFile(new URL('../assets/logo.svg', import.meta.url), 'utf8'); const faviconSvg = await readFile(new URL('../assets/favicon.svg', import.meta.url), 'utf8'); @@ -80,7 +73,7 @@ async function main() { } try { - exit(await main()); + doExit(await main()); } catch (err) { - exit(err); + doExit(err); } From 4b494d341f3142c066bc5b2b3cfd50f924d64fd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tim-Nicas=20Oelschl=C3=A4ger?= <72873130+zokkis@users.noreply.github.com> Date: Fri, 23 Feb 2024 01:24:57 +0100 Subject: [PATCH 206/271] Unify organizations header (#29248) Unify organizations header before: ![image](https://github.com/go-gitea/gitea/assets/72873130/74474e0d-33c3-4bbf-9324-d130ea2c62f8) after: ![image](https://github.com/go-gitea/gitea/assets/72873130/1c65de0d-fa0f-4b17-ab8d-067de8c7113b) --------- Co-authored-by: silverwind (cherry picked from commit 532e422027c88a4a3dc0c2968857f8d5f94d861f) Conflicts: routers/web/shared/user/header.go templates/org/home.tmpl context --- modules/context/org.go | 15 +++++ routers/web/org/home.go | 18 ------ routers/web/shared/user/header.go | 15 ----- routers/web/user/profile.go | 1 + templates/org/header.tmpl | 42 +++++++++----- templates/org/home.tmpl | 33 +---------- templates/org/member/members.tmpl | 2 +- templates/org/menu.tmpl | 10 ++-- templates/org/projects/list.tmpl | 7 +-- templates/package/settings.tmpl | 12 +++- templates/user/code.tmpl | 5 +- templates/user/overview/header.tmpl | 57 ++++++------------- templates/user/overview/package_versions.tmpl | 7 +-- templates/user/overview/packages.tmpl | 7 +-- web_src/css/org.css | 18 +++--- 15 files changed, 95 insertions(+), 154 deletions(-) diff --git a/modules/context/org.go b/modules/context/org.go index d068646577..018b76de43 100644 --- a/modules/context/org.go +++ b/modules/context/org.go @@ -11,6 +11,8 @@ import ( "code.gitea.io/gitea/models/perm" "code.gitea.io/gitea/models/unit" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" ) @@ -255,6 +257,19 @@ func HandleOrgAssignment(ctx *Context, args ...bool) { ctx.Data["CanReadProjects"] = ctx.Org.CanReadUnit(ctx, unit.TypeProjects) ctx.Data["CanReadPackages"] = ctx.Org.CanReadUnit(ctx, unit.TypePackages) ctx.Data["CanReadCode"] = ctx.Org.CanReadUnit(ctx, unit.TypeCode) + + ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) + if len(ctx.ContextUser.Description) != 0 { + content, err := markdown.RenderString(&markup.RenderContext{ + Metas: map[string]string{"mode": "document"}, + Ctx: ctx, + }, ctx.ContextUser.Description) + if err != nil { + ctx.ServerError("RenderString", err) + return + } + ctx.Data["RenderedDescription"] = content + } } // OrgAssignment returns a middleware to handle organization assignment diff --git a/routers/web/org/home.go b/routers/web/org/home.go index 8bf02b2c42..36f543dc45 100644 --- a/routers/web/org/home.go +++ b/routers/web/org/home.go @@ -11,7 +11,6 @@ import ( "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" repo_model "code.gitea.io/gitea/models/repo" - user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" @@ -46,17 +45,6 @@ func Home(ctx *context.Context) { ctx.Data["PageIsUserProfile"] = true ctx.Data["Title"] = org.DisplayName() - if len(org.Description) != 0 { - desc, err := markdown.RenderString(&markup.RenderContext{ - Ctx: ctx, - Metas: map[string]string{"mode": "document"}, - }, org.Description) - if err != nil { - ctx.ServerError("RenderString", err) - return - } - ctx.Data["RenderedDescription"] = desc - } var orderBy db.SearchOrderBy ctx.Data["SortType"] = ctx.FormString("sort") @@ -131,18 +119,12 @@ func Home(ctx *context.Context) { return } - var isFollowing bool - if ctx.Doer != nil { - isFollowing = user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) - } - ctx.Data["Repos"] = repos ctx.Data["Total"] = count ctx.Data["Members"] = members ctx.Data["Teams"] = ctx.Org.Teams ctx.Data["DisableNewPullMirrors"] = setting.Mirror.DisableNewPull ctx.Data["PageIsViewRepositories"] = true - ctx.Data["IsFollowing"] = isFollowing err = shared_user.LoadHeaderCount(ctx) if err != nil { diff --git a/routers/web/shared/user/header.go b/routers/web/shared/user/header.go index 6830bdb8a9..07026e484f 100644 --- a/routers/web/shared/user/header.go +++ b/routers/web/shared/user/header.go @@ -17,8 +17,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/markup" - "code.gitea.io/gitea/modules/markup/markdown" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" ) @@ -36,7 +34,6 @@ func prepareContextForCommonProfile(ctx *context.Context) { func PrepareContextForProfileBigAvatar(ctx *context.Context) { prepareContextForCommonProfile(ctx) - ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) ctx.Data["IsBlocked"] = ctx.Doer != nil && user_model.IsBlocked(ctx, ctx.Doer.ID, ctx.ContextUser.ID) ctx.Data["ShowUserEmail"] = setting.UI.ShowUserEmail && ctx.ContextUser.Email != "" && ctx.IsSigned && !ctx.ContextUser.KeepEmailPrivate ctx.Data["ContextUserLocationMapURL"] = setting.Service.UserLocationMapURL + url.QueryEscape(ctx.ContextUser.Location) @@ -49,18 +46,6 @@ func PrepareContextForProfileBigAvatar(ctx *context.Context) { } ctx.Data["OpenIDs"] = openIDs - if len(ctx.ContextUser.Description) != 0 { - content, err := markdown.RenderString(&markup.RenderContext{ - Metas: map[string]string{"mode": "document"}, - Ctx: ctx, - }, ctx.ContextUser.Description) - if err != nil { - ctx.ServerError("RenderString", err) - return - } - ctx.Data["RenderedDescription"] = content - } - showPrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID) orgs, err := db.Find[organization.Organization](ctx, organization.FindOrgOptions{ UserID: ctx.ContextUser.ID, diff --git a/routers/web/user/profile.go b/routers/web/user/profile.go index 1a9a9cf603..4eec0e9905 100644 --- a/routers/web/user/profile.go +++ b/routers/web/user/profile.go @@ -355,6 +355,7 @@ func Action(ctx *context.Context) { ctx.HTML(http.StatusOK, tplProfileBigAvatar) return } else if ctx.ContextUser.IsOrganization() { + ctx.Data["Org"] = ctx.ContextUser ctx.Data["IsFollowing"] = ctx.Doer != nil && user_model.IsFollowing(ctx, ctx.Doer.ID, ctx.ContextUser.ID) ctx.HTML(http.StatusOK, tplFollowUnfollow) return diff --git a/templates/org/header.tmpl b/templates/org/header.tmpl index 7b912c1c56..8423fd7d3b 100644 --- a/templates/org/header.tmpl +++ b/templates/org/header.tmpl @@ -1,18 +1,32 @@ -{{with .Org}} -
      -
      -
      -
      - {{ctx.AvatarUtils.Avatar . 100}} - {{.DisplayName}} - - {{if .Visibility.IsLimited}}
      {{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}
      {{end}} - {{if .Visibility.IsPrivate}}
      {{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}
      {{end}} -
      -
      -
      +
      + {{ctx.AvatarUtils.Avatar .Org 100 "org-avatar"}} +
      +
      + {{.Org.DisplayName}} + + {{if .Org.Visibility.IsLimited}}{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}{{end}} + {{if .Org.Visibility.IsPrivate}}{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}{{end}} + + + {{if .EnableFeed}} + + {{svg "octicon-rss" 24}} + + {{end}} + {{if .IsSigned}} + {{template "org/follow_unfollow" .}} + {{end}} + +
      + {{if .RenderedDescription}}
      {{.RenderedDescription | Str2html}}
      {{end}} +
      + {{if .Org.Location}}
      {{svg "octicon-location"}} {{.Org.Location}}
      {{end}} + {{if .Org.Website}}
      {{svg "octicon-link"}} {{.Org.Website}}
      {{end}} + {{if .IsSigned}} + {{if .Org.Email}}
      {{svg "octicon-mail"}} {{.Org.Email}}
      {{end}} + {{end}}
      -{{end}} +
      {{template "org/menu" .}} diff --git a/templates/org/home.tmpl b/templates/org/home.tmpl index fd2326ffd5..1c8a083aed 100644 --- a/templates/org/home.tmpl +++ b/templates/org/home.tmpl @@ -5,38 +5,7 @@ {{template "base/alert" .}}
      {{end}} -
      - {{ctx.AvatarUtils.Avatar .Org 140 "org-avatar"}} -
      -
      - {{.Org.DisplayName}} - - {{if .Org.Visibility.IsLimited}}{{ctx.Locale.Tr "org.settings.visibility.limited_shortname"}}{{end}} - {{if .Org.Visibility.IsPrivate}}{{ctx.Locale.Tr "org.settings.visibility.private_shortname"}}{{end}} - -
      - {{if $.RenderedDescription}}
      {{$.RenderedDescription|Str2html}}
      {{end}} -
      - {{if .Org.Location}}
      {{svg "octicon-location"}} {{.Org.Location}}
      {{end}} - {{if .Org.Website}}
      {{svg "octicon-link"}} {{.Org.Website}}
      {{end}} - {{if $.IsSigned}} - {{if .Org.Email}}
      {{svg "octicon-mail"}} {{.Org.Email}}
      {{end}} - {{end}} -
      -
      - -
      - - {{template "org/menu" .}} + {{template "org/header" .}}
      diff --git a/templates/org/member/members.tmpl b/templates/org/member/members.tmpl index 03509ec93e..64f1aaa7d2 100644 --- a/templates/org/member/members.tmpl +++ b/templates/org/member/members.tmpl @@ -1,5 +1,5 @@ {{template "base/head" .}} -
      +
      {{template "org/header" .}}
      {{template "base/alert" .}} diff --git a/templates/org/menu.tmpl b/templates/org/menu.tmpl index 8a97711ce2..f07b26865a 100644 --- a/templates/org/menu.tmpl +++ b/templates/org/menu.tmpl @@ -15,24 +15,24 @@ {{end}} {{if and .IsPackageEnabled .CanReadPackages}} - + {{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}} {{end}} {{if and .IsRepoIndexerEnabled .CanReadCode}} - - {{svg "octicon-code"}} {{ctx.Locale.Tr "org.code"}} + + {{svg "octicon-code"}} {{ctx.Locale.Tr "org.code"}} {{end}} {{if .NumMembers}} - {{svg "octicon-person"}} {{ctx.Locale.Tr "org.members"}} + {{svg "octicon-person"}} {{ctx.Locale.Tr "org.members"}}
      {{.NumMembers}}
      {{end}} {{if .IsOrganizationMember}} - {{svg "octicon-people"}} {{ctx.Locale.Tr "org.teams"}} + {{svg "octicon-people"}} {{ctx.Locale.Tr "org.teams"}} {{if .NumTeams}}
      {{.NumTeams}}
      {{end}} diff --git a/templates/org/projects/list.tmpl b/templates/org/projects/list.tmpl index 689091e5e0..97cc6cf66c 100644 --- a/templates/org/projects/list.tmpl +++ b/templates/org/projects/list.tmpl @@ -1,10 +1,9 @@ {{template "base/head" .}} {{if .ContextUser.IsOrganization}} -
      - {{template "shared/user/org_profile_avatar" .}} +
      + {{template "org/header" .}}
      - {{template "user/overview/header" .}} - {{template "projects/list" .}} + {{template "projects/list" .}}
      {{else}} diff --git a/templates/package/settings.tmpl b/templates/package/settings.tmpl index 6ef62753e2..10e26c7010 100644 --- a/templates/package/settings.tmpl +++ b/templates/package/settings.tmpl @@ -1,8 +1,14 @@ {{template "base/head" .}} -
      - {{template "shared/user/org_profile_avatar" .}} +
      + {{if .ContextUser.IsOrganization}} + {{template "org/header" .}} + {{else}} + {{template "shared/user/org_profile_avatar" .}} + {{end}}
      - {{template "user/overview/header" .}} + {{if not .ContextUser.IsOrganization}} + {{template "user/overview/header" .}} + {{end}} {{template "base/alert" .}}

      {{.PackageDescriptor.Package.Name}} ({{.PackageDescriptor.Version.Version}}) / {{ctx.Locale.Tr "repo.settings"}}

      diff --git a/templates/user/code.tmpl b/templates/user/code.tmpl index da9a3c3a24..f71f55c474 100644 --- a/templates/user/code.tmpl +++ b/templates/user/code.tmpl @@ -1,9 +1,8 @@ {{template "base/head" .}} {{if .ContextUser.IsOrganization}} -
      - {{template "shared/user/org_profile_avatar" .}} +
      + {{template "org/header" .}}
      - {{template "user/overview/header" .}} {{template "code/searchcombo" .}}
      diff --git a/templates/user/overview/header.tmpl b/templates/user/overview/header.tmpl index c0cbe2561c..4fdaa70d87 100644 --- a/templates/user/overview/header.tmpl +++ b/templates/user/overview/header.tmpl @@ -10,7 +10,7 @@
      {{.RepoCount}}
      {{end}} - {{if or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadProjects)}} + {{if or .ContextUser.IsIndividual .CanReadProjects}} {{svg "octicon-project-symlink"}} {{ctx.Locale.Tr "user.projects"}} {{if .ProjectCount}} @@ -18,55 +18,30 @@ {{end}} {{end}} - {{if and .IsPackageEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadPackages))}} + {{if and .IsPackageEnabled (or .ContextUser.IsIndividual .CanReadPackages)}} {{svg "octicon-package"}} {{ctx.Locale.Tr "packages.title"}} {{end}} - {{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual (and .ContextUser.IsOrganization .CanReadCode))}} + {{if and .IsRepoIndexerEnabled (or .ContextUser.IsIndividual .CanReadCode)}} {{svg "octicon-code"}} {{ctx.Locale.Tr "user.code"}} {{end}} - {{if .ContextUser.IsOrganization}} - {{if .NumMembers}} - - {{svg "octicon-person"}} {{ctx.Locale.Tr "org.members"}} -
      {{.NumMembers}}
      -
      - {{end}} - {{if .IsOrganizationMember}} - - {{svg "octicon-people"}} {{ctx.Locale.Tr "org.teams"}} - {{if .NumTeams}} -
      {{.NumTeams}}
      - {{end}} -
      - {{end}} - - {{if .IsOrganizationOwner}} - - {{end}} - {{else}} - - {{svg "octicon-rss"}} {{ctx.Locale.Tr "user.activity"}} + + {{svg "octicon-rss"}} {{ctx.Locale.Tr "user.activity"}} + + {{if not .DisableStars}} + + {{svg "octicon-star"}} {{ctx.Locale.Tr "user.starred"}} + {{if .ContextUser.NumStars}} +
      {{.ContextUser.NumStars}}
      + {{end}} +
      + {{else}} + + {{svg "octicon-eye"}} {{ctx.Locale.Tr "user.watched"}} - {{if not .DisableStars}} - - {{svg "octicon-star"}} {{ctx.Locale.Tr "user.starred"}} - {{if .ContextUser.NumStars}} -
      {{.ContextUser.NumStars}}
      - {{end}} -
      - {{else}} - - {{svg "octicon-eye"}} {{ctx.Locale.Tr "user.watched"}} - - {{end}} {{end}}
      diff --git a/templates/user/overview/package_versions.tmpl b/templates/user/overview/package_versions.tmpl index 6f740e0e7c..f6f963aecb 100644 --- a/templates/user/overview/package_versions.tmpl +++ b/templates/user/overview/package_versions.tmpl @@ -1,10 +1,9 @@ {{template "base/head" .}} {{if .ContextUser.IsOrganization}} -
      - {{template "shared/user/org_profile_avatar" .}} +
      + {{template "org/header" .}}
      - {{template "user/overview/header" .}} - {{template "package/shared/versionlist" .}} + {{template "package/shared/versionlist" .}}
      {{else}} diff --git a/templates/user/overview/packages.tmpl b/templates/user/overview/packages.tmpl index 4fd17696d1..30ff871cb2 100644 --- a/templates/user/overview/packages.tmpl +++ b/templates/user/overview/packages.tmpl @@ -1,10 +1,9 @@ {{template "base/head" .}} {{if .ContextUser.IsOrganization}} -
      - {{template "shared/user/org_profile_avatar" .}} +
      + {{template "org/header" .}}
      - {{template "user/overview/header" .}} - {{template "package/shared/list" .}} + {{template "package/shared/list" .}}
      {{else}} diff --git a/web_src/css/org.css b/web_src/css/org.css index 76512e0077..a1ef8e08ed 100644 --- a/web_src/css/org.css +++ b/web_src/css/org.css @@ -93,46 +93,44 @@ min-width: 300px; } -.organization.profile .org-avatar { - width: 100px; - height: 100px; +.page-content.organization .org-avatar { margin-right: 15px; } -.organization.profile #org-info { +.page-content.organization #org-info { overflow-wrap: anywhere; flex: 1; word-break: break-all; } -.organization.profile #org-info .ui.header { +.page-content.organization #org-info .ui.header { display: flex; align-items: center; font-size: 36px; margin-bottom: 0; } -.organization.profile #org-info .desc { +.page-content.organization #org-info .desc { font-size: 16px; margin-bottom: 10px; } -.organization.profile #org-info .meta { +.page-content.organization #org-info .meta { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; } -.organization.profile .ui.top.header .ui.right { +.page-content.organization .ui.top.header .ui.right { margin-top: 0; } -.organization.profile .teams .item { +.page-content.organization .teams .item { padding: 10px 15px; } -.organization.profile .members .ui.avatar { +.page-content.organization .members .ui.avatar { width: 48px; height: 48px; margin-right: 5px; From 8ff858b94b404506c9adb075d73f6353c047f84f Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 23 Feb 2024 03:18:33 +0100 Subject: [PATCH 207/271] Start to migrate from `util.OptionalBool` to `optional.Option[bool]` (#29329) just create transition helper and migrate two structs (cherry picked from commit 7fbdb60fc1152acc9a040dc04b1b0f5a3475b081) --- cmd/admin_user_create.go | 8 +++--- models/git/branch_list.go | 10 ++++---- models/git/branch_test.go | 4 +-- models/git/protected_branch_list.go | 4 +-- models/user/user.go | 25 ++++++++++--------- modules/context/repo.go | 3 ++- modules/optional/option_test.go | 5 ++-- modules/util/util.go | 18 +++++++++++++ routers/api/v1/admin/user.go | 8 ++---- routers/api/v1/repo/branch.go | 6 ++--- routers/install/install.go | 6 ++--- routers/web/admin/users.go | 2 +- routers/web/auth/oauth.go | 2 +- routers/web/repo/compare.go | 5 ++-- routers/web/repo/pull.go | 3 ++- routers/web/repo/repo.go | 5 ++-- services/auth/reverseproxy.go | 4 +-- .../auth/source/ldap/source_authenticate.go | 5 ++-- services/auth/source/ldap/source_sync.go | 5 ++-- .../auth/source/pam/source_authenticate.go | 4 +-- .../auth/source/smtp/source_authenticate.go | 3 ++- services/auth/sspi.go | 5 ++-- services/repository/adopt.go | 3 ++- services/repository/branch.go | 5 ++-- 24 files changed, 84 insertions(+), 64 deletions(-) diff --git a/cmd/admin_user_create.go b/cmd/admin_user_create.go index fefe18d39c..a257ce21c8 100644 --- a/cmd/admin_user_create.go +++ b/cmd/admin_user_create.go @@ -10,8 +10,8 @@ import ( auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" pwd "code.gitea.io/gitea/modules/auth/password" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "github.com/urfave/cli/v2" ) @@ -123,10 +123,10 @@ func runCreateUser(c *cli.Context) error { changePassword = c.Bool("must-change-password") } - restricted := util.OptionalBoolNone + restricted := optional.None[bool]() if c.IsSet("restricted") { - restricted = util.OptionalBoolOf(c.Bool("restricted")) + restricted = optional.Some(c.Bool("restricted")) } // default user visibility in app.ini @@ -142,7 +142,7 @@ func runCreateUser(c *cli.Context) error { } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), IsRestricted: restricted, } diff --git a/models/git/branch_list.go b/models/git/branch_list.go index 0e8d28038a..8319e5ecd0 100644 --- a/models/git/branch_list.go +++ b/models/git/branch_list.go @@ -9,7 +9,7 @@ import ( "code.gitea.io/gitea/models/db" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "xorm.io/builder" ) @@ -67,7 +67,7 @@ type FindBranchOptions struct { db.ListOptions RepoID int64 ExcludeBranchNames []string - IsDeletedBranch util.OptionalBool + IsDeletedBranch optional.Option[bool] OrderBy string Keyword string } @@ -81,8 +81,8 @@ func (opts FindBranchOptions) ToConds() builder.Cond { if len(opts.ExcludeBranchNames) > 0 { cond = cond.And(builder.NotIn("name", opts.ExcludeBranchNames)) } - if !opts.IsDeletedBranch.IsNone() { - cond = cond.And(builder.Eq{"is_deleted": opts.IsDeletedBranch.IsTrue()}) + if opts.IsDeletedBranch.Has() { + cond = cond.And(builder.Eq{"is_deleted": opts.IsDeletedBranch.Value()}) } if opts.Keyword != "" { cond = cond.And(builder.Like{"name", opts.Keyword}) @@ -92,7 +92,7 @@ func (opts FindBranchOptions) ToConds() builder.Cond { func (opts FindBranchOptions) ToOrders() string { orderBy := opts.OrderBy - if !opts.IsDeletedBranch.IsFalse() { // if deleted branch included, put them at the end + if opts.IsDeletedBranch.ValueOrDefault(true) { // if deleted branch included, put them at the end if orderBy != "" { orderBy += ", " } diff --git a/models/git/branch_test.go b/models/git/branch_test.go index d480e2ec30..b984244cd2 100644 --- a/models/git/branch_test.go +++ b/models/git/branch_test.go @@ -12,7 +12,7 @@ import ( repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/git" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "github.com/stretchr/testify/assert" ) @@ -49,7 +49,7 @@ func TestGetDeletedBranches(t *testing.T) { branches, err := db.Find[git_model.Branch](db.DefaultContext, git_model.FindBranchOptions{ ListOptions: db.ListOptionsAll, RepoID: repo.ID, - IsDeletedBranch: util.OptionalBoolTrue, + IsDeletedBranch: optional.Some(true), }) assert.NoError(t, err) assert.Len(t, branches, 2) diff --git a/models/git/protected_branch_list.go b/models/git/protected_branch_list.go index eeb307e245..613333a5a2 100644 --- a/models/git/protected_branch_list.go +++ b/models/git/protected_branch_list.go @@ -8,7 +8,7 @@ import ( "sort" "code.gitea.io/gitea/models/db" - "code.gitea.io/gitea/modules/util" + "code.gitea.io/gitea/modules/optional" "github.com/gobwas/glob" ) @@ -56,7 +56,7 @@ func FindAllMatchedBranches(ctx context.Context, repoID int64, ruleName string) Page: page, }, RepoID: repoID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), }) if err != nil { return nil, err diff --git a/models/user/user.go b/models/user/user.go index 51a65ce6f4..9ff4881ec9 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -25,6 +25,7 @@ import ( "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" @@ -585,14 +586,14 @@ func IsUsableUsername(name string) error { // CreateUserOverwriteOptions are an optional options who overwrite system defaults on user creation type CreateUserOverwriteOptions struct { - KeepEmailPrivate util.OptionalBool + KeepEmailPrivate optional.Option[bool] Visibility *structs.VisibleType - AllowCreateOrganization util.OptionalBool + AllowCreateOrganization optional.Option[bool] EmailNotificationsPreference *string MaxRepoCreation *int Theme *string - IsRestricted util.OptionalBool - IsActive util.OptionalBool + IsRestricted optional.Option[bool] + IsActive optional.Option[bool] } // CreateUser creates record of a new user. @@ -619,14 +620,14 @@ func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOve // overwrite defaults if set if len(overwriteDefault) != 0 && overwriteDefault[0] != nil { overwrite := overwriteDefault[0] - if !overwrite.KeepEmailPrivate.IsNone() { - u.KeepEmailPrivate = overwrite.KeepEmailPrivate.IsTrue() + if overwrite.KeepEmailPrivate.Has() { + u.KeepEmailPrivate = overwrite.KeepEmailPrivate.Value() } if overwrite.Visibility != nil { u.Visibility = *overwrite.Visibility } - if !overwrite.AllowCreateOrganization.IsNone() { - u.AllowCreateOrganization = overwrite.AllowCreateOrganization.IsTrue() + if overwrite.AllowCreateOrganization.Has() { + u.AllowCreateOrganization = overwrite.AllowCreateOrganization.Value() } if overwrite.EmailNotificationsPreference != nil { u.EmailNotificationsPreference = *overwrite.EmailNotificationsPreference @@ -637,11 +638,11 @@ func CreateUser(ctx context.Context, u *User, overwriteDefault ...*CreateUserOve if overwrite.Theme != nil { u.Theme = *overwrite.Theme } - if !overwrite.IsRestricted.IsNone() { - u.IsRestricted = overwrite.IsRestricted.IsTrue() + if overwrite.IsRestricted.Has() { + u.IsRestricted = overwrite.IsRestricted.Value() } - if !overwrite.IsActive.IsNone() { - u.IsActive = overwrite.IsActive.IsTrue() + if overwrite.IsActive.Has() { + u.IsActive = overwrite.IsActive.Value() } } diff --git a/modules/context/repo.go b/modules/context/repo.go index 9d63f9eec3..490d798d50 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -27,6 +27,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" code_indexer "code.gitea.io/gitea/modules/indexer/code" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -672,7 +673,7 @@ func RepoAssignment(ctx *Context) context.CancelFunc { branchOpts := git_model.FindBranchOptions{ RepoID: ctx.Repo.Repository.ID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), ListOptions: db.ListOptionsAll, } branchesTotal, err := db.Count[git_model.Branch](ctx, branchOpts) diff --git a/modules/optional/option_test.go b/modules/optional/option_test.go index 7ec345b6ba..bfc4577dbe 100644 --- a/modules/optional/option_test.go +++ b/modules/optional/option_test.go @@ -6,8 +6,6 @@ package optional import ( "testing" - "code.gitea.io/gitea/modules/util" - "github.com/stretchr/testify/assert" ) @@ -30,7 +28,8 @@ func TestOption(t *testing.T) { var ptr *int assert.False(t, FromPtr(ptr).Has()) - opt1 := FromPtr(util.ToPointer(1)) + int1 := 1 + opt1 := FromPtr(&int1) assert.True(t, opt1.Has()) assert.Equal(t, int(1), opt1.Value()) diff --git a/modules/util/util.go b/modules/util/util.go index 0e5c6a4e64..28b549f405 100644 --- a/modules/util/util.go +++ b/modules/util/util.go @@ -11,6 +11,8 @@ import ( "strconv" "strings" + "code.gitea.io/gitea/modules/optional" + "golang.org/x/text/cases" "golang.org/x/text/language" ) @@ -42,6 +44,22 @@ func (o OptionalBool) IsNone() bool { return o == OptionalBoolNone } +// ToGeneric converts OptionalBool to optional.Option[bool] +func (o OptionalBool) ToGeneric() optional.Option[bool] { + if o.IsNone() { + return optional.None[bool]() + } + return optional.Some[bool](o.IsTrue()) +} + +// OptionalBoolFromGeneric converts optional.Option[bool] to OptionalBool +func OptionalBoolFromGeneric(o optional.Option[bool]) OptionalBool { + if o.Has() { + return OptionalBoolOf(o.Value()) + } + return OptionalBoolNone +} + // OptionalBoolOf get the corresponding OptionalBool of a bool func OptionalBoolOf(b bool) OptionalBool { if b { diff --git a/routers/api/v1/admin/user.go b/routers/api/v1/admin/user.go index 272996f43d..2ce7651a09 100644 --- a/routers/api/v1/admin/user.go +++ b/routers/api/v1/admin/user.go @@ -21,7 +21,6 @@ import ( "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/user" "code.gitea.io/gitea/routers/api/v1/utils" @@ -117,11 +116,8 @@ func CreateUser(ctx *context.APIContext) { } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolTrue, - } - - if form.Restricted != nil { - overwriteDefault.IsRestricted = util.OptionalBoolOf(*form.Restricted) + IsActive: optional.Some(true), + IsRestricted: optional.FromPtr(form.Restricted), } if form.Visibility != "" { diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index bd02a8afc4..2cdbcd25a2 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -17,9 +17,9 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" + "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" api "code.gitea.io/gitea/modules/structs" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/convert" @@ -141,7 +141,7 @@ func DeleteBranch(ctx *context.APIContext) { // check whether branches of this repository has been synced totalNumOfBranches, err := db.Count[git_model.Branch](ctx, git_model.FindBranchOptions{ RepoID: ctx.Repo.Repository.ID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), }) if err != nil { ctx.Error(http.StatusInternalServerError, "CountBranches", err) @@ -340,7 +340,7 @@ func ListBranches(ctx *context.APIContext) { branchOpts := git_model.FindBranchOptions{ ListOptions: listOptions, RepoID: ctx.Repo.Repository.ID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), } var err error totalNumOfBranches, err = db.Count[git_model.Branch](ctx, branchOpts) diff --git a/routers/install/install.go b/routers/install/install.go index 5c43bd486a..48271a64f2 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -25,11 +25,11 @@ import ( "code.gitea.io/gitea/modules/generate" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/templates" "code.gitea.io/gitea/modules/translation" "code.gitea.io/gitea/modules/user" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web" "code.gitea.io/gitea/modules/web/middleware" "code.gitea.io/gitea/routers/common" @@ -537,8 +537,8 @@ func SubmitInstall(ctx *context.Context) { IsAdmin: true, } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsRestricted: util.OptionalBoolFalse, - IsActive: util.OptionalBoolTrue, + IsRestricted: optional.Some(false), + IsActive: optional.Some(true), } if err = user_model.CreateUser(ctx, u, overwriteDefault); err != nil { diff --git a/routers/web/admin/users.go b/routers/web/admin/users.go index af184fa9eb..adb9799c01 100644 --- a/routers/web/admin/users.go +++ b/routers/web/admin/users.go @@ -140,7 +140,7 @@ func NewUserPost(ctx *context.Context) { } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), Visibility: &form.Visibility, } diff --git a/routers/web/auth/oauth.go b/routers/web/auth/oauth.go index e840e03bcf..4e4079d8ff 100644 --- a/routers/web/auth/oauth.go +++ b/routers/web/auth/oauth.go @@ -982,7 +982,7 @@ func SignInOAuthCallback(ctx *context.Context) { } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolOf(!setting.OAuth2Client.RegisterEmailConfirm && !setting.Service.RegisterManualConfirm), + IsActive: optional.Some(!setting.OAuth2Client.RegisterEmailConfirm && !setting.Service.RegisterManualConfirm), } source := authSource.Cfg.(*oauth2.Source) diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index 67d41cf807..df41c750de 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -31,6 +31,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/markup" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" api "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/typesniffer" @@ -700,7 +701,7 @@ func getBranchesAndTagsForRepo(ctx gocontext.Context, repo *repo_model.Repositor ListOptions: db.ListOptions{ ListAll: true, }, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), }) if err != nil { return nil, nil, err @@ -757,7 +758,7 @@ func CompareDiff(ctx *context.Context) { ListOptions: db.ListOptions{ ListAll: true, }, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), }) if err != nil { ctx.ServerError("GetBranches", err) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index fd7e902225..dcb0623504 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -33,6 +33,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" issue_template "code.gitea.io/gitea/modules/issue/template" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/upload" @@ -193,7 +194,7 @@ func updateForkRepositoryInContext(ctx *context.Context, forkRepo *repo_model.Re ListOptions: db.ListOptions{ ListAll: true, }, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), // Add it as the first option ExcludeBranchNames: []string{ctx.Repo.Repository.DefaultBranch}, }) diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 88c73f65f0..17e5d9b582 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -24,6 +24,7 @@ import ( "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/storage" @@ -685,7 +686,7 @@ type branchTagSearchResponse struct { func GetBranchesList(ctx *context.Context) { branchOpts := git_model.FindBranchOptions{ RepoID: ctx.Repo.Repository.ID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), ListOptions: db.ListOptions{ ListAll: true, }, @@ -720,7 +721,7 @@ func GetTagList(ctx *context.Context) { func PrepareBranchList(ctx *context.Context) { branchOpts := git_model.FindBranchOptions{ RepoID: ctx.Repo.Repository.ID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), ListOptions: db.ListOptions{ ListAll: true, }, diff --git a/services/auth/reverseproxy.go b/services/auth/reverseproxy.go index 359c1f2473..b6aeb0aed2 100644 --- a/services/auth/reverseproxy.go +++ b/services/auth/reverseproxy.go @@ -10,8 +10,8 @@ import ( user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web/middleware" gouuid "github.com/google/uuid" @@ -161,7 +161,7 @@ func (r *ReverseProxy) newUser(req *http.Request) *user_model.User { } overwriteDefault := user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), } if err := user_model.CreateUser(req.Context(), user, &overwriteDefault); err != nil { diff --git a/services/auth/source/ldap/source_authenticate.go b/services/auth/source/ldap/source_authenticate.go index 8f641ed541..68ecd16342 100644 --- a/services/auth/source/ldap/source_authenticate.go +++ b/services/auth/source/ldap/source_authenticate.go @@ -13,7 +13,6 @@ import ( user_model "code.gitea.io/gitea/models/user" auth_module "code.gitea.io/gitea/modules/auth" "code.gitea.io/gitea/modules/optional" - "code.gitea.io/gitea/modules/util" source_service "code.gitea.io/gitea/services/auth/source" user_service "code.gitea.io/gitea/services/user" ) @@ -85,8 +84,8 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u IsAdmin: sr.IsAdmin, } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsRestricted: util.OptionalBoolOf(sr.IsRestricted), - IsActive: util.OptionalBoolTrue, + IsRestricted: optional.Some(sr.IsRestricted), + IsActive: optional.Some(true), } err := user_model.CreateUser(ctx, user, overwriteDefault) diff --git a/services/auth/source/ldap/source_sync.go b/services/auth/source/ldap/source_sync.go index eee7bb585a..62f052d68c 100644 --- a/services/auth/source/ldap/source_sync.go +++ b/services/auth/source/ldap/source_sync.go @@ -16,7 +16,6 @@ import ( "code.gitea.io/gitea/modules/container" "code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/optional" - "code.gitea.io/gitea/modules/util" source_service "code.gitea.io/gitea/services/auth/source" user_service "code.gitea.io/gitea/services/user" ) @@ -125,8 +124,8 @@ func (source *Source) Sync(ctx context.Context, updateExisting bool) error { IsAdmin: su.IsAdmin, } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsRestricted: util.OptionalBoolOf(su.IsRestricted), - IsActive: util.OptionalBoolTrue, + IsRestricted: optional.Some(su.IsRestricted), + IsActive: optional.Some(true), } err = user_model.CreateUser(ctx, usr, overwriteDefault) diff --git a/services/auth/source/pam/source_authenticate.go b/services/auth/source/pam/source_authenticate.go index 0891a86392..addd1bd2c9 100644 --- a/services/auth/source/pam/source_authenticate.go +++ b/services/auth/source/pam/source_authenticate.go @@ -11,8 +11,8 @@ import ( "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/auth/pam" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" - "code.gitea.io/gitea/modules/util" "github.com/google/uuid" ) @@ -60,7 +60,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u LoginName: userName, // This is what the user typed in } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), } if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil { diff --git a/services/auth/source/smtp/source_authenticate.go b/services/auth/source/smtp/source_authenticate.go index b244fc7d40..1f0a61c789 100644 --- a/services/auth/source/smtp/source_authenticate.go +++ b/services/auth/source/smtp/source_authenticate.go @@ -12,6 +12,7 @@ import ( auth_model "code.gitea.io/gitea/models/auth" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/util" ) @@ -75,7 +76,7 @@ func (source *Source) Authenticate(ctx context.Context, user *user_model.User, u LoginName: userName, } overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolTrue, + IsActive: optional.Some(true), } if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil { diff --git a/services/auth/sspi.go b/services/auth/sspi.go index 0e974fde8f..8c0fc77a96 100644 --- a/services/auth/sspi.go +++ b/services/auth/sspi.go @@ -16,6 +16,7 @@ import ( "code.gitea.io/gitea/modules/base" gitea_context "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" "code.gitea.io/gitea/modules/web/middleware" @@ -172,8 +173,8 @@ func (s *SSPI) newUser(ctx context.Context, username string, cfg *sspi.Source) ( } emailNotificationPreference := user_model.EmailNotificationsDisabled overwriteDefault := &user_model.CreateUserOverwriteOptions{ - IsActive: util.OptionalBoolOf(cfg.AutoActivateUsers), - KeepEmailPrivate: util.OptionalBoolTrue, + IsActive: optional.Some(cfg.AutoActivateUsers), + KeepEmailPrivate: optional.Some(true), EmailNotificationsPreference: &emailNotificationPreference, } if err := user_model.CreateUser(ctx, user, overwriteDefault); err != nil { diff --git a/services/repository/adopt.go b/services/repository/adopt.go index bfb965063f..7ca68776b5 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/util" @@ -154,7 +155,7 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r ListOptions: db.ListOptions{ ListAll: true, }, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), }) found := false diff --git a/services/repository/branch.go b/services/repository/branch.go index d7ac25b7c9..6add9422f4 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -19,6 +19,7 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/graceful" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/optional" "code.gitea.io/gitea/modules/queue" repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/timeutil" @@ -59,7 +60,7 @@ func LoadBranches(ctx context.Context, repo *repo_model.Repository, gitRepo *git branchOpts := git_model.FindBranchOptions{ RepoID: repo.ID, - IsDeletedBranch: isDeletedBranch, + IsDeletedBranch: isDeletedBranch.ToGeneric(), ListOptions: db.ListOptions{ Page: page, PageSize: pageSize, @@ -243,7 +244,7 @@ func syncBranchToDB(ctx context.Context, repoID, pusherID int64, branchName stri // we cannot simply insert the branch but need to check we have branches or not hasBranch, err := db.Exist[git_model.Branch](ctx, git_model.FindBranchOptions{ RepoID: repoID, - IsDeletedBranch: util.OptionalBoolFalse, + IsDeletedBranch: optional.Some(false), }.ToConds()) if err != nil { return err From 767e9634d3d02acab27f05e1783391c9c7f6292e Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 23 Feb 2024 15:24:04 +0800 Subject: [PATCH 208/271] Allow options to disable user deletion from the interface on app.ini (#29275) Extract from #20549 This PR added a new option on app.ini `[admin]USER_DISABLED_FEATURES` to allow the site administrator to disable users visiting deletion user interface or allow. This options are also potentially allowed to define more features in future PRs. --------- Co-authored-by: wxiaoguang (cherry picked from commit 3ef6252e06a1f3981f8b7d1717bfc581418b1dc5) Conflicts: custom/conf/app.example.ini docs/content/administration/config-cheat-sheet.en-us.md modules/setting/admin.go context --- custom/conf/app.example.ini | 3 +++ modules/setting/admin.go | 10 +++++++++- routers/web/user/setting/account.go | 6 ++++++ templates/user/settings/account.tmpl | 23 ++++++++++++----------- 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/custom/conf/app.example.ini b/custom/conf/app.example.ini index dc1843097f..04714e5502 100644 --- a/custom/conf/app.example.ini +++ b/custom/conf/app.example.ini @@ -1492,6 +1492,9 @@ LEVEL = Info ;DEFAULT_EMAIL_NOTIFICATIONS = enabled ;; Send an email to all admins when a new user signs up to inform the admins about this act. Options: true, false ;SEND_NOTIFICATION_EMAIL_ON_NEW_USER = false +;; Disabled features for users, could be "deletion", more features can be disabled in future +;; - deletion: a user cannot delete their own account +;USER_DISABLED_FEATURES = ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/modules/setting/admin.go b/modules/setting/admin.go index d7f0ee827d..502efd0eb9 100644 --- a/modules/setting/admin.go +++ b/modules/setting/admin.go @@ -3,15 +3,23 @@ package setting +import "code.gitea.io/gitea/modules/container" + // Admin settings var Admin struct { DisableRegularOrgCreation bool DefaultEmailNotification string SendNotificationEmailOnNewUser bool + UserDisabledFeatures container.Set[string] } func loadAdminFrom(rootCfg ConfigProvider) { - mustMapSetting(rootCfg, "admin", &Admin) sec := rootCfg.Section("admin") + Admin.DisableRegularOrgCreation = sec.Key("DISABLE_REGULAR_ORG_CREATION").MustBool(false) Admin.DefaultEmailNotification = sec.Key("DEFAULT_EMAIL_NOTIFICATIONS").MustString("enabled") + Admin.UserDisabledFeatures = container.SetOf(sec.Key("USER_DISABLED_FEATURES").Strings(",")...) } + +const ( + UserFeatureDeletion = "deletion" +) diff --git a/routers/web/user/setting/account.go b/routers/web/user/setting/account.go index 371718ba23..6042e0b6cd 100644 --- a/routers/web/user/setting/account.go +++ b/routers/web/user/setting/account.go @@ -242,6 +242,11 @@ func DeleteEmail(ctx *context.Context) { // DeleteAccount render user suicide page and response for delete user himself func DeleteAccount(ctx *context.Context) { + if setting.Admin.UserDisabledFeatures.Contains(setting.UserFeatureDeletion) { + ctx.Error(http.StatusNotFound) + return + } + ctx.Data["Title"] = ctx.Tr("settings") ctx.Data["PageIsSettingsAccount"] = true @@ -308,6 +313,7 @@ func loadAccountData(ctx *context.Context) { ctx.Data["EmailNotificationsPreference"] = ctx.Doer.EmailNotificationsPreference ctx.Data["ActivationsPending"] = pendingActivation ctx.Data["CanAddEmails"] = !pendingActivation || !setting.Service.RegisterEmailConfirm + ctx.Data["UserDisabledFeatures"] = &setting.Admin.UserDisabledFeatures if setting.Service.UserDeleteWithCommentsMaxTime != 0 { ctx.Data["UserDeleteWithCommentsMaxTime"] = setting.Service.UserDeleteWithCommentsMaxTime.String() diff --git a/templates/user/settings/account.tmpl b/templates/user/settings/account.tmpl index 820d48a94b..c7bf3c0a41 100644 --- a/templates/user/settings/account.tmpl +++ b/templates/user/settings/account.tmpl @@ -128,6 +128,7 @@ {{end}}
      + {{if not ($.UserDisabledFeatures.Contains "deletion")}}

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

      @@ -151,7 +152,18 @@
      +

      + {{end}}
      - - {{template "user/settings/layout_footer" .}} From 826bf12bb499b16f6c9980c54a36c9de4bcd6529 Mon Sep 17 00:00:00 2001 From: Zettat123 Date: Sat, 24 Feb 2024 01:49:46 +0800 Subject: [PATCH 209/271] Fix tarball/zipball download bug (#29342) Fix #29249 ~~Use the `/repos/{owner}/{repo}/archive/{archive}` API to download.~~ Apply #26430 to archive download URLs. (cherry picked from commit b762a1f1b1f7941a7db2207552d7b441d868cbe9) --- services/auth/auth.go | 5 +++++ services/auth/oauth2.go | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/services/auth/auth.go b/services/auth/auth.go index 6e22d8298e..e53ff02dcf 100644 --- a/services/auth/auth.go +++ b/services/auth/auth.go @@ -40,6 +40,7 @@ func isContainerPath(req *http.Request) bool { var ( gitRawOrAttachPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/(?:(?:git-(?:(?:upload)|(?:receive))-pack$)|(?:info/refs$)|(?:HEAD$)|(?:objects/)|(?:raw/)|(?:releases/download/)|(?:attachments/))`) lfsPathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/info/lfs/`) + archivePathRe = regexp.MustCompile(`^/[a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+/archive/`) ) func isGitRawOrAttachPath(req *http.Request) bool { @@ -56,6 +57,10 @@ func isGitRawOrAttachOrLFSPath(req *http.Request) bool { return false } +func isArchivePath(req *http.Request) bool { + return archivePathRe.MatchString(req.URL.Path) +} + // handleSignIn clears existing session variables and stores new ones for the specified user object func handleSignIn(resp http.ResponseWriter, req *http.Request, sess SessionStore, user *user_model.User) { // We need to regenerate the session... diff --git a/services/auth/oauth2.go b/services/auth/oauth2.go index f2f7858a85..46d8510143 100644 --- a/services/auth/oauth2.go +++ b/services/auth/oauth2.go @@ -133,7 +133,7 @@ func (o *OAuth2) userIDFromToken(ctx context.Context, tokenSHA string, store Dat func (o *OAuth2) Verify(req *http.Request, w http.ResponseWriter, store DataStore, sess SessionStore) (*user_model.User, error) { // These paths are not API paths, but we still want to check for tokens because they maybe in the API returned URLs if !middleware.IsAPIPath(req) && !isAttachmentDownload(req) && !isAuthenticatedTokenRequest(req) && - !isGitRawOrAttachPath(req) { + !isGitRawOrAttachPath(req) && !isArchivePath(req) { return nil, nil } From 7bf905a43c8911e58a99eb8820e89a8caec5a38d Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Fri, 23 Feb 2024 23:19:54 +0200 Subject: [PATCH 210/271] Remove jQuery from the stopwatch (#29351) - Switched to plain JavaScript - Tested the stopwatch functionality and it works as before # Demo using JavaScript without jQuery ![action](https://github.com/go-gitea/gitea/assets/20454870/c8e9a401-45e5-4a1d-a683-0d655f1d570e) Signed-off-by: Yarden Shoham (cherry picked from commit 12d233faf786a54579a33b99b3cd56586c279f56) --- web_src/js/features/stopwatch.js | 38 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/web_src/js/features/stopwatch.js b/web_src/js/features/stopwatch.js index e20a983e60..d070f52d30 100644 --- a/web_src/js/features/stopwatch.js +++ b/web_src/js/features/stopwatch.js @@ -1,8 +1,9 @@ -import $ from 'jquery'; import prettyMilliseconds from 'pretty-ms'; import {createTippy} from '../modules/tippy.js'; +import {GET} from '../modules/fetch.js'; +import {hideElem, showElem} from '../utils/dom.js'; -const {appSubUrl, csrfToken, notificationSettings, enableTimeTracking, assetVersionEncoded} = window.config; +const {appSubUrl, notificationSettings, enableTimeTracking, assetVersionEncoded} = window.config; export function initStopwatch() { if (!enableTimeTracking) { @@ -28,7 +29,7 @@ export function initStopwatch() { }); // global stop watch (in the head_navbar), it should always work in any case either the EventSource or the PeriodicPoller is used. - const currSeconds = $('.stopwatch-time').attr('data-seconds'); + const currSeconds = document.querySelector('.stopwatch-time')?.getAttribute('data-seconds'); if (currSeconds) { updateStopwatchTime(currSeconds); } @@ -112,29 +113,31 @@ async function updateStopwatchWithCallback(callback, timeout) { } async function updateStopwatch() { - const data = await $.ajax({ - type: 'GET', - url: `${appSubUrl}/user/stopwatches`, - headers: {'X-Csrf-Token': csrfToken}, - }); + const response = await GET(`${appSubUrl}/user/stopwatches`); + if (!response.ok) { + console.error('Failed to fetch stopwatch data'); + return false; + } + const data = await response.json(); return updateStopwatchData(data); } function updateStopwatchData(data) { const watch = data[0]; - const btnEl = $('.active-stopwatch-trigger'); + const btnEl = document.querySelector('.active-stopwatch-trigger'); if (!watch) { clearStopwatchTimer(); - btnEl.addClass('gt-hidden'); + hideElem(btnEl); } else { const {repo_owner_name, repo_name, issue_index, seconds} = watch; const issueUrl = `${appSubUrl}/${repo_owner_name}/${repo_name}/issues/${issue_index}`; - $('.stopwatch-link').attr('href', issueUrl); - $('.stopwatch-commit').attr('action', `${issueUrl}/times/stopwatch/toggle`); - $('.stopwatch-cancel').attr('action', `${issueUrl}/times/stopwatch/cancel`); - $('.stopwatch-issue').text(`${repo_owner_name}/${repo_name}#${issue_index}`); + document.querySelector('.stopwatch-link')?.setAttribute('href', issueUrl); + document.querySelector('.stopwatch-commit')?.setAttribute('action', `${issueUrl}/times/stopwatch/toggle`); + document.querySelector('.stopwatch-cancel')?.setAttribute('action', `${issueUrl}/times/stopwatch/cancel`); + const stopwatchIssue = document.querySelector('.stopwatch-issue'); + if (stopwatchIssue) stopwatchIssue.textContent = `${repo_owner_name}/${repo_name}#${issue_index}`; updateStopwatchTime(seconds); - btnEl.removeClass('gt-hidden'); + showElem(btnEl); } return Boolean(data.length); } @@ -151,12 +154,13 @@ function updateStopwatchTime(seconds) { if (!Number.isFinite(secs)) return; clearStopwatchTimer(); - const $stopwatch = $('.stopwatch-time'); + const stopwatch = document.querySelector('.stopwatch-time'); + // TODO: replace with similar to how system status up time is shown const start = Date.now(); const updateUi = () => { const delta = Date.now() - start; const dur = prettyMilliseconds(secs * 1000 + delta, {compact: true}); - $stopwatch.text(dur); + if (stopwatch) stopwatch.textContent = dur; }; updateUi(); updateTimeIntervalId = setInterval(updateUi, 1000); From 7db422d98992a4aa9328b18b4ec6391dae4d3c0d Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Fri, 23 Feb 2024 22:51:46 +0100 Subject: [PATCH 211/271] Make optional.Option[T] type serializable (#29282) make the generic `Option` type de-/serializable for json and yaml --------- Co-authored-by: KN4CK3R (cherry picked from commit 53c7d8908e5ef35818b72b8c3d873b509269bc1a) --- modules/optional/option_test.go | 22 +-- modules/optional/serialization.go | 46 ++++++ modules/optional/serialization_test.go | 190 +++++++++++++++++++++++++ 3 files changed, 248 insertions(+), 10 deletions(-) create mode 100644 modules/optional/serialization.go create mode 100644 modules/optional/serialization_test.go diff --git a/modules/optional/option_test.go b/modules/optional/option_test.go index bfc4577dbe..410fd73577 100644 --- a/modules/optional/option_test.go +++ b/modules/optional/option_test.go @@ -1,47 +1,49 @@ // Copyright 2024 The Gitea Authors. All rights reserved. // SPDX-License-Identifier: MIT -package optional +package optional_test import ( "testing" + "code.gitea.io/gitea/modules/optional" + "github.com/stretchr/testify/assert" ) func TestOption(t *testing.T) { - var uninitialized Option[int] + var uninitialized optional.Option[int] assert.False(t, uninitialized.Has()) assert.Equal(t, int(0), uninitialized.Value()) assert.Equal(t, int(1), uninitialized.ValueOrDefault(1)) - none := None[int]() + none := optional.None[int]() assert.False(t, none.Has()) assert.Equal(t, int(0), none.Value()) assert.Equal(t, int(1), none.ValueOrDefault(1)) - some := Some[int](1) + some := optional.Some[int](1) assert.True(t, some.Has()) assert.Equal(t, int(1), some.Value()) assert.Equal(t, int(1), some.ValueOrDefault(2)) var ptr *int - assert.False(t, FromPtr(ptr).Has()) + assert.False(t, optional.FromPtr(ptr).Has()) int1 := 1 - opt1 := FromPtr(&int1) + opt1 := optional.FromPtr(&int1) assert.True(t, opt1.Has()) assert.Equal(t, int(1), opt1.Value()) - assert.False(t, FromNonDefault("").Has()) + assert.False(t, optional.FromNonDefault("").Has()) - opt2 := FromNonDefault("test") + opt2 := optional.FromNonDefault("test") assert.True(t, opt2.Has()) assert.Equal(t, "test", opt2.Value()) - assert.False(t, FromNonDefault(0).Has()) + assert.False(t, optional.FromNonDefault(0).Has()) - opt3 := FromNonDefault(1) + opt3 := optional.FromNonDefault(1) assert.True(t, opt3.Has()) assert.Equal(t, int(1), opt3.Value()) } diff --git a/modules/optional/serialization.go b/modules/optional/serialization.go new file mode 100644 index 0000000000..6688e78cd1 --- /dev/null +++ b/modules/optional/serialization.go @@ -0,0 +1,46 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package optional + +import ( + "code.gitea.io/gitea/modules/json" + + "gopkg.in/yaml.v3" +) + +func (o *Option[T]) UnmarshalJSON(data []byte) error { + var v *T + if err := json.Unmarshal(data, &v); err != nil { + return err + } + *o = FromPtr(v) + return nil +} + +func (o Option[T]) MarshalJSON() ([]byte, error) { + if !o.Has() { + return []byte("null"), nil + } + + return json.Marshal(o.Value()) +} + +func (o *Option[T]) UnmarshalYAML(value *yaml.Node) error { + var v *T + if err := value.Decode(&v); err != nil { + return err + } + *o = FromPtr(v) + return nil +} + +func (o Option[T]) MarshalYAML() (interface{}, error) { + if !o.Has() { + return nil, nil + } + + value := new(yaml.Node) + err := value.Encode(o.Value()) + return value, err +} diff --git a/modules/optional/serialization_test.go b/modules/optional/serialization_test.go new file mode 100644 index 0000000000..09a4bddea0 --- /dev/null +++ b/modules/optional/serialization_test.go @@ -0,0 +1,190 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package optional_test + +import ( + std_json "encoding/json" //nolint:depguard + "testing" + + "code.gitea.io/gitea/modules/json" + "code.gitea.io/gitea/modules/optional" + + "github.com/stretchr/testify/assert" + "gopkg.in/yaml.v3" +) + +type testSerializationStruct struct { + NormalString string `json:"normal_string" yaml:"normal_string"` + NormalBool bool `json:"normal_bool" yaml:"normal_bool"` + OptBool optional.Option[bool] `json:"optional_bool,omitempty" yaml:"optional_bool,omitempty"` + OptString optional.Option[string] `json:"optional_string,omitempty" yaml:"optional_string,omitempty"` + OptTwoBool optional.Option[bool] `json:"optional_two_bool" yaml:"optional_two_bool"` + OptTwoString optional.Option[string] `json:"optional_twostring" yaml:"optional_two_string"` +} + +func TestOptionalToJson(t *testing.T) { + tests := []struct { + name string + obj *testSerializationStruct + want string + }{ + { + name: "empty", + obj: new(testSerializationStruct), + want: `{"normal_string":"","normal_bool":false,"optional_two_bool":null,"optional_twostring":null}`, + }, + { + name: "some", + obj: &testSerializationStruct{ + NormalString: "a string", + NormalBool: true, + OptBool: optional.Some(false), + OptString: optional.Some(""), + OptTwoBool: optional.None[bool](), + OptTwoString: optional.None[string](), + }, + want: `{"normal_string":"a string","normal_bool":true,"optional_bool":false,"optional_string":"","optional_two_bool":null,"optional_twostring":null}`, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + b, err := json.Marshal(tc.obj) + assert.NoError(t, err) + assert.EqualValues(t, tc.want, string(b), "gitea json module returned unexpected") + + b, err = std_json.Marshal(tc.obj) + assert.NoError(t, err) + assert.EqualValues(t, tc.want, string(b), "std json module returned unexpected") + }) + } +} + +func TestOptionalFromJson(t *testing.T) { + tests := []struct { + name string + data string + want testSerializationStruct + }{ + { + name: "empty", + data: `{}`, + want: testSerializationStruct{ + NormalString: "", + }, + }, + { + name: "some", + data: `{"normal_string":"a string","normal_bool":true,"optional_bool":false,"optional_string":"","optional_two_bool":null,"optional_twostring":null}`, + want: testSerializationStruct{ + NormalString: "a string", + NormalBool: true, + OptBool: optional.Some(false), + OptString: optional.Some(""), + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var obj1 testSerializationStruct + err := json.Unmarshal([]byte(tc.data), &obj1) + assert.NoError(t, err) + assert.EqualValues(t, tc.want, obj1, "gitea json module returned unexpected") + + var obj2 testSerializationStruct + err = std_json.Unmarshal([]byte(tc.data), &obj2) + assert.NoError(t, err) + assert.EqualValues(t, tc.want, obj2, "std json module returned unexpected") + }) + } +} + +func TestOptionalToYaml(t *testing.T) { + tests := []struct { + name string + obj *testSerializationStruct + want string + }{ + { + name: "empty", + obj: new(testSerializationStruct), + want: `normal_string: "" +normal_bool: false +optional_two_bool: null +optional_two_string: null +`, + }, + { + name: "some", + obj: &testSerializationStruct{ + NormalString: "a string", + NormalBool: true, + OptBool: optional.Some(false), + OptString: optional.Some(""), + }, + want: `normal_string: a string +normal_bool: true +optional_bool: false +optional_string: "" +optional_two_bool: null +optional_two_string: null +`, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + b, err := yaml.Marshal(tc.obj) + assert.NoError(t, err) + assert.EqualValues(t, tc.want, string(b), "yaml module returned unexpected") + }) + } +} + +func TestOptionalFromYaml(t *testing.T) { + tests := []struct { + name string + data string + want testSerializationStruct + }{ + { + name: "empty", + data: ``, + want: testSerializationStruct{}, + }, + { + name: "empty but init", + data: `normal_string: "" +normal_bool: false +optional_bool: +optional_two_bool: +optional_two_string: +`, + want: testSerializationStruct{}, + }, + { + name: "some", + data: ` +normal_string: a string +normal_bool: true +optional_bool: false +optional_string: "" +optional_two_bool: null +optional_twostring: null +`, + want: testSerializationStruct{ + NormalString: "a string", + NormalBool: true, + OptBool: optional.Some(false), + OptString: optional.Some(""), + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var obj testSerializationStruct + err := yaml.Unmarshal([]byte(tc.data), &obj) + assert.NoError(t, err) + assert.EqualValues(t, tc.want, obj, "yaml module returned unexpected") + }) + } +} From 7143f8fcdd07571af748c1aa8b01bb2b636389e5 Mon Sep 17 00:00:00 2001 From: silverwind Date: Fri, 23 Feb 2024 23:07:27 +0100 Subject: [PATCH 212/271] Refactor generate-svg.js (#29348) Small refactor to avoid `process` global and to sync it with `generate-images`. (cherry picked from commit 08c1926e1c3e2487f207b5f225d8b0f2831d0708) --- build/generate-svg.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/build/generate-svg.js b/build/generate-svg.js index 1d92bc0b19..660ac9157e 100755 --- a/build/generate-svg.js +++ b/build/generate-svg.js @@ -4,15 +4,16 @@ import {optimize} from 'svgo'; import {parse} from 'node:path'; import {readFile, writeFile, mkdir} from 'node:fs/promises'; import {fileURLToPath} from 'node:url'; +import {exit} from 'node:process'; const glob = (pattern) => fastGlob.sync(pattern, { cwd: fileURLToPath(new URL('..', import.meta.url)), absolute: true, }); -function exit(err) { +function doExit(err) { if (err) console.error(err); - process.exit(err ? 1 : 0); + exit(err ? 1 : 0); } async function processFile(file, {prefix, fullName} = {}) { @@ -63,7 +64,7 @@ async function main() { } try { - exit(await main()); + doExit(await main()); } catch (err) { - exit(err); + doExit(err); } From fc384e494eae0264bf1c00c91567cacadd3ace8d Mon Sep 17 00:00:00 2001 From: Carlos Felgueiras Date: Sat, 24 Feb 2024 00:02:14 +0100 Subject: [PATCH 213/271] Fix validity of the FROM email address not being checked (#29347) Fixes #27188. Introduces a check on the installation that tries to parse the FROM address. If it fails, shows a new error message to the user. --------- Co-authored-by: KN4CK3R (cherry picked from commit 6f6120dfa8d549d0b866eeb9317054fea831c844) Conflicts: options/locale/locale_en-US.ini context --- options/locale/locale_en-US.ini | 1 + routers/install/install.go | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 10e96b4a27..7ffb6ef490 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -248,6 +248,7 @@ email_title = Email Settings smtp_addr = SMTP Host smtp_port = SMTP Port smtp_from = Send Email As +smtp_from_invalid = The "Send Email As" address is invalid smtp_from_helper = Email address Forgejo will use. Enter a plain email address or use the "Name" format. mailer_user = SMTP Username mailer_password = SMTP Password diff --git a/routers/install/install.go b/routers/install/install.go index 48271a64f2..13504953ce 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -7,6 +7,7 @@ package install import ( "fmt" "net/http" + "net/mail" "os" "os/exec" "path/filepath" @@ -423,6 +424,11 @@ func SubmitInstall(ctx *context.Context) { } if len(strings.TrimSpace(form.SMTPAddr)) > 0 { + if _, err := mail.ParseAddress(form.SMTPFrom); err != nil { + ctx.RenderWithErr(ctx.Tr("install.smtp_from_invalid"), tplInstall, &form) + return + } + cfg.Section("mailer").Key("ENABLED").SetValue("true") cfg.Section("mailer").Key("SMTP_ADDR").SetValue(form.SMTPAddr) cfg.Section("mailer").Key("SMTP_PORT").SetValue(form.SMTPPort) From f097799953c5f510b7e3314f1e3e115761f207d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Eahin=20Akkaya?= Date: Sat, 24 Feb 2024 02:41:24 +0300 Subject: [PATCH 214/271] Implement code frequency graph (#29191) ### Overview This is the implementation of Code Frequency page. This feature was mentioned on these issues: #18262, #7392. It adds another tab to Activity page called Code Frequency. Code Frequency tab shows additions and deletions over time since the repository existed. Before: image After: image --- #### Features - See additions deletions over time since repository existed - Click on "Additions" or "Deletions" legend to show only one type of contribution - Use the same cache from Contributors page so that the loading of data will be fast once it is cached by visiting either one of the pages --------- Co-authored-by: Giteabot (cherry picked from commit 875f5ea6d83c8371f309df99654ca3556623004c) --- options/locale/locale_en-US.ini | 2 + routers/web/repo/code_frequency.go | 41 +++++ routers/web/web.go | 4 + services/repository/contributors_graph.go | 2 - templates/repo/activity.tmpl | 1 + templates/repo/code_frequency.tmpl | 9 + templates/repo/navbar.tmpl | 3 + web_src/js/components/RepoCodeFrequency.vue | 172 ++++++++++++++++++++ web_src/js/components/RepoContributors.vue | 36 +--- web_src/js/features/code-frequency.js | 21 +++ web_src/js/index.js | 2 + web_src/js/utils.js | 2 + web_src/js/utils/color.js | 14 ++ 13 files changed, 277 insertions(+), 32 deletions(-) create mode 100644 routers/web/repo/code_frequency.go create mode 100644 templates/repo/code_frequency.tmpl create mode 100644 web_src/js/components/RepoCodeFrequency.vue create mode 100644 web_src/js/features/code-frequency.js diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 7ffb6ef490..d4a5ca4517 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1962,6 +1962,7 @@ wiki.original_git_entry_tooltip = View original Git file instead of using friend activity = Activity activity.navbar.pulse = Pulse activity.navbar.contributors = Contributors +activity.navbar.code_frequency = Code Frequency activity.period.filter_label = Period: activity.period.daily = 1 day activity.period.halfweekly = 3 days @@ -2656,6 +2657,7 @@ component_loading = Loading %s... component_loading_failed = Could not load %s component_loading_info = This might take a bit… component_failed_to_load = An unexpected error happened. +code_frequency.what = code frequency contributors.what = contributions [org] diff --git a/routers/web/repo/code_frequency.go b/routers/web/repo/code_frequency.go new file mode 100644 index 0000000000..48ade655b7 --- /dev/null +++ b/routers/web/repo/code_frequency.go @@ -0,0 +1,41 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "errors" + "net/http" + + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + contributors_service "code.gitea.io/gitea/services/repository" +) + +const ( + tplCodeFrequency base.TplName = "repo/activity" +) + +// CodeFrequency renders the page to show repository code frequency +func CodeFrequency(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.activity.navbar.code_frequency") + + ctx.Data["PageIsActivity"] = true + ctx.Data["PageIsCodeFrequency"] = true + ctx.PageData["repoLink"] = ctx.Repo.RepoLink + + ctx.HTML(http.StatusOK, tplCodeFrequency) +} + +// CodeFrequencyData returns JSON of code frequency data +func CodeFrequencyData(ctx *context.Context) { + if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.CommitID); err != nil { + if errors.Is(err, contributors_service.ErrAwaitGeneration) { + ctx.Status(http.StatusAccepted) + return + } + ctx.ServerError("GetCodeFrequencyData", err) + } else { + ctx.JSON(http.StatusOK, contributorStats["total"].Weeks) + } +} diff --git a/routers/web/web.go b/routers/web/web.go index 38cf5e92bc..cef563f615 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1448,6 +1448,10 @@ func registerRoutes(m *web.Route) { m.Get("", repo.Contributors) m.Get("/data", repo.ContributorsData) }) + m.Group("/code-frequency", func() { + m.Get("", repo.CodeFrequency) + m.Get("/data", repo.CodeFrequencyData) + }) }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases)) m.Group("/activity_author_data", func() { diff --git a/services/repository/contributors_graph.go b/services/repository/contributors_graph.go index 8421df8e3a..7c9f535ae0 100644 --- a/services/repository/contributors_graph.go +++ b/services/repository/contributors_graph.go @@ -143,7 +143,6 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int PipelineFunc: func(ctx context.Context, cancel context.CancelFunc) error { _ = stdoutWriter.Close() scanner := bufio.NewScanner(stdoutReader) - scanner.Split(bufio.ScanLines) for scanner.Scan() { line := strings.TrimSpace(scanner.Text()) @@ -180,7 +179,6 @@ func getExtendedCommitStats(repo *git.Repository, revision string /*, limit int } } commitStats.Total = commitStats.Additions + commitStats.Deletions - scanner.Scan() scanner.Text() // empty line at the end res := &ExtendedCommitStats{ diff --git a/templates/repo/activity.tmpl b/templates/repo/activity.tmpl index 960083d2fb..94f52b0e26 100644 --- a/templates/repo/activity.tmpl +++ b/templates/repo/activity.tmpl @@ -8,6 +8,7 @@
      {{if .PageIsPulse}}{{template "repo/pulse" .}}{{end}} {{if .PageIsContributors}}{{template "repo/contributors" .}}{{end}} + {{if .PageIsCodeFrequency}}{{template "repo/code_frequency" .}}{{end}}
      diff --git a/templates/repo/code_frequency.tmpl b/templates/repo/code_frequency.tmpl new file mode 100644 index 0000000000..50ec1beb6b --- /dev/null +++ b/templates/repo/code_frequency.tmpl @@ -0,0 +1,9 @@ +{{if .Permission.CanRead $.UnitTypeCode}} +
      +
      +{{end}} diff --git a/templates/repo/navbar.tmpl b/templates/repo/navbar.tmpl index a9042ee30d..aa5021e73a 100644 --- a/templates/repo/navbar.tmpl +++ b/templates/repo/navbar.tmpl @@ -5,4 +5,7 @@ {{ctx.Locale.Tr "repo.activity.navbar.contributors"}} + + {{ctx.Locale.Tr "repo.activity.navbar.code_frequency"}} +
      diff --git a/web_src/js/components/RepoCodeFrequency.vue b/web_src/js/components/RepoCodeFrequency.vue new file mode 100644 index 0000000000..ad607a041a --- /dev/null +++ b/web_src/js/components/RepoCodeFrequency.vue @@ -0,0 +1,172 @@ + + + diff --git a/web_src/js/components/RepoContributors.vue b/web_src/js/components/RepoContributors.vue index fa1545b3df..84fdcae1f6 100644 --- a/web_src/js/components/RepoContributors.vue +++ b/web_src/js/components/RepoContributors.vue @@ -3,10 +3,7 @@ import {SvgIcon} from '../svg.js'; import { Chart, Title, - Tooltip, - Legend, BarElement, - CategoryScale, LinearScale, TimeScale, PointElement, @@ -21,27 +18,13 @@ import { firstStartDateAfterDate, fillEmptyStartDaysWithZeroes, } from '../utils/time.js'; +import {chartJsColors} from '../utils/color.js'; +import {sleep} from '../utils.js'; import 'chartjs-adapter-dayjs-4/dist/chartjs-adapter-dayjs-4.esm'; import $ from 'jquery'; const {pageData} = window.config; -const colors = { - text: '--color-text', - border: '--color-secondary-alpha-60', - commits: '--color-primary-alpha-60', - additions: '--color-green', - deletions: '--color-red', - title: '--color-secondary-dark-4', -}; - -const styles = window.getComputedStyle(document.documentElement); -const getColor = (name) => styles.getPropertyValue(name).trim(); - -for (const [key, value] of Object.entries(colors)) { - colors[key] = getColor(value); -} - const customEventListener = { id: 'customEventListener', afterEvent: (chart, args, opts) => { @@ -54,17 +37,14 @@ const customEventListener = { } }; -Chart.defaults.color = colors.text; -Chart.defaults.borderColor = colors.border; +Chart.defaults.color = chartJsColors.text; +Chart.defaults.borderColor = chartJsColors.border; Chart.register( TimeScale, - CategoryScale, LinearScale, BarElement, Title, - Tooltip, - Legend, PointElement, LineElement, Filler, @@ -122,7 +102,7 @@ export default { do { response = await GET(`${this.repoLink}/activity/contributors/data`); if (response.status === 202) { - await new Promise((resolve) => setTimeout(resolve, 1000)); // wait for 1 second before retrying + await sleep(1000); // wait for 1 second before retrying } } while (response.status === 202); if (response.ok) { @@ -222,7 +202,7 @@ export default { pointRadius: 0, pointHitRadius: 0, fill: 'start', - backgroundColor: colors[this.type], + backgroundColor: chartJsColors[this.type], borderWidth: 0, tension: 0.3, }, @@ -254,7 +234,6 @@ export default { title: { display: type === 'main', text: 'drag: zoom, shift+drag: pan, double click: reset zoom', - color: colors.title, position: 'top', align: 'center', }, @@ -262,9 +241,6 @@ export default { chartType: type, instance: this, }, - legend: { - display: false, - }, zoom: { pan: { enabled: true, diff --git a/web_src/js/features/code-frequency.js b/web_src/js/features/code-frequency.js new file mode 100644 index 0000000000..103d82f6e3 --- /dev/null +++ b/web_src/js/features/code-frequency.js @@ -0,0 +1,21 @@ +import {createApp} from 'vue'; + +export async function initRepoCodeFrequency() { + const el = document.getElementById('repo-code-frequency-chart'); + if (!el) return; + + const {default: RepoCodeFrequency} = await import(/* webpackChunkName: "code-frequency-graph" */'../components/RepoCodeFrequency.vue'); + try { + const View = createApp(RepoCodeFrequency, { + locale: { + loadingTitle: el.getAttribute('data-locale-loading-title'), + loadingTitleFailed: el.getAttribute('data-locale-loading-title-failed'), + loadingInfo: el.getAttribute('data-locale-loading-info'), + } + }); + View.mount(el); + } catch (err) { + console.error('RepoCodeFrequency failed to load', err); + el.textContent = el.getAttribute('data-locale-component-failed-to-load'); + } +} diff --git a/web_src/js/index.js b/web_src/js/index.js index 117279c3c4..d9cfff4084 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -84,6 +84,7 @@ import {onDomReady} from './utils/dom.js'; import {initRepoIssueList} from './features/repo-issue-list.js'; import {initCommonIssueListQuickGoto} from './features/common-issue-list.js'; import {initRepoContributors} from './features/contributors.js'; +import {initRepoCodeFrequency} from './features/code-frequency.js'; import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js'; import {initDirAuto} from './modules/dirauto.js'; @@ -174,6 +175,7 @@ onDomReady(() => { initRepository(); initRepositoryActionView(); initRepoContributors(); + initRepoCodeFrequency(); initCommitStatuses(); initCaptcha(); diff --git a/web_src/js/utils.js b/web_src/js/utils.js index c82e42d349..3a2694335f 100644 --- a/web_src/js/utils.js +++ b/web_src/js/utils.js @@ -139,3 +139,5 @@ export function parseDom(text, contentType) { export function serializeXml(node) { return xmlSerializer.serializeToString(node); } + +export const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); diff --git a/web_src/js/utils/color.js b/web_src/js/utils/color.js index 5d9c4ca45d..0ba6af49ee 100644 --- a/web_src/js/utils/color.js +++ b/web_src/js/utils/color.js @@ -19,3 +19,17 @@ function getLuminance(r, g, b) { export function useLightTextOnBackground(r, g, b) { return getLuminance(r, g, b) < 0.453; } + +function resolveColors(obj) { + const styles = window.getComputedStyle(document.documentElement); + const getColor = (name) => styles.getPropertyValue(name).trim(); + return Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, getColor(value)])); +} + +export const chartJsColors = resolveColors({ + text: '--color-text', + border: '--color-secondary-alpha-60', + commits: '--color-primary-alpha-60', + additions: '--color-green', + deletions: '--color-red', +}); From 69055400881777148d5e5d3f531c563ebfdb9287 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 24 Feb 2024 14:55:19 +0800 Subject: [PATCH 215/271] Use the database object format name but not read from git repoisitory everytime and fix possible migration wrong objectformat when migrating a sha256 repository (#29294) Now we can get object format name from git command line or from the database repository table. Assume the column is right, we don't need to read from git command line every time. This also fixed a possible bug that the object format is wrong when migrating a sha256 repository from external. image (cherry picked from commit b79c30435f439af8243ee281310258cdf141e27b) Conflicts: routers/web/repo/blame.go services/agit/agit.go context --- modules/context/api.go | 9 ++------- modules/context/repo.go | 20 ++++++++------------ routers/api/v1/utils/git.go | 2 +- routers/private/hook_pre_receive.go | 2 +- routers/web/repo/blame.go | 5 +---- routers/web/repo/compare.go | 4 ++-- routers/web/repo/setting/lfs.go | 2 +- services/agit/agit.go | 5 +---- services/migrations/gitea_uploader.go | 16 +++++++++++++--- services/pull/check.go | 5 +---- services/pull/merge.go | 2 +- services/release/release.go | 2 +- services/repository/branch.go | 7 ++----- services/repository/files/commit.go | 6 ++---- services/repository/files/tree.go | 2 +- services/repository/lfs.go | 2 +- services/repository/push.go | 6 +----- 17 files changed, 40 insertions(+), 57 deletions(-) diff --git a/modules/context/api.go b/modules/context/api.go index 7557a5f435..fafd49fd42 100644 --- a/modules/context/api.go +++ b/modules/context/api.go @@ -309,12 +309,6 @@ func RepoRefForAPI(next http.Handler) http.Handler { return } - objectFormat, err := ctx.Repo.GitRepo.GetObjectFormat() - if err != nil { - ctx.Error(http.StatusInternalServerError, "GetCommit", err) - return - } - if ref := ctx.FormTrim("ref"); len(ref) > 0 { commit, err := ctx.Repo.GitRepo.GetCommit(ref) if err != nil { @@ -333,6 +327,7 @@ func RepoRefForAPI(next http.Handler) http.Handler { } refName := getRefName(ctx.Base, ctx.Repo, RepoRefAny) + var err error if ctx.Repo.GitRepo.IsBranchExist(refName) { ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetBranchCommit(refName) @@ -348,7 +343,7 @@ func RepoRefForAPI(next http.Handler) http.Handler { return } ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() - } else if len(refName) == objectFormat.FullLength() { + } else if len(refName) == ctx.Repo.GetObjectFormat().FullLength() { ctx.Repo.CommitID = refName ctx.Repo.Commit, err = ctx.Repo.GitRepo.GetCommit(refName) if err != nil { diff --git a/modules/context/repo.go b/modules/context/repo.go index 490d798d50..e8ed1134fe 100644 --- a/modules/context/repo.go +++ b/modules/context/repo.go @@ -83,6 +83,10 @@ func (r *Repository) CanCreateBranch() bool { return r.Permission.CanWrite(unit_model.TypeCode) && r.Repository.CanCreateBranch() } +func (r *Repository) GetObjectFormat() git.ObjectFormat { + return git.ObjectFormatFromName(r.Repository.ObjectFormatName) +} + // RepoMustNotBeArchived checks if a repo is archived func RepoMustNotBeArchived() func(ctx *Context) { return func(ctx *Context) { @@ -831,9 +835,8 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string { } // For legacy and API support only full commit sha parts := strings.Split(path, "/") - objectFormat, _ := repo.GitRepo.GetObjectFormat() - if len(parts) > 0 && len(parts[0]) == objectFormat.FullLength() { + if len(parts) > 0 && len(parts[0]) == git.ObjectFormatFromName(repo.Repository.ObjectFormatName).FullLength() { repo.TreePath = strings.Join(parts[1:], "/") return parts[0] } @@ -877,9 +880,8 @@ func getRefName(ctx *Base, repo *Repository, pathType RepoRefType) string { return getRefNameFromPath(ctx, repo, path, repo.GitRepo.IsTagExist) case RepoRefCommit: parts := strings.Split(path, "/") - objectFormat, _ := repo.GitRepo.GetObjectFormat() - if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= objectFormat.FullLength() { + if len(parts) > 0 && len(parts[0]) >= 7 && len(parts[0]) <= repo.GetObjectFormat().FullLength() { repo.TreePath = strings.Join(parts[1:], "/") return parts[0] } @@ -938,12 +940,6 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context } } - objectFormat, err := ctx.Repo.GitRepo.GetObjectFormat() - if err != nil { - log.Error("Cannot determine objectFormat for repository: %w", err) - ctx.Repo.Repository.MarkAsBrokenEmpty() - } - // Get default branch. if len(ctx.Params("*")) == 0 { refName = ctx.Repo.Repository.DefaultBranch @@ -1010,7 +1006,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context return cancel } ctx.Repo.CommitID = ctx.Repo.Commit.ID.String() - } else if len(refName) >= 7 && len(refName) <= objectFormat.FullLength() { + } else if len(refName) >= 7 && len(refName) <= ctx.Repo.GetObjectFormat().FullLength() { ctx.Repo.IsViewCommit = true ctx.Repo.CommitID = refName @@ -1020,7 +1016,7 @@ func RepoRefByType(refType RepoRefType, ignoreNotExistErr ...bool) func(*Context return cancel } // If short commit ID add canonical link header - if len(refName) < objectFormat.FullLength() { + if len(refName) < ctx.Repo.GetObjectFormat().FullLength() { ctx.RespHeader().Set("Link", fmt.Sprintf("<%s>; rel=\"canonical\"", util.URLJoin(setting.AppURL, strings.Replace(ctx.Req.URL.RequestURI(), util.PathEscapeSegments(refName), url.PathEscape(ctx.Repo.Commit.ID.String()), 1)))) } diff --git a/routers/api/v1/utils/git.go b/routers/api/v1/utils/git.go index 2299cdc247..5e80190017 100644 --- a/routers/api/v1/utils/git.go +++ b/routers/api/v1/utils/git.go @@ -72,7 +72,7 @@ func searchRefCommitByType(ctx *context.APIContext, refType, filter string) (str // ConvertToObjectID returns a full-length SHA1 from a potential ID string func ConvertToObjectID(ctx gocontext.Context, repo *context.Repository, commitID string) (git.ObjectID, error) { - objectFormat, _ := repo.GitRepo.GetObjectFormat() + objectFormat := repo.GetObjectFormat() if len(commitID) == objectFormat.FullLength() && objectFormat.IsValid(commitID) { sha, err := git.NewIDFromString(commitID) if err == nil { diff --git a/routers/private/hook_pre_receive.go b/routers/private/hook_pre_receive.go index 90d8287f06..f28ae4c0eb 100644 --- a/routers/private/hook_pre_receive.go +++ b/routers/private/hook_pre_receive.go @@ -145,7 +145,7 @@ func preReceiveBranch(ctx *preReceiveContext, oldCommitID, newCommitID string, r repo := ctx.Repo.Repository gitRepo := ctx.Repo.GitRepo - objectFormat, _ := gitRepo.GetObjectFormat() + objectFormat := ctx.Repo.GetObjectFormat() if branchName == repo.DefaultBranch && newCommitID == objectFormat.EmptyObjectID().String() { log.Warn("Forbidden: Branch: %s is the default branch in %-v and cannot be deleted", branchName, repo) diff --git a/routers/web/repo/blame.go b/routers/web/repo/blame.go index 49443162e4..2c938dc966 100644 --- a/routers/web/repo/blame.go +++ b/routers/web/repo/blame.go @@ -112,10 +112,7 @@ type blameResult struct { func performBlame(ctx *context.Context, commit *git.Commit, file string, bypassBlameIgnore bool) (*blameResult, error) { repoPath := ctx.Repo.Repository.RepoPath() - objectFormat, err := ctx.Repo.GitRepo.GetObjectFormat() - if err != nil { - return nil, err - } + objectFormat := ctx.Repo.GetObjectFormat() blameReader, err := git.CreateBlameReader(ctx, objectFormat, repoPath, commit, file, bypassBlameIgnore) if err != nil { diff --git a/routers/web/repo/compare.go b/routers/web/repo/compare.go index df41c750de..535487d5fd 100644 --- a/routers/web/repo/compare.go +++ b/routers/web/repo/compare.go @@ -312,14 +312,14 @@ func ParseCompareInfo(ctx *context.Context) *CompareInfo { baseIsCommit := ctx.Repo.GitRepo.IsCommitExist(ci.BaseBranch) baseIsBranch := ctx.Repo.GitRepo.IsBranchExist(ci.BaseBranch) baseIsTag := ctx.Repo.GitRepo.IsTagExist(ci.BaseBranch) - objectFormat, _ := ctx.Repo.GitRepo.GetObjectFormat() + if !baseIsCommit && !baseIsBranch && !baseIsTag { // Check if baseBranch is short sha commit hash if baseCommit, _ := ctx.Repo.GitRepo.GetCommit(ci.BaseBranch); baseCommit != nil { ci.BaseBranch = baseCommit.ID.String() ctx.Data["BaseBranch"] = ci.BaseBranch baseIsCommit = true - } else if ci.BaseBranch == objectFormat.EmptyObjectID().String() { + } else if ci.BaseBranch == ctx.Repo.GetObjectFormat().EmptyObjectID().String() { if isSameRepo { ctx.Redirect(ctx.Repo.RepoLink + "/compare/" + util.PathEscapeSegments(ci.HeadBranch)) } else { diff --git a/routers/web/repo/setting/lfs.go b/routers/web/repo/setting/lfs.go index e360ae2b8c..53e7b22d1b 100644 --- a/routers/web/repo/setting/lfs.go +++ b/routers/web/repo/setting/lfs.go @@ -388,7 +388,7 @@ func LFSFileFind(ctx *context.Context) { sha := ctx.FormString("sha") ctx.Data["Title"] = oid ctx.Data["PageIsSettingsLFS"] = true - objectFormat, _ := ctx.Repo.GitRepo.GetObjectFormat() + objectFormat := ctx.Repo.GetObjectFormat() var objectID git.ObjectID if len(sha) == 0 { pointer := lfs.Pointer{Oid: oid, Size: size} diff --git a/services/agit/agit.go b/services/agit/agit.go index 4c970ee78a..e46a5771e1 100644 --- a/services/agit/agit.go +++ b/services/agit/agit.go @@ -28,10 +28,7 @@ func ProcReceive(ctx context.Context, repo *repo_model.Repository, gitRepo *git. title, hasTitle := opts.GitPushOptions["title"] description, hasDesc := opts.GitPushOptions["description"] - objectFormat, err := gitRepo.GetObjectFormat() - if err != nil { - return nil, fmt.Errorf("couldn't get object format of the repository: %w", err) - } + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) pusher, err := user_model.GetUserByID(ctx, opts.UserID) if err != nil { diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index ebb1313c2b..7920c07056 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -140,8 +140,18 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate if err != nil { return err } - g.gitRepo, err = gitrepo.OpenRepository(g.ctx, r) - return err + g.gitRepo, err = gitrepo.OpenRepository(g.ctx, g.repo) + if err != nil { + return err + } + + // detect object format from git repository and update to database + objectFormat, err := g.gitRepo.GetObjectFormat() + if err != nil { + return err + } + g.repo.ObjectFormatName = objectFormat.Name() + return repo_model.UpdateRepositoryCols(g.ctx, g.repo, "object_format_name") } // Close closes this uploader @@ -901,7 +911,7 @@ func (g *GiteaLocalUploader) CreateReviews(reviews ...*base.Review) error { comment.UpdatedAt = comment.CreatedAt } - objectFormat, _ := g.gitRepo.GetObjectFormat() + objectFormat := git.ObjectFormatFromName(g.repo.ObjectFormatName) if !objectFormat.IsValid(comment.CommitID) { log.Warn("Invalid comment CommitID[%s] on comment[%d] in PR #%d of %s/%s replaced with %s", comment.CommitID, pr.Index, g.repoOwner, g.repoName, headCommitID) comment.CommitID = headCommitID diff --git a/services/pull/check.go b/services/pull/check.go index dd6c3ed230..f4dd332b14 100644 --- a/services/pull/check.go +++ b/services/pull/check.go @@ -222,10 +222,7 @@ func getMergeCommit(ctx context.Context, pr *issues_model.PullRequest) (*git.Com } defer gitRepo.Close() - objectFormat, err := gitRepo.GetObjectFormat() - if err != nil { - return nil, fmt.Errorf("%-v GetObjectFormat: %w", pr.BaseRepo, err) - } + objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) // Get the commit from BaseBranch where the pull request got merged mergeCommit, _, err := git.NewCommand(ctx, "rev-list", "--ancestry-path", "--merges", "--reverse"). diff --git a/services/pull/merge.go b/services/pull/merge.go index f09067996c..2112108d45 100644 --- a/services/pull/merge.go +++ b/services/pull/merge.go @@ -503,7 +503,7 @@ func MergedManually(ctx context.Context, pr *issues_model.PullRequest, doer *use return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: repo_model.MergeStyleManuallyMerged} } - objectFormat, _ := baseGitRepo.GetObjectFormat() + objectFormat := git.ObjectFormatFromName(pr.BaseRepo.ObjectFormatName) if len(commitID) != objectFormat.FullLength() { return fmt.Errorf("Wrong commit ID") } diff --git a/services/release/release.go b/services/release/release.go index 4c522c18be..a359e5078e 100644 --- a/services/release/release.go +++ b/services/release/release.go @@ -88,7 +88,7 @@ func createTag(ctx context.Context, gitRepo *git.Repository, rel *repo_model.Rel created = true rel.LowerTagName = strings.ToLower(rel.TagName) - objectFormat, _ := gitRepo.GetObjectFormat() + objectFormat := git.ObjectFormatFromName(rel.Repo.ObjectFormatName) commits := repository.NewPushCommits() commits.HeadCommit = repository.CommitToPushCommit(commit) commits.CompareURL = rel.Repo.ComposeCompareURL(objectFormat.EmptyObjectID().String(), commit.ID.String()) diff --git a/services/repository/branch.go b/services/repository/branch.go index 6add9422f4..39be3984ae 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -369,11 +369,6 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R return fmt.Errorf("GetBranch: %v", err) } - objectFormat, err := gitRepo.GetObjectFormat() - if err != nil { - return err - } - if rawBranch.IsDeleted { return nil } @@ -395,6 +390,8 @@ func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.R return err } + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) + // Don't return error below this if err := PushUpdate( &repo_module.PushUpdateOptions{ diff --git a/services/repository/files/commit.go b/services/repository/files/commit.go index 16a15e06a7..512aec7c81 100644 --- a/services/repository/files/commit.go +++ b/services/repository/files/commit.go @@ -30,10 +30,8 @@ func CreateCommitStatus(ctx context.Context, repo *repo_model.Repository, creato } defer closer.Close() - objectFormat, err := gitRepo.GetObjectFormat() - if err != nil { - return fmt.Errorf("GetObjectFormat[%s]: %w", repoPath, err) - } + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) + commit, err := gitRepo.GetCommit(sha) if err != nil { gitRepo.Close() diff --git a/services/repository/files/tree.go b/services/repository/files/tree.go index 9d3185c3fc..e3a7f3b8b0 100644 --- a/services/repository/files/tree.go +++ b/services/repository/files/tree.go @@ -37,7 +37,7 @@ func GetTreeBySHA(ctx context.Context, repo *repo_model.Repository, gitRepo *git } apiURL := repo.APIURL() apiURLLen := len(apiURL) - objectFormat, _ := gitRepo.GetObjectFormat() + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) hashLen := objectFormat.FullLength() const gitBlobsPath = "/git/blobs/" diff --git a/services/repository/lfs.go b/services/repository/lfs.go index 4504f796bd..4d48881b87 100644 --- a/services/repository/lfs.go +++ b/services/repository/lfs.go @@ -79,7 +79,7 @@ func GarbageCollectLFSMetaObjectsForRepo(ctx context.Context, repo *repo_model.R store := lfs.NewContentStore() errStop := errors.New("STOPERR") - objectFormat, _ := gitRepo.GetObjectFormat() + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) err = git_model.IterateLFSMetaObjectsForRepo(ctx, repo.ID, func(ctx context.Context, metaObject *git_model.LFSMetaObject, count int64) error { if opts.NumberToCheckPerRepo > 0 && total > opts.NumberToCheckPerRepo { diff --git a/services/repository/push.go b/services/repository/push.go index 5e2853b27d..bb080e30cc 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -93,11 +93,6 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } defer gitRepo.Close() - objectFormat, err := gitRepo.GetObjectFormat() - if err != nil { - return fmt.Errorf("unknown repository ObjectFormat [%s]: %w", repo.FullName(), err) - } - if err = repo_module.UpdateRepoSize(ctx, repo); err != nil { return fmt.Errorf("Failed to update size for repository: %v", err) } @@ -105,6 +100,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { addTags := make([]string, 0, len(optsList)) delTags := make([]string, 0, len(optsList)) var pusher *user_model.User + objectFormat := git.ObjectFormatFromName(repo.ObjectFormatName) for _, opts := range optsList { log.Trace("pushUpdates: %-v %s %s %s", repo, opts.OldCommitID, opts.NewCommitID, opts.RefFullName) From f0acc71ba13713f07602294b4a33028125343d47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Br=C3=BCckner?= Date: Sat, 24 Feb 2024 07:49:16 +0000 Subject: [PATCH 216/271] Properly migrate target branch change GitLab comment (#29340) GitLab generates "system notes" whenever an event happens within the platform. Unlike Gitea, those events are stored and retrieved as text comments with no semantic details. The only way to tell whether a comment was generated in this manner is the `system` flag on the note type. This PR adds detection for a new specific kind of event: Changing the target branch of a PR. When detected, it is downloaded using Gitea's type for this event, and eventually uploaded into Gitea in the expected format, i.e. with no text content in the comment. This PR also updates the template used to render comments to add support for migrated comments of this type. ref: https://gitlab.com/gitlab-org/gitlab/-/blob/11bd6dc826e0bea2832324a1d7356949a9398884/app/services/system_notes/merge_requests_service.rb#L102 (cherry picked from commit 6e5966597c2d498d1a8540dad965461d44ff8e57) --- services/migrations/gitea_uploader.go | 10 ++++++++-- services/migrations/gitlab.go | 10 +++++++++- services/migrations/gitlab_test.go | 19 ++++++++++++++++++- .../repo/issue/view_content/comments.tmpl | 19 +++++++++++++++---- 4 files changed, 50 insertions(+), 8 deletions(-) diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 7920c07056..99a44dff0f 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -492,10 +492,16 @@ func (g *GiteaLocalUploader) CreateComments(comments ...*base.Comment) error { } case issues_model.CommentTypeChangeTitle: if comment.Meta["OldTitle"] != nil { - cm.OldTitle = fmt.Sprintf("%s", comment.Meta["OldTitle"]) + cm.OldTitle = fmt.Sprint(comment.Meta["OldTitle"]) } if comment.Meta["NewTitle"] != nil { - cm.NewTitle = fmt.Sprintf("%s", comment.Meta["NewTitle"]) + cm.NewTitle = fmt.Sprint(comment.Meta["NewTitle"]) + } + case issues_model.CommentTypeChangeTargetBranch: + if comment.Meta["OldRef"] != nil && comment.Meta["NewRef"] != nil { + cm.OldRef = fmt.Sprint(comment.Meta["OldRef"]) + cm.NewRef = fmt.Sprint(comment.Meta["NewRef"]) + cm.Content = "" } case issues_model.CommentTypePRScheduledToAutoMerge, issues_model.CommentTypePRUnScheduledToAutoMerge: cm.Content = "" diff --git a/services/migrations/gitlab.go b/services/migrations/gitlab.go index d08eaf0f84..5e49ae6d57 100644 --- a/services/migrations/gitlab.go +++ b/services/migrations/gitlab.go @@ -11,6 +11,7 @@ import ( "net/http" "net/url" "path" + "regexp" "strings" "time" @@ -519,6 +520,8 @@ func (g *GitlabDownloader) GetComments(commentable base.Commentable) ([]*base.Co return allComments, true, nil } +var targetBranchChangeRegexp = regexp.MustCompile("^changed target branch from `(.*?)` to `(.*?)`$") + func (g *GitlabDownloader) convertNoteToComment(localIndex int64, note *gitlab.Note) *base.Comment { comment := &base.Comment{ IssueIndex: localIndex, @@ -528,11 +531,16 @@ func (g *GitlabDownloader) convertNoteToComment(localIndex int64, note *gitlab.N PosterEmail: note.Author.Email, Content: note.Body, Created: *note.CreatedAt, + Meta: map[string]any{}, } // Try to find the underlying event of system notes. if note.System { - if strings.HasPrefix(note.Body, "enabled an automatic merge") { + if match := targetBranchChangeRegexp.FindStringSubmatch(note.Body); match != nil { + comment.CommentType = issues_model.CommentTypeChangeTargetBranch.String() + comment.Meta["OldRef"] = match[1] + comment.Meta["NewRef"] = match[2] + } else if strings.HasPrefix(note.Body, "enabled an automatic merge") { comment.CommentType = issues_model.CommentTypePRScheduledToAutoMerge.String() } else if note.Body == "canceled the automatic merge" { comment.CommentType = issues_model.CommentTypePRUnScheduledToAutoMerge.String() diff --git a/services/migrations/gitlab_test.go b/services/migrations/gitlab_test.go index d941cebd2c..6e5ab86720 100644 --- a/services/migrations/gitlab_test.go +++ b/services/migrations/gitlab_test.go @@ -586,7 +586,8 @@ func TestNoteToComment(t *testing.T) { notes := []gitlab.Note{ makeTestNote(1, "This is a regular comment", false), makeTestNote(2, "enabled an automatic merge for abcd1234", true), - makeTestNote(3, "canceled the automatic merge", true), + makeTestNote(3, "changed target branch from `master` to `main`", true), + makeTestNote(4, "canceled the automatic merge", true), } comments := []base.Comment{{ IssueIndex: 17, @@ -597,6 +598,7 @@ func TestNoteToComment(t *testing.T) { CommentType: "", Content: "This is a regular comment", Created: now, + Meta: map[string]any{}, }, { IssueIndex: 17, Index: 2, @@ -606,15 +608,30 @@ func TestNoteToComment(t *testing.T) { CommentType: "pull_scheduled_merge", Content: "enabled an automatic merge for abcd1234", Created: now, + Meta: map[string]any{}, }, { IssueIndex: 17, Index: 3, PosterID: 72, PosterName: "test", PosterEmail: "test@example.com", + CommentType: "change_target_branch", + Content: "changed target branch from `master` to `main`", + Created: now, + Meta: map[string]any{ + "OldRef": "master", + "NewRef": "main", + }, + }, { + IssueIndex: 17, + Index: 4, + PosterID: 72, + PosterName: "test", + PosterEmail: "test@example.com", CommentType: "pull_cancel_scheduled_merge", Content: "canceled the automatic merge", Created: now, + Meta: map[string]any{}, }} for i, note := range notes { diff --git a/templates/repo/issue/view_content/comments.tmpl b/templates/repo/issue/view_content/comments.tmpl index cf9df4dbda..20dbbdc6e4 100644 --- a/templates/repo/issue/view_content/comments.tmpl +++ b/templates/repo/issue/view_content/comments.tmpl @@ -369,8 +369,7 @@ {{else if eq .Type 22}}
      - {{if .OriginalAuthor}} - {{else}} + {{if not .OriginalAuthor}} {{/* Some timeline avatars need a offset to correctly align with their speech bubble. The condition depends on review type and for positive reviews whether there is a comment element or not */}} @@ -499,9 +498,21 @@ {{else if eq .Type 25}}
      {{svg "octicon-git-branch"}} - {{template "shared/user/avatarlink" dict "user" .Poster}} + {{if not .OriginalAuthor}} + {{template "shared/user/avatarlink" dict "user" .Poster}} + {{end}} - {{.Poster.Name}} + {{if .OriginalAuthor}} + + {{svg (MigrationIcon $.Repository.GetOriginalURLHostname)}} + {{.OriginalAuthor}} + + {{if $.Repository.OriginalURL}} + ({{ctx.Locale.Tr "repo.migrated_from" $.Repository.OriginalURL $.Repository.GetOriginalURLHostname}}) + {{end}} + {{else}} + {{template "shared/user/authorlink" .Poster}} + {{end}} {{ctx.Locale.Tr "repo.pulls.change_target_branch_at" (.OldRef|Escape) (.NewRef|Escape) $createdStr}}
      From 1608ef0ce9ce2ea1c87aef715d111cf441637d01 Mon Sep 17 00:00:00 2001 From: qwerty287 <80460567+qwerty287@users.noreply.github.com> Date: Sat, 24 Feb 2024 09:18:39 +0100 Subject: [PATCH 217/271] Add API to get merged PR of a commit (#29243) Adds a new API `/repos/{owner}/{repo}/commits/{sha}/pull` that allows you to get the merged PR associated to a commit. --------- Co-authored-by: 6543 <6543@obermui.de> (cherry picked from commit 0a426cc575734e5eff410d6a790f40473117f753) --- models/fixtures/pull_request.yml | 1 + models/issues/pull.go | 20 +++++++++++++ models/issues/pull_test.go | 12 ++++++++ routers/api/v1/api.go | 1 + routers/api/v1/repo/commits.go | 51 ++++++++++++++++++++++++++++++++ templates/swagger/v1_json.tmpl | 43 +++++++++++++++++++++++++++ 6 files changed, 128 insertions(+) diff --git a/models/fixtures/pull_request.yml b/models/fixtures/pull_request.yml index 560674c370..54590fb830 100644 --- a/models/fixtures/pull_request.yml +++ b/models/fixtures/pull_request.yml @@ -9,6 +9,7 @@ head_branch: branch1 base_branch: master merge_base: 4a357436d925b5c974181ff12a994538ddc5a269 + merged_commit_id: 1a8823cd1a9549fde083f992f6b9b87a7ab74fb3 has_merged: true merger_id: 2 diff --git a/models/issues/pull.go b/models/issues/pull.go index ce2fd9233d..7d299eac27 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -1122,3 +1122,23 @@ func InsertPullRequests(ctx context.Context, prs ...*PullRequest) error { } return committer.Commit() } + +// GetPullRequestByMergedCommit returns a merged pull request by the given commit +func GetPullRequestByMergedCommit(ctx context.Context, repoID int64, sha string) (*PullRequest, error) { + pr := new(PullRequest) + has, err := db.GetEngine(ctx).Where("base_repo_id = ? AND merged_commit_id = ?", repoID, sha).Get(pr) + if err != nil { + return nil, err + } else if !has { + return nil, ErrPullRequestNotExist{0, 0, 0, repoID, "", ""} + } + + if err = pr.LoadAttributes(ctx); err != nil { + return nil, err + } + if err = pr.LoadIssue(ctx); err != nil { + return nil, err + } + + return pr, nil +} diff --git a/models/issues/pull_test.go b/models/issues/pull_test.go index 4702049af2..213838abec 100644 --- a/models/issues/pull_test.go +++ b/models/issues/pull_test.go @@ -425,6 +425,18 @@ func TestGetApprovers(t *testing.T) { assert.EqualValues(t, expected, approvers) } +func TestGetPullRequestByMergedCommit(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + pr, err := issues_model.GetPullRequestByMergedCommit(db.DefaultContext, 1, "1a8823cd1a9549fde083f992f6b9b87a7ab74fb3") + assert.NoError(t, err) + assert.EqualValues(t, 1, pr.ID) + + _, err = issues_model.GetPullRequestByMergedCommit(db.DefaultContext, 0, "1a8823cd1a9549fde083f992f6b9b87a7ab74fb3") + assert.ErrorAs(t, err, &issues_model.ErrPullRequestNotExist{}) + _, err = issues_model.GetPullRequestByMergedCommit(db.DefaultContext, 1, "") + assert.ErrorAs(t, err, &issues_model.ErrPullRequestNotExist{}) +} + func TestMigrate_InsertPullRequests(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) reponame := "repo1" diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index e75d4cc964..afceeefd77 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -1300,6 +1300,7 @@ func Routes() *web.Route { m.Group("/{ref}", func() { m.Get("/status", repo.GetCombinedCommitStatusByRef) m.Get("/statuses", repo.GetCommitStatusesByRef) + m.Get("/pull", repo.GetCommitPullRequest) }, context.ReferencesGitRepo()) }, reqRepoReader(unit.TypeCode)) m.Group("/git", func() { diff --git a/routers/api/v1/repo/commits.go b/routers/api/v1/repo/commits.go index 43b6400009..d01cf6b8bc 100644 --- a/routers/api/v1/repo/commits.go +++ b/routers/api/v1/repo/commits.go @@ -10,6 +10,7 @@ import ( "net/http" "strconv" + issues_model "code.gitea.io/gitea/models/issues" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/context" "code.gitea.io/gitea/modules/git" @@ -323,3 +324,53 @@ func DownloadCommitDiffOrPatch(ctx *context.APIContext) { return } } + +// GetCommitPullRequest returns the pull request of the commit +func GetCommitPullRequest(ctx *context.APIContext) { + // swagger:operation GET /repos/{owner}/{repo}/commits/{sha}/pull repository repoGetCommitPullRequest + // --- + // summary: Get the pull request of the commit + // produces: + // - application/json + // parameters: + // - name: owner + // in: path + // description: owner of the repo + // type: string + // required: true + // - name: repo + // in: path + // description: name of the repo + // type: string + // required: true + // - name: sha + // in: path + // description: SHA of the commit to get + // type: string + // required: true + // responses: + // "200": + // "$ref": "#/responses/PullRequest" + // "404": + // "$ref": "#/responses/notFound" + + pr, err := issues_model.GetPullRequestByMergedCommit(ctx, ctx.Repo.Repository.ID, ctx.Params(":sha")) + if err != nil { + if issues_model.IsErrPullRequestNotExist(err) { + ctx.Error(http.StatusNotFound, "GetPullRequestByMergedCommit", err) + } else { + ctx.Error(http.StatusInternalServerError, "GetPullRequestByIndex", err) + } + return + } + + if err = pr.LoadBaseRepo(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadBaseRepo", err) + return + } + if err = pr.LoadHeadRepo(ctx); err != nil { + ctx.Error(http.StatusInternalServerError, "LoadHeadRepo", err) + return + } + ctx.JSON(http.StatusOK, convert.ToAPIPullRequest(ctx, pr, ctx.Doer)) +} diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 973759604b..1bceee0802 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -4686,6 +4686,49 @@ } } }, + "/repos/{owner}/{repo}/commits/{sha}/pull": { + "get": { + "produces": [ + "application/json" + ], + "tags": [ + "repository" + ], + "summary": "Get the pull request of the commit", + "operationId": "repoGetCommitPullRequest", + "parameters": [ + { + "type": "string", + "description": "owner of the repo", + "name": "owner", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "name of the repo", + "name": "repo", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "SHA of the commit to get", + "name": "sha", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "$ref": "#/responses/PullRequest" + }, + "404": { + "$ref": "#/responses/notFound" + } + } + } + }, "/repos/{owner}/{repo}/contents": { "get": { "produces": [ From 428008ac19185125b7cb1e3d379254d7b1932529 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=9Eahin=20Akkaya?= Date: Sat, 24 Feb 2024 13:22:51 +0300 Subject: [PATCH 218/271] Implement recent commits graph (#29210) This is the implementation of Recent Commits page. This feature was mentioned on #18262. It adds another tab to Activity page called Recent Commits. Recent Commits tab shows number of commits since last year for the repository. (cherry picked from commit d3982bcd814bac93e3cbce1c7eb749b17e413fbd) --- options/locale/locale_en-US.ini | 4 +- routers/web/repo/recent_commits.go | 41 ++++++ routers/web/web.go | 4 + templates/repo/activity.tmpl | 1 + templates/repo/navbar.tmpl | 3 + templates/repo/recent_commits.tmpl | 9 ++ web_src/js/components/RepoRecentCommits.vue | 149 ++++++++++++++++++++ web_src/js/features/recent-commits.js | 21 +++ web_src/js/index.js | 2 + 9 files changed, 233 insertions(+), 1 deletion(-) create mode 100644 routers/web/repo/recent_commits.go create mode 100644 templates/repo/recent_commits.tmpl create mode 100644 web_src/js/components/RepoRecentCommits.vue create mode 100644 web_src/js/features/recent-commits.js diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index d4a5ca4517..3e4872420f 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1961,8 +1961,9 @@ wiki.original_git_entry_tooltip = View original Git file instead of using friend activity = Activity activity.navbar.pulse = Pulse -activity.navbar.contributors = Contributors activity.navbar.code_frequency = Code Frequency +activity.navbar.contributors = Contributors +activity.navbar.recent_commits = Recent Commits activity.period.filter_label = Period: activity.period.daily = 1 day activity.period.halfweekly = 3 days @@ -2659,6 +2660,7 @@ component_loading_info = This might take a bit… component_failed_to_load = An unexpected error happened. code_frequency.what = code frequency contributors.what = contributions +recent_commits.what = recent commits [org] org_name_holder = Organization Name diff --git a/routers/web/repo/recent_commits.go b/routers/web/repo/recent_commits.go new file mode 100644 index 0000000000..3507cb8752 --- /dev/null +++ b/routers/web/repo/recent_commits.go @@ -0,0 +1,41 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repo + +import ( + "errors" + "net/http" + + "code.gitea.io/gitea/modules/base" + "code.gitea.io/gitea/modules/context" + contributors_service "code.gitea.io/gitea/services/repository" +) + +const ( + tplRecentCommits base.TplName = "repo/activity" +) + +// RecentCommits renders the page to show recent commit frequency on repository +func RecentCommits(ctx *context.Context) { + ctx.Data["Title"] = ctx.Tr("repo.activity.navbar.recent_commits") + + ctx.Data["PageIsActivity"] = true + ctx.Data["PageIsRecentCommits"] = true + ctx.PageData["repoLink"] = ctx.Repo.RepoLink + + ctx.HTML(http.StatusOK, tplRecentCommits) +} + +// RecentCommitsData returns JSON of recent commits data +func RecentCommitsData(ctx *context.Context) { + if contributorStats, err := contributors_service.GetContributorStats(ctx, ctx.Cache, ctx.Repo.Repository, ctx.Repo.CommitID); err != nil { + if errors.Is(err, contributors_service.ErrAwaitGeneration) { + ctx.Status(http.StatusAccepted) + return + } + ctx.ServerError("RecentCommitsData", err) + } else { + ctx.JSON(http.StatusOK, contributorStats["total"].Weeks) + } +} diff --git a/routers/web/web.go b/routers/web/web.go index cef563f615..6f4a7543e7 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1452,6 +1452,10 @@ func registerRoutes(m *web.Route) { m.Get("", repo.CodeFrequency) m.Get("/data", repo.CodeFrequencyData) }) + m.Group("/recent-commits", func() { + m.Get("", repo.RecentCommits) + m.Get("/data", repo.RecentCommitsData) + }) }, context.RepoRef(), repo.MustBeNotEmpty, context.RequireRepoReaderOr(unit.TypePullRequests, unit.TypeIssues, unit.TypeReleases)) m.Group("/activity_author_data", func() { diff --git a/templates/repo/activity.tmpl b/templates/repo/activity.tmpl index 94f52b0e26..a19fb66261 100644 --- a/templates/repo/activity.tmpl +++ b/templates/repo/activity.tmpl @@ -9,6 +9,7 @@ {{if .PageIsPulse}}{{template "repo/pulse" .}}{{end}} {{if .PageIsContributors}}{{template "repo/contributors" .}}{{end}} {{if .PageIsCodeFrequency}}{{template "repo/code_frequency" .}}{{end}} + {{if .PageIsRecentCommits}}{{template "repo/recent_commits" .}}{{end}}
      diff --git a/templates/repo/navbar.tmpl b/templates/repo/navbar.tmpl index aa5021e73a..b2471dc17e 100644 --- a/templates/repo/navbar.tmpl +++ b/templates/repo/navbar.tmpl @@ -8,4 +8,7 @@ {{ctx.Locale.Tr "repo.activity.navbar.code_frequency"}} + + {{ctx.Locale.Tr "repo.activity.navbar.recent_commits"}} +
      diff --git a/templates/repo/recent_commits.tmpl b/templates/repo/recent_commits.tmpl new file mode 100644 index 0000000000..5c241d635c --- /dev/null +++ b/templates/repo/recent_commits.tmpl @@ -0,0 +1,9 @@ +{{if .Permission.CanRead $.UnitTypeCode}} +
      +
      +{{end}} diff --git a/web_src/js/components/RepoRecentCommits.vue b/web_src/js/components/RepoRecentCommits.vue new file mode 100644 index 0000000000..77697cd413 --- /dev/null +++ b/web_src/js/components/RepoRecentCommits.vue @@ -0,0 +1,149 @@ + + + diff --git a/web_src/js/features/recent-commits.js b/web_src/js/features/recent-commits.js new file mode 100644 index 0000000000..ded10d39be --- /dev/null +++ b/web_src/js/features/recent-commits.js @@ -0,0 +1,21 @@ +import {createApp} from 'vue'; + +export async function initRepoRecentCommits() { + const el = document.getElementById('repo-recent-commits-chart'); + if (!el) return; + + const {default: RepoRecentCommits} = await import(/* webpackChunkName: "recent-commits-graph" */'../components/RepoRecentCommits.vue'); + try { + const View = createApp(RepoRecentCommits, { + locale: { + loadingTitle: el.getAttribute('data-locale-loading-title'), + loadingTitleFailed: el.getAttribute('data-locale-loading-title-failed'), + loadingInfo: el.getAttribute('data-locale-loading-info'), + } + }); + View.mount(el); + } catch (err) { + console.error('RepoRecentCommits failed to load', err); + el.textContent = el.getAttribute('data-locale-component-failed-to-load'); + } +} diff --git a/web_src/js/index.js b/web_src/js/index.js index d9cfff4084..b7f3ba99a0 100644 --- a/web_src/js/index.js +++ b/web_src/js/index.js @@ -85,6 +85,7 @@ import {initRepoIssueList} from './features/repo-issue-list.js'; import {initCommonIssueListQuickGoto} from './features/common-issue-list.js'; import {initRepoContributors} from './features/contributors.js'; import {initRepoCodeFrequency} from './features/code-frequency.js'; +import {initRepoRecentCommits} from './features/recent-commits.js'; import {initRepoDiffCommitBranchesAndTags} from './features/repo-diff-commit.js'; import {initDirAuto} from './modules/dirauto.js'; @@ -176,6 +177,7 @@ onDomReady(() => { initRepositoryActionView(); initRepoContributors(); initRepoCodeFrequency(); + initRepoRecentCommits(); initCommitStatuses(); initCaptcha(); From 64ef7d365872b3a6160488c9fb5f652ec3e6413c Mon Sep 17 00:00:00 2001 From: KN4CK3R Date: Sat, 24 Feb 2024 12:45:59 +0100 Subject: [PATCH 219/271] Do not double close reader (#29354) Fixes #29346 --------- Co-authored-by: wxiaoguang (cherry picked from commit 553d46e6f6a144905266d58315a2b0ff2e976380) --- modules/git/blob_nogogit.go | 12 +++++++++++- routers/web/repo/editor.go | 3 --- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/modules/git/blob_nogogit.go b/modules/git/blob_nogogit.go index 9e1c2a0376..945a6bc432 100644 --- a/modules/git/blob_nogogit.go +++ b/modules/git/blob_nogogit.go @@ -102,7 +102,17 @@ func (b *blobReader) Read(p []byte) (n int, err error) { // Close implements io.Closer func (b *blobReader) Close() error { + if b.rd == nil { + return nil + } + defer b.cancel() - return DiscardFull(b.rd, b.n+1) + if err := DiscardFull(b.rd, b.n+1); err != nil { + return err + } + + b.rd = nil + + return nil } diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 7a0501604e..93decc085b 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -183,9 +183,6 @@ func editFile(ctx *context.Context, isNewFile bool) { } d, _ := io.ReadAll(dataRc) - if err := dataRc.Close(); err != nil { - log.Error("Error whilst closing blob data: %v", err) - } buf = append(buf, d...) if content, err := charset.ToUTF8(buf, charset.ConvertOpts{KeepBOM: true}); err != nil { From e91b9486133bf38acc75bf16819c4bce7d2789b0 Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sat, 24 Feb 2024 14:03:53 +0200 Subject: [PATCH 220/271] Remove jQuery from the issue reference context popup (#29367) - Removed all jQuery calls - Tested the context popup functionality and it works as before # Demo without jQuery ![action](https://github.com/go-gitea/gitea/assets/20454870/90b53de5-a8e9-4ed7-9236-1c9dfc324f38) --------- Signed-off-by: Yarden Shoham Co-authored-by: Giteabot (cherry picked from commit 267dbb4e938cc42dc09a4a893cca631b2f755557) --- web_src/js/components/ContextPopup.vue | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/web_src/js/components/ContextPopup.vue b/web_src/js/components/ContextPopup.vue index d9e6da316c..3a1b828cca 100644 --- a/web_src/js/components/ContextPopup.vue +++ b/web_src/js/components/ContextPopup.vue @@ -1,8 +1,8 @@
      {{template "base/footer" .}} diff --git a/web_src/css/base.css b/web_src/css/base.css index 6f51ad57ab..9cad9c5d23 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -29,6 +29,14 @@ --fonts-regular: var(--fonts-override, var(--fonts-proportional)), "Noto Sans", "Liberation Sans", sans-serif, var(--fonts-emoji); } +*, ::before, ::after { + /* these are needed for tailwind borders to work because we do not load tailwind's base + https://github.com/tailwindlabs/tailwindcss/blob/master/src/css/preflight.css */ + border-width: 0; + border-style: solid; + border-color: currentcolor; +} + textarea { font-family: var(--fonts-regular); } diff --git a/web_src/css/index.css b/web_src/css/index.css index f893531b78..ab925a4aa0 100644 --- a/web_src/css/index.css +++ b/web_src/css/index.css @@ -59,4 +59,6 @@ @import "./explore.css"; @import "./review.css"; @import "./actions.css"; + +@tailwind utilities; @import "./helpers.css"; diff --git a/webpack.config.js b/webpack.config.js index d4700ebe2b..896d541113 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -11,6 +11,8 @@ import webpack from 'webpack'; import {fileURLToPath} from 'node:url'; import {readFileSync} from 'node:fs'; import {env} from 'node:process'; +import tailwindcss from 'tailwindcss'; +import tailwindConfig from './tailwind.config.js'; const {EsbuildPlugin} = EsBuildLoader; const {SourceMapDevToolPlugin, DefinePlugin} = webpack; @@ -149,6 +151,15 @@ export default { import: {filter: filterCssImport}, }, }, + { + loader: 'postcss-loader', + options: { + postcssOptions: { + map: false, // https://github.com/postcss/postcss/issues/1914 + plugins: [tailwindcss(tailwindConfig)], + }, + }, + } ], }, { From 8a25361d9ab3bff1ed630e138aa6bb767a5ae1fe Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Sun, 25 Feb 2024 18:53:44 +0200 Subject: [PATCH 238/271] Remove jQuery AJAX from the archive download links (#29380) - Removed all jQuery AJAX calls and replaced with our fetch wrapper - Tested the repo archive download links dropdown functionality and it works as before # Demo using `fetch` instead of jQuery AJAX ![action](https://github.com/go-gitea/gitea/assets/20454870/db791249-bca1-4d22-ac5e-623f68023e15) --------- Signed-off-by: Yarden Shoham (cherry picked from commit ed3892d8430652c2bc8e2af21844d14769825e8a) --- web_src/js/features/repo-common.js | 58 ++++++++++++++---------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/web_src/js/features/repo-common.js b/web_src/js/features/repo-common.js index 3573e4d50b..a8221bbea8 100644 --- a/web_src/js/features/repo-common.js +++ b/web_src/js/features/repo-common.js @@ -1,38 +1,34 @@ import $ from 'jquery'; import {hideElem, showElem} from '../utils/dom.js'; +import {POST} from '../modules/fetch.js'; -const {csrfToken} = window.config; - -function getArchive($target, url, first) { - $.ajax({ - url, - type: 'POST', - data: { - _csrf: csrfToken, - }, - complete(xhr) { - if (xhr.status === 200) { - if (!xhr.responseJSON) { - // XXX Shouldn't happen? - $target.closest('.dropdown').children('i').removeClass('loading'); - return; - } - - if (!xhr.responseJSON.complete) { - $target.closest('.dropdown').children('i').addClass('loading'); - // Wait for only three quarters of a second initially, in case it's - // quickly archived. - setTimeout(() => { - getArchive($target, url, false); - }, first ? 750 : 2000); - } else { - // We don't need to continue checking. - $target.closest('.dropdown').children('i').removeClass('loading'); - window.location.href = url; - } +async function getArchive($target, url, first) { + try { + const response = await POST(url); + if (response.status === 200) { + const data = await response.json(); + if (!data) { + // XXX Shouldn't happen? + $target.closest('.dropdown').children('i').removeClass('loading'); + return; } - }, - }); + + if (!data.complete) { + $target.closest('.dropdown').children('i').addClass('loading'); + // Wait for only three quarters of a second initially, in case it's + // quickly archived. + setTimeout(() => { + getArchive($target, url, false); + }, first ? 750 : 2000); + } else { + // We don't need to continue checking. + $target.closest('.dropdown').children('i').removeClass('loading'); + window.location.href = url; + } + } + } catch { + $target.closest('.dropdown').children('i').removeClass('loading'); + } } export function initRepoArchiveLinks() { From 6ed19cc00d1f8affe9f834db2246db99c628b357 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 25 Feb 2024 21:36:45 +0100 Subject: [PATCH 239/271] templates: Safe & Escape no longer exist --- templates/repo/issue/view_title.tmpl | 2 +- templates/repo/settings/options.tmpl | 2 +- templates/shared/blocked_users_list.tmpl | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl index 6a60c68ec3..3f93f3ba35 100644 --- a/templates/repo/issue/view_title.tmpl +++ b/templates/repo/issue/view_title.tmpl @@ -50,7 +50,7 @@ {{if not .MadeUsingAGit}} {{$headHref = HTMLFormat `%s ` $headHref (ctx.Locale.Tr "copy_branch") .HeadTarget (svg "octicon-copy" 14)}} {{end}} - {{$baseHref := .BaseTarget|Escape}} + {{$baseHref := .BaseTarget}} {{if .BaseBranchLink}} {{$baseHref = HTMLFormat `%s` .BaseBranchLink $baseHref}} {{end}} diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index 128f553d3d..b0fa5aee89 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -691,7 +691,7 @@
        -
      • {{ctx.Locale.Tr "repo.settings.wiki_rename_branch_main_notices_1" | Safe}}
      • +
      • {{ctx.Locale.Tr "repo.settings.wiki_rename_branch_main_notices_1"}}
      • {{ctx.Locale.Tr "repo.settings.wiki_rename_branch_main_notices_2" .Repository.Name}}
      diff --git a/templates/shared/blocked_users_list.tmpl b/templates/shared/blocked_users_list.tmpl index 392947e80c..409b7f4aa5 100644 --- a/templates/shared/blocked_users_list.tmpl +++ b/templates/shared/blocked_users_list.tmpl @@ -9,7 +9,7 @@ {{template "shared/user/name" .}}
      - {{ctx.Locale.Tr "settings.blocked_since" (DateTime "short" .CreatedUnix) | Safe}} + {{ctx.Locale.Tr "settings.blocked_since" (DateTime "short" .CreatedUnix)}}
      From 0f098d1ad71605831ef2a9259abb2ca089079d74 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Mon, 26 Feb 2024 00:12:19 +0100 Subject: [PATCH 240/271] [DEADCODE] update --- .deadcode-out | 1 + 1 file changed, 1 insertion(+) diff --git a/.deadcode-out b/.deadcode-out index 3290e6102a..8fca0d96bb 100644 --- a/.deadcode-out +++ b/.deadcode-out @@ -296,6 +296,7 @@ package "code.gitea.io/gitea/modules/translation" package "code.gitea.io/gitea/modules/util" func UnsafeStringToBytes + func OptionalBoolFromGeneric package "code.gitea.io/gitea/modules/util/filebuffer" func CreateFromReader From fc635f101487f4ac940382c3e61acec829408491 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 27 Feb 2024 13:15:44 +0100 Subject: [PATCH 241/271] Restore the ability to view tags without a release The `repo.SingleRelease` handler was broken by gitea#29149, as the switch to `getReleaseInfos` stopped returning tags without an associated release. This resulted in the web UI showing a 404 when trying to view a tag without a release. This restores the functionality by explicitly including tags in the search, and also adds tests to exercise the fix. Signed-off-by: Gergely Nagy --- routers/web/repo/release.go | 2 ++ tests/integration/repo_tag_test.go | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 33e7f739ff..1998bd8ccd 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -279,6 +279,8 @@ func SingleRelease(ctx *context.Context) { releases, err := getReleaseInfos(ctx, &repo_model.FindReleasesOptions{ ListOptions: db.ListOptions{Page: 1, PageSize: 1}, RepoID: ctx.Repo.Repository.ID, + // Include tags in the search too. + IncludeTags: true, TagNames: []string{ctx.Params("*")}, // only show draft releases for users who can write, read-only users shouldn't see draft releases. IncludeDrafts: writeAccess, diff --git a/tests/integration/repo_tag_test.go b/tests/integration/repo_tag_test.go index 7e77906473..d2c4160602 100644 --- a/tests/integration/repo_tag_test.go +++ b/tests/integration/repo_tag_test.go @@ -1,9 +1,11 @@ // Copyright 2021 The Gitea Authors. All rights reserved. +// Copyright 2024 The Forgejo Authors c/o Codeberg e.V.. All rights reserved. // SPDX-License-Identifier: MIT package integration import ( + "net/http" "net/url" "testing" @@ -20,6 +22,34 @@ import ( "github.com/stretchr/testify/assert" ) +func TestTagViewWithoutRelease(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo.OwnerID}) + + defer func() { + releases, err := db.Find[repo_model.Release](db.DefaultContext, repo_model.FindReleasesOptions{ + IncludeTags: true, + TagNames: []string{"no-release"}, + RepoID: repo.ID, + }) + assert.NoError(t, err) + + for _, release := range releases { + _, err = db.DeleteByID[repo_model.Release](db.DefaultContext, release.ID) + assert.NoError(t, err) + } + }() + + err := release.CreateNewTag(git.DefaultContext, owner, repo, "master", "no-release", "release-less tag") + assert.NoError(t, err) + + // Test that the page loads + req := NewRequestf(t, "GET", "/%s/releases/tag/no-release", repo.FullName()) + MakeRequest(t, req, http.StatusOK) +} + func TestCreateNewTagProtected(t *testing.T) { defer tests.PrepareTestEnv(t)() From c9b410fb354f23b14b30373cc6ca62f9052ed5cf Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 27 Feb 2024 13:19:18 +0100 Subject: [PATCH 242/271] Highlight the correct small menu item when viewing a tag When viewing a tag that isn't associated with a release, highlight the "N Tags" sub-menu item, rather than the "M releases" one. Signed-off-by: Gergely Nagy --- templates/repo/release_tag_header.tmpl | 4 ++-- tests/integration/repo_tag_test.go | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/templates/repo/release_tag_header.tmpl b/templates/repo/release_tag_header.tmpl index f474fb89ea..6f3d28e7ad 100644 --- a/templates/repo/release_tag_header.tmpl +++ b/templates/repo/release_tag_header.tmpl @@ -5,9 +5,9 @@
      diff --git a/tests/integration/repo_tag_test.go b/tests/integration/repo_tag_test.go index d2c4160602..60c73ae63e 100644 --- a/tests/integration/repo_tag_test.go +++ b/tests/integration/repo_tag_test.go @@ -47,7 +47,15 @@ func TestTagViewWithoutRelease(t *testing.T) { // Test that the page loads req := NewRequestf(t, "GET", "/%s/releases/tag/no-release", repo.FullName()) - MakeRequest(t, req, http.StatusOK) + resp := MakeRequest(t, req, http.StatusOK) + + // Test that the tags sub-menu is active + htmlDoc := NewHTMLParser(t, resp.Body) + htmlDoc.AssertElement(t, ".small-menu-items .active.item[href*='/tags']", true) + + // Test that the release sub-menu isn't active + releaseLink := htmlDoc.Find(".small-menu-items .item[href*='/releases']") + assert.False(t, releaseLink.HasClass("active")) } func TestCreateNewTagProtected(t *testing.T) { From 2e76e4fb339f72a0530663e8a8cbec58ef85d312 Mon Sep 17 00:00:00 2001 From: 0ko <0ko@noreply.codeberg.org> Date: Tue, 27 Feb 2024 18:39:59 +0500 Subject: [PATCH 243/271] [I18N] Improve English strings & consistency --- options/locale/locale_en-US.ini | 147 +++++++++++++------------- templates/repo/migrate/migrating.tmpl | 2 +- templates/repo/settings/options.tmpl | 12 +-- templates/status/500.tmpl | 4 +- tests/integration/xss_test.go | 2 +- 5 files changed, 84 insertions(+), 83 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 3e4872420f..96c5942486 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -28,10 +28,10 @@ licenses = Licenses return_to_gitea = Return to Forgejo username = Username -email = Email Address +email = Email address password = Password -access_token = Access Token -re_type = Confirm Password +access_token = Access token +re_type = Confirm password captcha = CAPTCHA twofa = Two-Factor Authentication twofa_scratch = Two-Factor Scratch Code @@ -61,7 +61,6 @@ new_fork = New Repository Fork new_org = New Organization new_project = New Project new_project_column = New Column -manage_org = Manage Organizations admin_panel = Site Administration account_settings = Account Settings settings = Settings @@ -124,7 +123,7 @@ pin = Pin unpin = Unpin artifacts = Artifacts -confirm_delete_artifact = Are you sure you want to delete the artifact '%s' ? +confirm_delete_artifact = Are you sure you want to delete the artifact "%s" ? archived = Archived @@ -182,6 +181,7 @@ missing_csrf = Bad Request: no CSRF token present invalid_csrf = Bad Request: invalid CSRF token not_found = The target couldn't be found. network_error = Network error +server_internal = Internal Server Error [startpage] app_desc = A painless, self-hosted Git service @@ -279,13 +279,13 @@ admin_password = Password confirm_password = Confirm Password admin_email = Email Address install_btn_confirm = Install Forgejo -test_git_failed = Could not test 'git' command: %v -sqlite3_not_available = This Forgejo version does not support SQLite3. Please download the official binary version from %s (not the 'gobuild' version). +test_git_failed = Could not test "git" command: %v +sqlite3_not_available = This Forgejo version does not support SQLite3. Please download the official binary version from %s (not the "gobuild" version). invalid_db_setting = The database settings are invalid: %v invalid_db_table = The database table "%s" is invalid: %v invalid_repo_path = The repository root path is invalid: %v invalid_app_data_path = The app data path is invalid: %v -run_user_not_match = The 'run as' username is not the current username: %s -> %s +run_user_not_match = The "run as" username is not the current username: %s -> %s internal_token_failed = Failed to generate internal token: %v secret_key_failed = Failed to generate secret key: %v save_config_failed = Failed to save configuration: %v @@ -300,7 +300,7 @@ default_enable_timetracking = Enable Time Tracking by Default default_enable_timetracking_popup = Enable time tracking for new repositories by default. allow_dots_in_usernames = Allow users to use dots in their usernames. Doesn't affect existing accounts. no_reply_address = Hidden Email Domain -no_reply_address_helper = Domain name for users with a hidden email address. For example, the username 'joe' will be logged in Git as 'joe@noreply.example.org' if the hidden email domain is set to 'noreply.example.org'. +no_reply_address_helper = Domain name for users with a hidden email address. For example, the username "joe" will be logged in Git as "joe@noreply.example.org" if the hidden email domain is set to "noreply.example.org". password_algorithm = Password Hash Algorithm invalid_password_algorithm = Invalid password hash algorithm password_algorithm_helper = Set the password hashing algorithm. Algorithms have differing requirements and strength. The argon2 algorithm is rather secure but uses a lot of memory and may be inappropriate for small systems. @@ -310,7 +310,7 @@ env_config_keys = Environment Configuration env_config_keys_prompt = The following environment variables will also be applied to your configuration file: [home] -uname_holder = Username or Email Address +uname_holder = Username or Email address password_holder = Password switch_dashboard_context = Switch Dashboard Context my_repos = Repositories @@ -365,7 +365,7 @@ social_register_helper_msg = Already have an account? Link it now! disable_register_prompt = Registration is disabled. Please contact your site administrator. disable_register_mail = Email confirmation for registration is disabled. manual_activation_only = Contact your site administrator to complete activation. -remember_me = Remember This Device +remember_me = Remember this device remember_me.compromised = The login token is not valid anymore which may indicate a compromised account. Please check your account for unusual activities. forgot_password_title= Forgot Password forgot_password = Forgot password? @@ -534,8 +534,8 @@ SSPISeparatorReplacement = Separator SSPIDefaultLanguage = Default Language require_error = ` cannot be empty.` -alpha_dash_error = ` should contain only alphanumeric, dash ('-') and underscore ('_') characters.` -alpha_dash_dot_error = ` should contain only alphanumeric, dash ('-'), underscore ('_') and dot ('.') characters.` +alpha_dash_error = ` should contain only alphanumeric, dash ("-") and underscore ("_") characters.` +alpha_dash_dot_error = ` should contain only alphanumeric, dash ("-"), underscore ("_") and dot (".") characters.` git_ref_name_error = ` must be a well-formed Git reference name.` size_error = ` must be size %s.` min_size_error = ` must contain at least %s characters.` @@ -545,8 +545,8 @@ url_error = `"%s" is not a valid URL.` include_error = ` must contain substring "%s".` glob_pattern_error = ` glob pattern is invalid: %s.` regex_pattern_error = ` regex pattern is invalid: %s.` -username_error = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-'), underscore ('_') and dot ('.'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.` -username_error_no_dots = ` can only contain alphanumeric chars ('0-9','a-z','A-Z'), dash ('-') and underscore ('_'). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.` +username_error = ` can only contain alphanumeric chars ("0-9","a-z","A-Z"), dash ("-"), underscore ("_") and dot ("."). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.` +username_error_no_dots = ` can only contain alphanumeric chars ("0-9","a-z","A-Z"), dash ("-") and underscore ("_"). It cannot begin or end with non-alphanumeric chars, and consecutive non-alphanumeric chars are also forbidden.` invalid_group_team_map_error = ` mapping is invalid: %s` unknown_error = Unknown error: captcha_incorrect = The CAPTCHA code is incorrect. @@ -582,7 +582,7 @@ enterred_invalid_owner_name = The new owner name is not valid. enterred_invalid_password = The password you entered is incorrect. user_not_exist = The user does not exist. team_not_exist = The team does not exist. -last_org_owner = You cannot remove the last user from the 'owners' team. There must be at least one owner for an organization. +last_org_owner = You cannot remove the last user from the "owners" team. There must be at least one owner for an organization. cannot_add_org_to_team = An organization cannot be added as a team member. duplicate_invite_to_team = The user was already invited as a team member. organization_leave_success = You have successfully left the organization %s. @@ -647,7 +647,7 @@ avatar = Avatar ssh_gpg_keys = SSH / GPG Keys social = Social Accounts applications = Applications -orgs = Manage Organizations +orgs = Manage organizations repos = Repositories delete = Delete Account twofa = Two-Factor Authentication (TOTP) @@ -657,7 +657,7 @@ uid = UID webauthn = Two-Factor Authentication (Security Keys) blocked_users = Blocked Users -public_profile = Public Profile +public_profile = Public profile biography_placeholder = Tell us a little bit about yourself! (You can use Markdown) location_placeholder = Share your approximate location with others profile_desc = Control how your profile is shown to other users. Your primary email address will be used for notifications, password recovery and web-based Git operations. @@ -703,7 +703,7 @@ keep_activity_private_popup = Makes the activity visible only for you and the ad lookup_avatar_by_mail = Look Up Avatar by Email Address federated_avatar_lookup = Federated Avatar Lookup -enable_custom_avatar = Use Custom Avatar +enable_custom_avatar = Use custom avatar choose_new_avatar = Choose new avatar update_avatar = Update Avatar delete_current_avatar = Delete Current Avatar @@ -751,13 +751,13 @@ add_email_confirmation_sent = A confirmation email has been sent to "%s". Please add_email_success = The new email address has been added. email_preference_set_success = Email preference has been set successfully. add_openid_success = The new OpenID address has been added. -keep_email_private = Hide Email Address +keep_email_private = Hide email address keep_email_private_popup = This will hide your email address from your profile, as well as when you make a pull request or edit a file using the web interface. Pushed commits will not be modified. Use %s in commits to associate them with your account. openid_desc = OpenID lets you delegate authentication to an external provider. -manage_ssh_keys = Manage SSH Keys +manage_ssh_keys = Manage SSH keys manage_ssh_principals = Manage SSH Certificate Principals -manage_gpg_keys = Manage GPG Keys +manage_gpg_keys = Manage GPG keys add_key = Add Key ssh_desc = These public SSH keys are associated with your account. The corresponding private keys allow full access to your repositories. SSH keys that have been verified can be used to verify SSH-signed Git commits. principal_desc = These SSH certificate principals are associated with your account and allow full access to your repositories. @@ -766,8 +766,8 @@ ssh_helper = Need help? Have a look at the guide to about GPG. add_new_key = Add SSH Key add_new_gpg_key = Add GPG Key -key_content_ssh_placeholder = Begins with 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', or 'sk-ssh-ed25519@openssh.com' -key_content_gpg_placeholder = Begins with '-----BEGIN PGP PUBLIC KEY BLOCK-----' +key_content_ssh_placeholder = Begins with "ssh-ed25519", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521", "sk-ecdsa-sha2-nistp256@openssh.com", or "sk-ssh-ed25519@openssh.com" +key_content_gpg_placeholder = Begins with "-----BEGIN PGP PUBLIC KEY BLOCK-----" add_new_principal = Add Principal ssh_key_been_used = This SSH key has already been added to the server. ssh_key_name_used = An SSH key with same name already exists on your account. @@ -785,7 +785,7 @@ gpg_token = Token gpg_token_help = You can generate a signature using: gpg_token_code = echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature = Armored GPG signature -key_signature_gpg_placeholder = Begins with '-----BEGIN PGP SIGNATURE-----' +key_signature_gpg_placeholder = Begins with "-----BEGIN PGP SIGNATURE-----" verify_gpg_key_success = GPG key "%s" has been verified. ssh_key_verified=Verified Key ssh_key_verified_long=Key has been verified with a token and can be used to verify commits matching any activated email addresses for this user. @@ -795,11 +795,11 @@ 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_signature = Armored SSH signature -key_signature_ssh_placeholder = Begins with '-----BEGIN SSH SIGNATURE-----' +key_signature_ssh_placeholder = Begins with "-----BEGIN SSH SIGNATURE-----" verify_ssh_key_success = SSH key "%s" has been verified. subkeys = Subkeys key_id = Key ID -key_name = Key Name +key_name = Key name key_content = Content principal_content = Content add_key_success = The SSH key "%s" has been added. @@ -835,10 +835,10 @@ social_desc = These social accounts can be used to sign in to your account. Make unbind = Unlink unbind_success = The social account has been removed successfully. -manage_access_token = Manage Access Tokens -generate_new_token = Generate New Token +manage_access_token = Manage access tokens +generate_new_token = Generate new token tokens_desc = These tokens grant access to your account using the Forgejo API. -token_name = Token Name +token_name = Token name generate_token = Generate Token generate_token_success = Your new token has been generated. Copy it now as it will not be shown again. generate_token_name_duplicate = %s has been used as an application name already. Please use a new one. @@ -859,17 +859,17 @@ access_token_desc = Selected token permissions limit authorization only to the c at_least_one_permission = You must select at least one permission to create a token permissions_list = Permissions: -manage_oauth2_applications = Manage OAuth2 Applications +manage_oauth2_applications = Manage OAuth2 applications edit_oauth2_application = Edit OAuth2 Application oauth2_applications_desc = OAuth2 applications enables your third-party application to securely authenticate users at this Forgejo instance. remove_oauth2_application = Remove OAuth2 Application remove_oauth2_application_desc = Removing an OAuth2 application will revoke access to all signed access tokens. Continue? remove_oauth2_application_success = The application has been deleted. -create_oauth2_application = Create a new OAuth2 Application +create_oauth2_application = Create a new OAuth2 application create_oauth2_application_button = Create Application create_oauth2_application_success = You have successfully created a new OAuth2 application. update_oauth2_application_success = You have successfully updated the OAuth2 application. -oauth2_application_name = Application Name +oauth2_application_name = Application name oauth2_confidential_client = Confidential Client. Select for apps that keep the secret confidential, such as web apps. Do not select for native apps including desktop and mobile apps. oauth2_redirect_uris = Redirect URIs. Please use a new line for every URI. save_application = Save @@ -883,7 +883,7 @@ oauth2_application_create_description = OAuth2 applications gives your third-par oauth2_application_remove_description = Removing an OAuth2 application will prevent it from accessing authorized user accounts on this instance. Continue? oauth2_application_locked = Forgejo pre-registers some OAuth2 applications on startup if enabled in config. To prevent unexpected behavior, these can neither be edited nor removed. Please refer to the OAuth2 documentation for more information. -authorized_oauth2_applications = Authorized OAuth2 Applications +authorized_oauth2_applications = Authorized OAuth2 applications authorized_oauth2_applications_description = You have granted access to your personal Forgejo account to these third party applications. Please revoke access for applications that are no longer in use. revoke_key = Revoke revoke_oauth2_grant = Revoke Access @@ -968,7 +968,7 @@ admin.flags_replaced = Repository flags replaced new_repo_helper = A repository contains all project files, including revision history. Already hosting one elsewhere? Migrate repository. owner = Owner owner_helper = Some organizations may not show up in the dropdown due to a maximum repository count limit. -repo_name = Repository Name +repo_name = Repository name repo_name_helper = Good repository names use short, memorable and unique keywords. repo_size = Repository Size template = Template @@ -1024,7 +1024,7 @@ default_branch_label = default default_branch_helper = The default branch is the base branch for pull requests and code commits. mirror_prune = Prune mirror_prune_desc = Remove obsolete remote-tracking references -mirror_interval = Mirror Interval (valid time units are 'h', 'm', 's'). 0 to disable periodic sync. (Minimum interval: %s) +mirror_interval = Mirror Interval (valid time units are "h", "m", "s"). 0 to disable periodic sync. (Minimum interval: %s) mirror_interval_invalid = The mirror interval is not valid. mirror_sync = synced mirror_sync_on_commit = Sync when commits are pushed @@ -1101,7 +1101,7 @@ form.name_reserved = The repository name "%s" is reserved. form.name_pattern_not_allowed = The pattern "%s" is not allowed in a repository name. need_auth = Authorization -migrate_options = Migration Options +migrate_options = Migration options migrate_service = Migration Service migrate_options_mirror_helper = This repository will be a mirror migrate_options_lfs = Migrate LFS files @@ -1109,7 +1109,7 @@ migrate_options_lfs_endpoint.label = LFS Endpoint migrate_options_lfs_endpoint.description = Migration will attempt to use your Git remote to determine the LFS server. You can also specify a custom endpoint if the repository LFS data is stored somewhere else. migrate_options_lfs_endpoint.description.local = A local server path is supported too. migrate_options_lfs_endpoint.placeholder = If left blank, the endpoint will be derived from the clone URL -migrate_items = Migration Items +migrate_items = Migration items migrate_items_wiki = Wiki migrate_items_milestones = Milestones migrate_items_labels = Labels @@ -1118,8 +1118,8 @@ migrate_items_pullrequests = Pull Requests migrate_items_merge_requests = Merge Requests migrate_items_releases = Releases migrate_repo = Migrate Repository -migrate.clone_address = Migrate / Clone From URL -migrate.clone_address_desc = The HTTP(S) or Git 'clone' URL of an existing repository +migrate.clone_address = Migrate / Clone from URL +migrate.clone_address_desc = The HTTP(S) or Git "clone" URL of an existing repository migrate.github_token_desc = You can put one or more tokens with comma separated here to make migrating faster because of GitHub API rate limit. WARN: Abusing this feature may violate the service provider's policy and lead to account blocking. migrate.clone_local_path = or a local server path migrate.permission_denied = You are not allowed to import local repositories. @@ -1223,8 +1223,8 @@ escape_control_characters = Escape unescape_control_characters = Unescape file_copy_permalink = Copy Permalink view_git_blame = View Git Blame -video_not_supported_in_browser = Your browser does not support the HTML5 'video' tag. -audio_not_supported_in_browser = Your browser does not support the HTML5 'audio' tag. +video_not_supported_in_browser = Your browser does not support the HTML5 "video" tag. +audio_not_supported_in_browser = Your browser does not support the HTML5 "audio" tag. stored_lfs = Stored with Git LFS symbolic_link = Symbolic link executable_file = Executable File @@ -1260,12 +1260,12 @@ editor.delete_this_file = Delete File editor.must_have_write_access = You must have write access to make or propose changes to this file. editor.file_delete_success = File "%s" has been deleted. editor.name_your_file = Name your file… -editor.filename_help = Add a directory by typing its name followed by a slash ('/'). Remove a directory by typing backspace at the beginning of the input field. +editor.filename_help = Add a directory by typing its name followed by a slash ("/"). Remove a directory by typing backspace at the beginning of the input field. editor.or = or editor.cancel_lower = Cancel editor.commit_signed_changes = Commit Signed Changes editor.commit_changes = Commit Changes -editor.add_tmpl = Add '' +editor.add_tmpl = Add "" editor.add = Add %s editor.update = Update %s editor.delete = Delete %s @@ -1383,7 +1383,7 @@ projects.column.set_default_desc = Set this column as default for uncategorized projects.column.unset_default = Unset Default projects.column.unset_default_desc = Unset this column as default projects.column.delete = Delete Column -projects.column.deletion_desc = Deleting a project column moves all related issues to 'Uncategorized'. Continue? +projects.column.deletion_desc = Deleting a project column moves all related issues to "Uncategorized". Continue? projects.column.color = Color projects.open = Open projects.close = Close @@ -1432,7 +1432,7 @@ issues.new_label_placeholder = Label name issues.new_label_desc_placeholder = Description issues.create_label = Create Label issues.label_templates.title = Load a predefined set of labels -issues.label_templates.info = No labels exist yet. Create a label with 'New Label' or use a predefined label set: +issues.label_templates.info = No labels exist yet. Create a label with "New Label" or use a predefined label set: issues.label_templates.helper = Select a label set issues.label_templates.use = Use Label Set issues.label_templates.fail_to_load_file = Failed to load label template file "%s": %v @@ -1606,7 +1606,7 @@ issues.lock_no_reason = locked and limited conversation to collaborators %s issues.unlock_comment = unlocked this conversation %s issues.lock_confirm = Lock issues.unlock_confirm = Unlock -issues.lock.notice_1 = - Other users can’t add new comments to this issue. +issues.lock.notice_1 = - Other users can't add new comments to this issue. issues.lock.notice_2 = - You and other collaborators with access to this repository can still leave comments that others can see. issues.lock.notice_3 = - You can always unlock this issue again in the future. issues.unlock.notice_1 = - Everyone would be able to comment on this issue once more. @@ -1640,7 +1640,7 @@ issues.add_time_sum_to_small = No time was entered. issues.time_spent_total = Total Time Spent issues.time_spent_from_all_authors = `Total Time Spent: %s` issues.due_date = Due Date -issues.invalid_due_date_format = Due date format must be 'yyyy-mm-dd'. +issues.invalid_due_date_format = Due date format must be "yyyy-mm-dd". issues.error_modifying_due_date = Failed to modify the due date. issues.error_removing_due_date = Failed to remove the due date. issues.push_commit_1 = added %d commit %s @@ -1657,7 +1657,7 @@ issues.due_date_added = added the due date %s %s issues.due_date_modified = modified the due date from %[2]s to %[1]s %[3]s issues.due_date_remove = removed the due date %s %s issues.due_date_overdue = Overdue -issues.due_date_invalid = The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'. +issues.due_date_invalid = The due date is invalid or out of range. Please use the format "yyyy-mm-dd". issues.dependency.title = Dependencies issues.dependency.issue_no_dependencies = No dependencies set. issues.dependency.pr_no_dependencies = No dependencies set. @@ -1693,7 +1693,7 @@ issues.review.self.approval = You cannot approve your own pull request. issues.review.self.rejection = You cannot request changes on your own pull request. issues.review.approve = approved these changes %s issues.review.comment = reviewed %s -issues.review.dismissed = dismissed %s’s review %s +issues.review.dismissed = dismissed %s's review %s issues.review.dismissed_label = Dismissed issues.review.left_comment = left a comment issues.review.content.empty = You need to leave a comment indicating the requested change(s). @@ -1897,7 +1897,7 @@ milestones.title = Title milestones.desc = Description milestones.due_date = Due Date (optional) milestones.clear = Clear -milestones.invalid_due_date_format = Due date format must be 'yyyy-mm-dd'. +milestones.invalid_due_date_format = Due date format must be "yyyy-mm-dd". milestones.create_success = The milestone "%s" has been created. milestones.edit = Edit Milestone milestones.edit_subheader = Milestones organize issues and track progress. @@ -1956,7 +1956,7 @@ wiki.page_already_exists = A wiki page with the same name already exists. wiki.reserved_page = The wiki page name "%s" is reserved. wiki.pages = Pages wiki.last_updated = Last updated %s -wiki.page_name_desc = Enter a name for this Wiki page. Some special names are: 'Home', '_Sidebar' and '_Footer'. +wiki.page_name_desc = Enter a name for this Wiki page. Some special names are: "Home", "_Sidebar" and "_Footer". wiki.original_git_entry_tooltip = View original Git file instead of using friendly link. activity = Activity @@ -2132,7 +2132,7 @@ settings.actions_desc = Enable Repository Actions settings.admin_settings = Administrator Settings settings.admin_enable_health_check = Enable Repository Health Checks (git fsck) settings.admin_code_indexer = Code Indexer -settings.admin_stats_indexer = Code Statistics Indexer +settings.admin_stats_indexer = Code statistics indexer settings.admin_indexer_commit_sha = Last Indexed SHA settings.admin_indexer_unindexed = Unindexed settings.reindex_button = Add to Reindex Queue @@ -2159,6 +2159,7 @@ settings.transfer_abort_invalid = You cannot cancel a non existent repository tr settings.transfer_abort_success = The repository transfer to %s was successfully canceled. settings.transfer_desc = Transfer this repository to a user or to an organization for which you have administrator rights. settings.enter_repo_name = Enter the owner and repository name exactly as shown: +settings.confirmation_string = Confirmation string settings.transfer_in_progress = There is currently an ongoing transfer. Please cancel it if you will like to transfer this repository to another user. settings.transfer_notices_1 = - You will lose access to the repository if you transfer it to an individual user. settings.transfer_notices_2 = - You will keep access to the repository if you transfer it to an organization that you (co-)own. @@ -2361,7 +2362,7 @@ settings.protected_branch.delete_rule = Delete Rule settings.protected_branch_can_push = Allow push? settings.protected_branch_can_push_yes = You can push settings.protected_branch_can_push_no = You cannot push -settings.branch_protection = Branch Protection Rules for Branch '%s' +settings.branch_protection = Branch Protection Rules for Branch "%s" settings.protect_this_branch = Enable Branch Protection settings.protect_this_branch_desc = Prevents deletion and restricts Git pushing and merging to the branch. settings.protect_disable_push = Disable Push @@ -2404,10 +2405,10 @@ settings.require_signed_commits_desc = Reject pushes to this branch if they are settings.protect_branch_name_pattern = Protected Branch Name Pattern settings.protect_branch_name_pattern_desc = Protected branch name patterns. See the documentation for pattern syntax. Examples: main, release/** settings.protect_patterns = Patterns -settings.protect_protected_file_patterns = Protected file patterns (separated using semicolon ';'): -settings.protect_protected_file_patterns_desc = Protected files are not allowed to be changed directly even if user has rights to add, edit, or delete files in this branch. Multiple patterns can be separated using semicolon (';'). See github.com/gobwas/glob documentation for pattern syntax. Examples: .drone.yml, /docs/**/*.txt. -settings.protect_unprotected_file_patterns = Unprotected file patterns (separated using semicolon ';'): -settings.protect_unprotected_file_patterns_desc = Unprotected files that are allowed to be changed directly if user has write access, bypassing push restriction. Multiple patterns can be separated using semicolon (';'). See github.com/gobwas/glob documentation for pattern syntax. Examples: .drone.yml, /docs/**/*.txt. +settings.protect_protected_file_patterns = Protected file patterns (separated using semicolon ";"): +settings.protect_protected_file_patterns_desc = Protected files are not allowed to be changed directly even if user has rights to add, edit, or delete files in this branch. Multiple patterns can be separated using semicolon (";"). See github.com/gobwas/glob documentation for pattern syntax. Examples: .drone.yml, /docs/**/*.txt. +settings.protect_unprotected_file_patterns = Unprotected file patterns (separated using semicolon ";"): +settings.protect_unprotected_file_patterns_desc = Unprotected files that are allowed to be changed directly if user has write access, bypassing push restriction. Multiple patterns can be separated using semicolon (";"). See github.com/gobwas/glob documentation for pattern syntax. Examples: .drone.yml, /docs/**/*.txt. settings.add_protected_branch = Enable protection settings.delete_protected_branch = Disable protection settings.update_protect_branch_success = Branch protection for rule "%s" has been updated. @@ -2468,7 +2469,7 @@ settings.lfs_findcommits=Find commits settings.lfs_lfs_file_no_commits=No Commits found for this LFS file settings.lfs_noattribute=This path does not have the lockable attribute in the default branch settings.lfs_delete=Delete LFS file with OID %s -settings.lfs_delete_warning=Deleting an LFS file may cause 'object does not exist' errors on checkout. Are you sure? +settings.lfs_delete_warning=Deleting an LFS file may cause "object does not exist" errors on checkout. Are you sure? settings.lfs_findpointerfiles=Find pointer files settings.lfs_locks=Locks settings.lfs_invalid_locking_path=Invalid path: %s @@ -2644,7 +2645,7 @@ tag.create_success = Tag "%s" has been created. topic.manage_topics = Manage Topics topic.done = Done topic.count_prompt = You cannot select more than 25 topics -topic.format_prompt = Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase. +topic.format_prompt = Topics must start with a letter or number, can include dashes ("-") and dots ("."), can be up to 35 characters long. Letters must be lowercase. find_file.go_to_file = Go to file find_file.no_matching = No matching file found @@ -2834,7 +2835,7 @@ dashboard.delete_repo_archives.started = Delete all repository archives task sta dashboard.delete_missing_repos = Delete all repositories missing their Git files dashboard.delete_missing_repos.started = Delete all repositories missing their Git files task started. dashboard.delete_generated_repository_avatars = Delete generated repository avatars -dashboard.sync_repo_branches = Sync missed branches from git data to databases +dashboard.sync_repo_branches = Sync missed branches from git data to database dashboard.sync_repo_tags = Sync tags from git data to database dashboard.update_mirrors = Update Mirrors dashboard.repo_health_check = Health check all repositories @@ -2843,9 +2844,9 @@ dashboard.archive_cleanup = Delete old repository archives dashboard.deleted_branches_cleanup = Clean-up deleted branches dashboard.update_migration_poster_id = Update migration poster IDs dashboard.git_gc_repos = Garbage collect all repositories -dashboard.resync_all_sshkeys = Update the '.ssh/authorized_keys' file with Forgejo SSH keys. -dashboard.resync_all_sshprincipals = Update the '.ssh/authorized_principals' file with Forgejo SSH principals. -dashboard.resync_all_hooks = Resynchronize pre-receive, update and post-receive hooks of all repositories. +dashboard.resync_all_sshkeys = Update the ".ssh/authorized_keys" file with Forgejo SSH keys. +dashboard.resync_all_sshprincipals = Update the ".ssh/authorized_principals" file with Forgejo SSH principals. +dashboard.resync_all_hooks = Resynchronize pre-receive, update and post-receive hooks of all repositories dashboard.reinit_missing_repos = Reinitialize all missing Git repositories for which records exist dashboard.sync_external_users = Synchronize external user data dashboard.cleanup_hook_task_table = Cleanup hook_task table @@ -3038,7 +3039,7 @@ auths.search_page_size = Page Size auths.filter = User Filter auths.admin_filter = Admin Filter auths.restricted_filter = Restricted Filter -auths.restricted_filter_helper = Leave empty to not set any users as restricted. Use an asterisk ('*') to set all users that do not match Admin Filter as restricted. +auths.restricted_filter_helper = Leave empty to not set any users as restricted. Use an asterisk ("*") to set all users that do not match Admin Filter as restricted. auths.verify_group_membership = Verify group membership in LDAP (leave the filter empty to skip) auths.group_search_base = Group Search Base DN auths.group_attribute_list_users = Group Attribute Containing List Of Users @@ -3051,7 +3052,7 @@ auths.smtp_auth = SMTP Authentication Type auths.smtphost = SMTP Host auths.smtpport = SMTP Port auths.allowed_domains = Allowed Domains -auths.allowed_domains_helper = Leave empty to allow all domains. Separate multiple domains with a comma (','). +auths.allowed_domains_helper = Leave empty to allow all domains. Separate multiple domains with a comma (","). auths.skip_tls_verify = Skip TLS Verify auths.force_smtps = Force SMTPS auths.force_smtps_helper = SMTPS is always used on port 465. Set this to force SMTPS on other ports. (Otherwise STARTTLS will be used on other ports if it is supported by the host.) @@ -3098,7 +3099,7 @@ auths.tips = Tips auths.tips.oauth2.general = OAuth2 Authentication auths.tips.oauth2.general.tip = When registering a new OAuth2 authentication, the callback/redirect URL should be: auths.tip.oauth2_provider = OAuth2 Provider -auths.tip.bitbucket = Register a new OAuth consumer on https://bitbucket.org/account/user//oauth-consumers/new and add the permission 'Account' - 'Read' +auths.tip.bitbucket = Register a new OAuth consumer on https://bitbucket.org/account/user//oauth-consumers/new and add the permission "Account" - "Read" auths.tip.nextcloud = Register a new OAuth consumer on your instance using the following menu "Settings -> Security -> OAuth 2.0 client" auths.tip.dropbox = Create a new application at https://www.dropbox.com/developers/apps auths.tip.facebook = Register a new application at https://developers.facebook.com/apps and add the product "Facebook Login" @@ -3153,7 +3154,7 @@ config.ssh_port = Port config.ssh_listen_port = Listen Port config.ssh_root_path = Root Path config.ssh_key_test_path = Key Test Path -config.ssh_keygen_path = Keygen ('ssh-keygen') Path +config.ssh_keygen_path = Keygen ("ssh-keygen") Path config.ssh_minimum_key_size_check = Minimum Key Size Check config.ssh_minimum_key_sizes = Minimum Key Sizes @@ -3532,7 +3533,7 @@ settings.delete.description = Deleting a package is permanent and cannot be undo settings.delete.notice = You are about to delete %s (%s). This operation is irreversible, are you sure? settings.delete.success = The package has been deleted. settings.delete.error = Failed to delete the package. -owner.settings.cargo.title = Cargo Registry Index +owner.settings.cargo.title = Cargo registry index owner.settings.cargo.initialize = Initialize Index owner.settings.cargo.initialize.description = A special index Git repository is needed to use the Cargo registry. Using this option will (re-)create the repository and configure it automatically. owner.settings.cargo.initialize.error = Failed to initialize Cargo index: %v @@ -3541,7 +3542,7 @@ owner.settings.cargo.rebuild = Rebuild Index owner.settings.cargo.rebuild.description = Rebuilding can be useful if the index is not synchronized with the stored Cargo packages. owner.settings.cargo.rebuild.error = Failed to rebuild Cargo index: %v owner.settings.cargo.rebuild.success = The Cargo index was successfully rebuild. -owner.settings.cleanuprules.title = Manage Cleanup Rules +owner.settings.cleanuprules.title = Manage cleanup rules owner.settings.cleanuprules.add = Add Cleanup Rule owner.settings.cleanuprules.edit = Edit Cleanup Rule owner.settings.cleanuprules.none = There are no cleanup rules yet. @@ -3561,7 +3562,7 @@ owner.settings.cleanuprules.remove.days = Remove versions older than owner.settings.cleanuprules.remove.pattern = Remove versions matching owner.settings.cleanuprules.success.update = Cleanup rule has been updated. owner.settings.cleanuprules.success.delete = Cleanup rule has been deleted. -owner.settings.chef.title = Chef Registry +owner.settings.chef.title = Chef registry owner.settings.chef.keypair = Generate key pair owner.settings.chef.keypair.description = A key pair is necessary to authenticate to the Chef registry. If you have generated a key pair before, generating a new key pair will discard the old key pair. @@ -3649,9 +3650,9 @@ runs.no_runs = The workflow has no runs yet. runs.empty_commit_message = (empty commit message) workflow.disable = Disable Workflow -workflow.disable_success = Workflow '%s' disabled successfully. +workflow.disable_success = Workflow "%s" disabled successfully. workflow.enable = Enable Workflow -workflow.enable_success = Workflow '%s' enabled successfully. +workflow.enable_success = Workflow "%s" enabled successfully. workflow.disabled = Workflow is disabled. need_approval_desc = Need approval to run workflows for fork pull request. diff --git a/templates/repo/migrate/migrating.tmpl b/templates/repo/migrate/migrating.tmpl index bf8ea0ef7a..4d7e45f2e0 100644 --- a/templates/repo/migrate/migrating.tmpl +++ b/templates/repo/migrate/migrating.tmpl @@ -73,7 +73,7 @@
      - +
      diff --git a/templates/repo/settings/options.tmpl b/templates/repo/settings/options.tmpl index b0fa5aee89..9541dd12d8 100644 --- a/templates/repo/settings/options.tmpl +++ b/templates/repo/settings/options.tmpl @@ -539,7 +539,7 @@
      - +
      @@ -570,7 +570,7 @@
      - +
      @@ -602,7 +602,7 @@
      - +
      @@ -640,7 +640,7 @@
      - +
      @@ -672,7 +672,7 @@
      - +
      @@ -705,7 +705,7 @@
      - +
      diff --git a/templates/status/500.tmpl b/templates/status/500.tmpl index 30cb255643..d0c8746148 100644 --- a/templates/status/500.tmpl +++ b/templates/status/500.tmpl @@ -9,7 +9,7 @@ - Internal Server Error - {{AppName}} + {{ctx.Locale.Tr "error.server_internal"}} - {{AppName}} {{template "base/head_style" .}} @@ -35,7 +35,7 @@

      500

      -

      Internal Server Error

      +

      {{ctx.Locale.Tr "error.server_internal"}}

      diff --git a/tests/integration/xss_test.go b/tests/integration/xss_test.go index acd716c7c7..6fe394acda 100644 --- a/tests/integration/xss_test.go +++ b/tests/integration/xss_test.go @@ -125,5 +125,5 @@ func TestXSSReviewDismissed(t *testing.T) { htmlDoc := NewHTMLParser(t, resp.Body) htmlDoc.AssertElement(t, "script.evil", false) - assert.Contains(t, htmlDoc.Find("#issuecomment-1000 .dismissed-message").Text(), `dismissed Otto ’s review`) + assert.Contains(t, htmlDoc.Find("#issuecomment-1000 .dismissed-message").Text(), `dismissed Otto 's review`) } From 5bc7b8baf8dd1269be708e82911168884b3c0b0b Mon Sep 17 00:00:00 2001 From: "Panagiotis \"Ivory\" Vasilopoulos" Date: Tue, 27 Feb 2024 13:35:21 +0100 Subject: [PATCH 244/271] [UI] Agit: Add link to docs and tooltip to label Continuation of #2444, which introduced the commit bf7fb89178f41c712b8a8863667a442ec1e1c5d4 but only added the label and the tests. The tooltip explaining what AGit is and its advantages is not meant to advertise AGit - it is meant to inform the reader that is presumably not familiar with the workflow that they will not be able to find a fork or a branch associated with the Pull Request as a direct consequence of this workflow. Issue #2474 mentions that we should show instructions on how to fetch an AGit-created Pull Request, and this is the plan. However, this may take time, so I might as well make the label a bit more "complete" and less out-of-place for now if we do not manage to improve these instructions until the next release (Forgejo v1.22). Refs: https://codeberg.org/forgejo/forgejo/issues/2474 --- options/locale/locale_en-US.ini | 4 +++- templates/repo/issue/view_title.tmpl | 11 +++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 96c5942486..b41d818399 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1508,7 +1508,6 @@ issues.action_check_all = Check/Uncheck all items issues.opened_by = opened %[1]s by %[3]s pulls.merged_by = by %[3]s was merged %[1]s pulls.merged_by_fake = by %[2]s was merged %[1]s -pulls.made_using_agit = AGit issues.closed_by = by %[3]s was closed %[1]s issues.opened_by_fake = opened %[1]s by %[2]s issues.closed_by_fake = by %[2]s was closed %[1]s @@ -1865,6 +1864,9 @@ pulls.clear_merge_message_hint = Clearing the merge message will only remove the pulls.reopen_failed.head_branch = The pull request cannot be reopened, because the head branch doesn't exist anymore. pulls.reopen_failed.base_branch = The pull request cannot be reopened, because the base branch doesn't exist anymore. +pulls.made_using_agit = AGit +pulls.agit_explanation = Created using the AGit workflow. AGit lets contributors propose changes using "git push" without creating a fork or a new branch. + pulls.auto_merge_button_when_succeed = (When checks succeed) pulls.auto_merge_when_succeed = Auto merge when all checks succeed pulls.auto_merge_newly_scheduled = The pull request was scheduled to merge when all checks succeed. diff --git a/templates/repo/issue/view_title.tmpl b/templates/repo/issue/view_title.tmpl index 3f93f3ba35..339def894c 100644 --- a/templates/repo/issue/view_title.tmpl +++ b/templates/repo/issue/view_title.tmpl @@ -73,10 +73,13 @@ {{end}} {{if .MadeUsingAGit}} - {{/* TODO: Add tooltip and a link to the documentation */}} - - {{ctx.Locale.Tr "repo.pulls.made_using_agit"}} - + {{/* TODO: Move documentation link to the instructions at the bottom of the PR, show instructions when clicking label */}} + {{/* Note: #agit-label is necessary for testing whether the label appears when it should in tests/integration/git_test.go */}} + + + {{ctx.Locale.Tr "repo.pulls.made_using_agit"}} + + {{end}} {{$reactions := .Reactions.GroupByType}} {{if $reactions}} diff --git a/web_src/js/features/common-global.js b/web_src/js/features/common-global.js index e8b546970f..cd0fc6d6a9 100644 --- a/web_src/js/features/common-global.js +++ b/web_src/js/features/common-global.js @@ -200,65 +200,68 @@ export function initGlobalCommon() { } export function initGlobalDropzone() { - // Dropzone for (const el of document.querySelectorAll('.dropzone')) { - const $dropzone = $(el); - const _promise = createDropzone(el, { - url: $dropzone.data('upload-url'), - headers: {'X-Csrf-Token': csrfToken}, - maxFiles: $dropzone.data('max-file'), - maxFilesize: $dropzone.data('max-size'), - acceptedFiles: (['*/*', ''].includes($dropzone.data('accepts'))) ? null : $dropzone.data('accepts'), - addRemoveLinks: true, - dictDefaultMessage: $dropzone.data('default-message'), - dictInvalidFileType: $dropzone.data('invalid-input-type'), - dictFileTooBig: $dropzone.data('file-too-big'), - dictRemoveFile: $dropzone.data('remove-file'), - timeout: 0, - thumbnailMethod: 'contain', - thumbnailWidth: 480, - thumbnailHeight: 480, - init() { - this.on('success', (file, data) => { - file.uuid = data.uuid; - const input = $(``).val(data.uuid); - $dropzone.find('.files').append(input); - // Create a "Copy Link" element, to conveniently copy the image - // or file link as Markdown to the clipboard - const copyLinkElement = document.createElement('div'); - copyLinkElement.className = 'gt-text-center'; - // The a element has a hardcoded cursor: pointer because the default is overridden by .dropzone - copyLinkElement.innerHTML = `${svg('octicon-copy', 14, 'copy link')} Copy link`; - copyLinkElement.addEventListener('click', async (e) => { - e.preventDefault(); - let fileMarkdown = `[${file.name}](/attachments/${file.uuid})`; - if (file.type.startsWith('image/')) { - fileMarkdown = `!${fileMarkdown}`; - } else if (file.type.startsWith('video/')) { - fileMarkdown = ``; - } - const success = await clippie(fileMarkdown); - showTemporaryTooltip(e.target, success ? i18n.copy_success : i18n.copy_error); - }); - file.previewTemplate.append(copyLinkElement); - }); - this.on('removedfile', (file) => { - $(`#${file.uuid}`).remove(); - if ($dropzone.data('remove-url')) { - POST($dropzone.data('remove-url'), { - data: new URLSearchParams({file: file.uuid}), - }); - } - }); - this.on('error', function (file, message) { - showErrorToast(message); - this.removeFile(file); - }); - }, - }); + initDropzone(el); } } +export function initDropzone(el) { + const $dropzone = $(el); + const _promise = createDropzone(el, { + url: $dropzone.data('upload-url'), + headers: {'X-Csrf-Token': csrfToken}, + maxFiles: $dropzone.data('max-file'), + maxFilesize: $dropzone.data('max-size'), + acceptedFiles: (['*/*', ''].includes($dropzone.data('accepts'))) ? null : $dropzone.data('accepts'), + addRemoveLinks: true, + dictDefaultMessage: $dropzone.data('default-message'), + dictInvalidFileType: $dropzone.data('invalid-input-type'), + dictFileTooBig: $dropzone.data('file-too-big'), + dictRemoveFile: $dropzone.data('remove-file'), + timeout: 0, + thumbnailMethod: 'contain', + thumbnailWidth: 480, + thumbnailHeight: 480, + init() { + this.on('success', (file, data) => { + file.uuid = data.uuid; + const input = $(``).val(data.uuid); + $dropzone.find('.files').append(input); + // Create a "Copy Link" element, to conveniently copy the image + // or file link as Markdown to the clipboard + const copyLinkElement = document.createElement('div'); + copyLinkElement.className = 'gt-text-center'; + // The a element has a hardcoded cursor: pointer because the default is overridden by .dropzone + copyLinkElement.innerHTML = `${svg('octicon-copy', 14, 'copy link')} Copy link`; + copyLinkElement.addEventListener('click', async (e) => { + e.preventDefault(); + let fileMarkdown = `[${file.name}](/attachments/${file.uuid})`; + if (file.type.startsWith('image/')) { + fileMarkdown = `!${fileMarkdown}`; + } else if (file.type.startsWith('video/')) { + fileMarkdown = ``; + } + const success = await clippie(fileMarkdown); + showTemporaryTooltip(e.target, success ? i18n.copy_success : i18n.copy_error); + }); + file.previewTemplate.append(copyLinkElement); + }); + this.on('removedfile', (file) => { + $(`#${file.uuid}`).remove(); + if ($dropzone.data('remove-url')) { + POST($dropzone.data('remove-url'), { + data: new URLSearchParams({file: file.uuid}), + }); + } + }); + this.on('error', function (file, message) { + showErrorToast(message); + this.removeFile(file); + }); + }, + }); +} + async function linkAction(e) { // A "link-action" can post AJAX request to its "data-url" // Then the browser is redirected to: the "redirect" in response, or "data-redirect" attribute, or current URL by reloading. diff --git a/web_src/js/features/repo-issue.js b/web_src/js/features/repo-issue.js index 3437565c80..10faeb135d 100644 --- a/web_src/js/features/repo-issue.js +++ b/web_src/js/features/repo-issue.js @@ -5,6 +5,7 @@ import {hideElem, showElem, toggleElem} from '../utils/dom.js'; import {setFileFolding} from './file-fold.js'; import {getComboMarkdownEditor, initComboMarkdownEditor} from './comp/ComboMarkdownEditor.js'; import {toAbsoluteUrl} from '../utils.js'; +import {initDropzone} from './common-global.js'; const {appSubUrl, csrfToken} = window.config; @@ -382,6 +383,11 @@ export async function handleReply($el) { const $textarea = form.find('textarea'); let editor = getComboMarkdownEditor($textarea); if (!editor) { + // FIXME: the initialization of the dropzone is not consistent. + // When the page is loaded, the dropzone is initialized by initGlobalDropzone, but the editor is not initialized. + // When the form is submitted and partially reload, none of them is initialized. + const dropzone = form.find('.dropzone')[0]; + if (!dropzone.dropzone) initDropzone(dropzone); editor = await initComboMarkdownEditor(form.find('.combo-markdown-editor')); } editor.focus(); @@ -511,6 +517,7 @@ export function initRepoPullRequestReview() { td.find("input[name='side']").val(side === 'left' ? 'previous' : 'proposed'); td.find("input[name='path']").val(path); + initDropzone(td.find('.dropzone')[0]); const editor = await initComboMarkdownEditor(td.find('.combo-markdown-editor')); editor.focus(); } From 3c9b176348e32c7d7356e175b083752f06ff53aa Mon Sep 17 00:00:00 2001 From: oliverpool Date: Tue, 27 Feb 2024 16:05:59 +0100 Subject: [PATCH 247/271] fix cherry-pick --- routers/web/repo/pull.go | 10 ++++++---- routers/web/repo/pull_review_test.go | 6 ++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 229016fcb5..561039c413 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -989,10 +989,12 @@ func viewPullFiles(ctx *context.Context, specifiedStartCommit, specifiedEndCommi for _, file := range diff.Files { for _, section := range file.Sections { for _, line := range section.Lines { - for _, comment := range line.Comments { - if err := comment.LoadAttachments(ctx); err != nil { - ctx.ServerError("LoadAttachments", err) - return + for _, comments := range line.Conversations { + for _, comment := range comments { + if err := comment.LoadAttachments(ctx); err != nil { + ctx.ServerError("LoadAttachments", err) + return + } } } } diff --git a/routers/web/repo/pull_review_test.go b/routers/web/repo/pull_review_test.go index 8fc9cecaf3..68f68d7bb0 100644 --- a/routers/web/repo/pull_review_test.go +++ b/routers/web/repo/pull_review_test.go @@ -28,7 +28,8 @@ func TestRenderConversation(t *testing.T) { run := func(name string, cb func(t *testing.T, ctx *context.Context, resp *httptest.ResponseRecorder)) { t.Run(name, func(t *testing.T) { - ctx, resp := contexttest.MockContext(t, "/", contexttest.MockContextOption{Render: templates.HTMLRenderer()}) + ctx, resp := contexttest.MockContext(t, "/") + ctx.Render = templates.HTMLRenderer() contexttest.LoadUser(t, ctx, pr.Issue.PosterID) contexttest.LoadRepo(t, ctx, pr.BaseRepoID) contexttest.LoadGitRepo(t, ctx) @@ -61,7 +62,8 @@ func TestRenderConversation(t *testing.T) { run("diff without outdated", func(t *testing.T, ctx *context.Context, resp *httptest.ResponseRecorder) { ctx.Data["ShowOutdatedComments"] = false renderConversation(ctx, preparedComment, "diff") - assert.Contains(t, resp.Body.String(), `conversation-not-existing`) + // unlike gitea, Forgejo renders the conversation (with the "outdated" label) + assert.Contains(t, resp.Body.String(), `repo.issues.review.outdated_description`) }) run("timeline with outdated", func(t *testing.T, ctx *context.Context, resp *httptest.ResponseRecorder) { ctx.Data["ShowOutdatedComments"] = true From e154655c8777d4587113b9992291b623f70b81da Mon Sep 17 00:00:00 2001 From: oliverpool Date: Tue, 27 Feb 2024 16:18:30 +0100 Subject: [PATCH 248/271] fix missing argument --- routers/api/v1/repo/pull_review.go | 1 + 1 file changed, 1 insertion(+) diff --git a/routers/api/v1/repo/pull_review.go b/routers/api/v1/repo/pull_review.go index efb077cbae..e39a096add 100644 --- a/routers/api/v1/repo/pull_review.go +++ b/routers/api/v1/repo/pull_review.go @@ -339,6 +339,7 @@ func CreatePullReviewComment(ctx *context.APIContext) { opts.Path, line, review.ID, + nil, ) if err != nil { ctx.InternalServerError(err) From aef0b024b7275cb706a05d1822652f527f617ffc Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 27 Feb 2024 19:08:16 +0100 Subject: [PATCH 249/271] Fix the Fork button in repo headers In #2445, I lifted out the fork button into its own template, but did not update it properly. This resulted in the fork button's counter not displaying, and pointing to the wrong place too. This patch updates the template to account for it moving to a separate file, and also adds test cases to verify the button is display as it should be. Fixes #2494. Signed-off-by: Gergely Nagy --- templates/repo/header_fork.tmpl | 2 ++ tests/integration/repo_fork_test.go | 39 +++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/templates/repo/header_fork.tmpl b/templates/repo/header_fork.tmpl index 5bce9e0f14..e248a77850 100644 --- a/templates/repo/header_fork.tmpl +++ b/templates/repo/header_fork.tmpl @@ -1,3 +1,4 @@ +{{with .Repository}} {{if and (not .IsEmpty) ($.Permission.CanRead $.UnitTypeCode)}}
      Date: Wed, 7 Feb 2024 17:36:31 +0100 Subject: [PATCH 250/271] [Swagger] update to latest version --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9dd0abb492..45493e5d62 100644 --- a/Makefile +++ b/Makefile @@ -32,7 +32,7 @@ GOFUMPT_PACKAGE ?= mvdan.cc/gofumpt@v0.6.0 GOLANGCI_LINT_PACKAGE ?= github.com/golangci/golangci-lint/cmd/golangci-lint@v1.56.1 GXZ_PACKAGE ?= github.com/ulikunitz/xz/cmd/gxz@v0.5.11 MISSPELL_PACKAGE ?= github.com/golangci/misspell/cmd/misspell@v0.4.1 -SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.5 +SWAGGER_PACKAGE ?= github.com/go-swagger/go-swagger/cmd/swagger@v0.30.6-0.20240201115257-bcc7c78b7786 XGO_PACKAGE ?= src.techknowlogick.com/xgo@latest GO_LICENSES_PACKAGE ?= github.com/google/go-licenses@v1.6.0 GOVULNCHECK_PACKAGE ?= golang.org/x/vuln/cmd/govulncheck@v1.0.3 From fb2795b5bb3a119f022b59a7b46af47eff94270f Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 27 Feb 2024 22:12:20 +0100 Subject: [PATCH 251/271] [BUG] Correct changed files for codeowners - The CODEOWNER feature relies on the changed files to determine which reviewers should be added according to the `CODEOWNER` file. - The current approach was to 'diff' between the base and head branch, which seems logical but fail in practice when the pull request is out of date with the base branch. Therefore it should instead diff between the head branch and the merge base of the head and base branch, so only the actual affected files by the pull requests are used, the same approach is used by the diff of an unmerged pull request. - Add integration testing (for the feature as well). - Resolves #2458 --- models/issues/pull.go | 9 ++- tests/integration/codeowner_test.go | 118 ++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 1 deletion(-) create mode 100644 tests/integration/codeowner_test.go diff --git a/models/issues/pull.go b/models/issues/pull.go index 7d299eac27..98d1617380 100644 --- a/models/issues/pull.go +++ b/models/issues/pull.go @@ -922,7 +922,14 @@ func PullRequestCodeOwnersReview(ctx context.Context, pull *Issue, pr *PullReque } rules, _ := GetCodeOwnersFromContent(ctx, data) - changedFiles, err := repo.GetFilesChangedBetween(git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName()) + + prInfo, err := repo.GetCompareInfo(repo.Path, git.BranchPrefix+pr.BaseBranch, pr.GetGitRefName(), false, false) + if err != nil { + return err + } + // Use the merge base as the base instead of the main branch to avoid problems + // if the pull request is out of date with the base branch. + changedFiles, err := repo.GetFilesChangedBetween(prInfo.MergeBase, pr.HeadCommitID) if err != nil { return err } diff --git a/tests/integration/codeowner_test.go b/tests/integration/codeowner_test.go new file mode 100644 index 0000000000..e1324782f8 --- /dev/null +++ b/tests/integration/codeowner_test.go @@ -0,0 +1,118 @@ +// Copyright 2024 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package integration + +import ( + "context" + "fmt" + "net/url" + "os" + "path" + "strings" + "testing" + "time" + + issues_model "code.gitea.io/gitea/models/issues" + unit_model "code.gitea.io/gitea/models/unit" + "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" + files_service "code.gitea.io/gitea/services/repository/files" + "code.gitea.io/gitea/tests" + "github.com/stretchr/testify/assert" +) + +func TestCodeOwner(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + // Create the repo. + repo, _, f := 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"), + }, + }, + ) + defer f() + + dstPath := t.TempDir() + r := fmt.Sprintf("%suser2/%s.git", u.String(), repo.Name) + u, _ = url.Parse(r) + u.User = url.UserPassword("user2", userPassword) + assert.NoError(t, git.CloneWithArgs(context.Background(), nil, u.String(), dstPath, git.CloneRepoOptions{})) + + t.Run("Normal", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + err := os.WriteFile(path.Join(dstPath, "README.md"), []byte("## test content"), 0o666) + assert.NoError(t, err) + + err = git.AddChanges(dstPath, true) + assert.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.", + }) + assert.NoError(t, err) + + err = git.NewCommand(git.DefaultContext, "push", "origin", "HEAD:refs/for/main", "-o", "topic=codeowner-normal").Run(&git.RunOpts{Dir: dstPath}) + assert.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}) + }) + + t.Run("Out of date", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + // Push the changes made from the previous subtest. + assert.NoError(t, git.NewCommand(git.DefaultContext, "push", "origin").Run(&git.RunOpts{Dir: dstPath})) + + // Reset the tree to the previous commit. + assert.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) + assert.NoError(t, err) + + err = git.AddChanges(dstPath, true) + assert.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.", + }) + assert.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}) + assert.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}) + }) + }) +} From 64a0d61aff6dd55ee61f600add4a06a8e5e16c7d Mon Sep 17 00:00:00 2001 From: Gusted Date: Sun, 18 Feb 2024 23:41:54 +0500 Subject: [PATCH 252/271] [BUG] Implement commit mail selection for other Git operations - Implement the commit mail selection feature for the other supported Git operations that can be done trough the web UI. - Adds integration tests (goodluck reviewing this). - Ref: #1788 Co-authored-by: 0ko <0ko@noreply.codeberg.org> --- routers/web/repo/cherry_pick.go | 7 + routers/web/repo/editor.go | 101 +++-- routers/web/repo/patch.go | 7 + routers/web/web.go | 2 +- services/forms/repo_form.go | 3 + services/repository/files/upload.go | 6 +- tests/integration/editor_test.go | 393 +++++++++++++----- tests/integration/empty_repo_test.go | 9 +- .../repo_mergecommit_revert_test.go | 1 + 9 files changed, 377 insertions(+), 152 deletions(-) diff --git a/routers/web/repo/cherry_pick.go b/routers/web/repo/cherry_pick.go index 8de54d569f..63516bb4d9 100644 --- a/routers/web/repo/cherry_pick.go +++ b/routers/web/repo/cherry_pick.go @@ -115,11 +115,18 @@ func CherryPickPost(ctx *context.Context) { message += "\n\n" + form.CommitMessage } + gitIdentity := getGitIdentity(ctx, form.CommitMailID, tplCherryPick, &form) + if ctx.Written() { + return + } + opts := &files.ApplyDiffPatchOptions{ LastCommitID: form.LastCommit, OldBranch: ctx.Repo.BranchName, NewBranch: branchName, Message: message, + Author: gitIdentity, + Committer: gitIdentity, } // First lets try the simple plain read-tree -m approach diff --git a/routers/web/repo/editor.go b/routers/web/repo/editor.go index 93decc085b..075477e5f0 100644 --- a/routers/web/repo/editor.go +++ b/routers/web/repo/editor.go @@ -121,6 +121,18 @@ func getSelectableEmailAddresses(ctx *context.Context) ([]*user_model.ActivatedE return commitEmails, nil } +// CommonEditorData sets common context data that is used by the editor. +func CommonEditorData(ctx *context.Context) { + // Set context for selectable email addresses. + commitEmails, err := getSelectableEmailAddresses(ctx) + if err != nil { + ctx.ServerError("getSelectableEmailAddresses", err) + return + } + ctx.Data["CommitMails"] = commitEmails + ctx.Data["DefaultCommitMail"] = ctx.Doer.GetEmail() +} + func editFile(ctx *context.Context, isNewFile bool) { ctx.Data["PageIsEdit"] = true ctx.Data["IsNewFile"] = isNewFile @@ -196,12 +208,6 @@ func editFile(ctx *context.Context, isNewFile bool) { treeNames = append(treeNames, fileName) } - commitEmails, err := getSelectableEmailAddresses(ctx) - if err != nil { - ctx.ServerError("getSelectableEmailAddresses", err) - return - } - ctx.Data["TreeNames"] = treeNames ctx.Data["TreePaths"] = treePaths ctx.Data["BranchLink"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() @@ -217,8 +223,6 @@ func editFile(ctx *context.Context, isNewFile bool) { ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",") ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, treePath) - ctx.Data["CommitMails"] = commitEmails - ctx.Data["DefaultCommitMail"] = ctx.Doer.GetEmail() ctx.HTML(http.StatusOK, tplEditFile) } @@ -254,12 +258,6 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b branchName = form.NewBranchName } - commitEmails, err := getSelectableEmailAddresses(ctx) - if err != nil { - ctx.ServerError("getSelectableEmailAddresses", err) - return - } - ctx.Data["PageIsEdit"] = true ctx.Data["PageHasPosted"] = true ctx.Data["IsNewFile"] = isNewFile @@ -276,8 +274,6 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b ctx.Data["PreviewableExtensions"] = strings.Join(markup.PreviewableExtensions(), ",") ctx.Data["LineWrapExtensions"] = strings.Join(setting.Repository.Editor.LineWrapExtensions, ",") ctx.Data["EditorconfigJson"] = GetEditorConfig(ctx, form.TreePath) - ctx.Data["CommitMails"] = commitEmails - ctx.Data["DefaultCommitMail"] = ctx.Doer.GetEmail() if ctx.HasError() { ctx.HTML(http.StatusOK, tplEditFile) @@ -312,28 +308,9 @@ func editFilePost(ctx *context.Context, form forms.EditRepoFileForm, isNewFile b operation = "create" } - gitIdentity := &files_service.IdentityOptions{ - Name: ctx.Doer.Name, - } - - // -1 is defined as placeholder email. - if form.CommitMailID == -1 { - gitIdentity.Email = ctx.Doer.GetPlaceholderEmail() - } else { - // Check if the given email is activated. - email, err := user_model.GetEmailAddressByID(ctx, ctx.Doer.ID, form.CommitMailID) - if err != nil { - ctx.ServerError("GetEmailAddressByID", err) - return - } - - if email == nil || !email.IsActivated { - ctx.Data["Err_CommitMailID"] = true - ctx.RenderWithErr(ctx.Tr("repo.editor.invalid_commit_mail"), tplEditFile, &form) - return - } - - gitIdentity.Email = email.Email + gitIdentity := getGitIdentity(ctx, form.CommitMailID, tplEditFile, form) + if ctx.Written() { + return } if _, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{ @@ -550,6 +527,11 @@ func DeleteFilePost(ctx *context.Context) { message += "\n\n" + form.CommitMessage } + gitIdentity := getGitIdentity(ctx, form.CommitMailID, tplDeleteFile, &form) + if ctx.Written() { + return + } + if _, err := files_service.ChangeRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.ChangeRepoFilesOptions{ LastCommitID: form.LastCommit, OldBranch: ctx.Repo.BranchName, @@ -560,8 +542,10 @@ func DeleteFilePost(ctx *context.Context) { TreePath: ctx.Repo.TreePath, }, }, - Message: message, - Signoff: form.Signoff, + Message: message, + Signoff: form.Signoff, + Author: gitIdentity, + Committer: gitIdentity, }); err != nil { // This is where we handle all the errors thrown by repofiles.DeleteRepoFile if git.IsErrNotExist(err) || models.IsErrRepoFileDoesNotExist(err) { @@ -760,6 +744,11 @@ func UploadFilePost(ctx *context.Context) { message += "\n\n" + form.CommitMessage } + gitIdentity := getGitIdentity(ctx, form.CommitMailID, tplUploadFile, &form) + if ctx.Written() { + return + } + if err := files_service.UploadRepoFiles(ctx, ctx.Repo.Repository, ctx.Doer, &files_service.UploadRepoFileOptions{ LastCommitID: ctx.Repo.CommitID, OldBranch: oldBranchName, @@ -768,6 +757,8 @@ func UploadFilePost(ctx *context.Context) { Message: message, Files: form.Files, Signoff: form.Signoff, + Author: gitIdentity, + Committer: gitIdentity, }); err != nil { if git_model.IsErrLFSFileLocked(err) { ctx.Data["Err_TreePath"] = true @@ -938,3 +929,33 @@ func GetClosestParentWithFiles(treePath string, commit *git.Commit) string { } return treePath } + +// getGitIdentity returns the Git identity that should be used for an Git +// operation, that takes into account an user's specified email. +func getGitIdentity(ctx *context.Context, commitMailID int64, tpl base.TplName, form any) *files_service.IdentityOptions { + gitIdentity := &files_service.IdentityOptions{ + Name: ctx.Doer.Name, + } + + // -1 is defined as placeholder email. + if commitMailID == -1 { + gitIdentity.Email = ctx.Doer.GetPlaceholderEmail() + } else { + // Check if the given email is activated. + email, err := user_model.GetEmailAddressByID(ctx, ctx.Doer.ID, commitMailID) + if err != nil { + ctx.ServerError("GetEmailAddressByID", err) + return nil + } + + if email == nil || !email.IsActivated { + ctx.Data["Err_CommitMailID"] = true + ctx.RenderWithErr(ctx.Tr("repo.editor.invalid_commit_mail"), tplEditFile, form) + return nil + } + + gitIdentity.Email = email.Email + } + + return gitIdentity +} diff --git a/routers/web/repo/patch.go b/routers/web/repo/patch.go index 00bd45aaec..03ea03467a 100644 --- a/routers/web/repo/patch.go +++ b/routers/web/repo/patch.go @@ -87,12 +87,19 @@ func NewDiffPatchPost(ctx *context.Context) { message += "\n\n" + form.CommitMessage } + gitIdenitity := getGitIdentity(ctx, form.CommitMailID, tplPatchFile, &form) + if ctx.Written() { + return + } + fileResponse, err := files.ApplyDiffPatch(ctx, ctx.Repo.Repository, ctx.Doer, &files.ApplyDiffPatchOptions{ LastCommitID: form.LastCommit, OldBranch: ctx.Repo.BranchName, NewBranch: branchName, Message: message, Content: strings.ReplaceAll(form.Content, "\r", ""), + Author: gitIdenitity, + Committer: gitIdenitity, }) if err != nil { if git_model.IsErrBranchAlreadyExists(err) { diff --git a/routers/web/web.go b/routers/web/web.go index 12fdaea165..fc09ed2b6b 100644 --- a/routers/web/web.go +++ b/routers/web/web.go @@ -1265,7 +1265,7 @@ func registerRoutes(m *web.Route) { Post(web.Bind(forms.EditRepoFileForm{}), repo.NewDiffPatchPost) m.Combo("/_cherrypick/{sha:([a-f0-9]{4,64})}/*").Get(repo.CherryPick). Post(web.Bind(forms.CherryPickForm{}), repo.CherryPickPost) - }, repo.MustBeEditable) + }, repo.MustBeEditable, repo.CommonEditorData) m.Group("", func() { m.Post("/upload-file", repo.UploadFileToServer) m.Post("/upload-remove", web.Bind(forms.RemoveUploadFileForm{}), repo.RemoveUploadFileFromServer) diff --git a/services/forms/repo_form.go b/services/forms/repo_form.go index 3693f935dd..da54f4de61 100644 --- a/services/forms/repo_form.go +++ b/services/forms/repo_form.go @@ -808,6 +808,7 @@ type CherryPickForm struct { CommitChoice string `binding:"Required;MaxSize(50)"` NewBranchName string `binding:"GitRefName;MaxSize(100)"` LastCommit string + CommitMailID int64 `binding:"Required"` Revert bool Signoff bool } @@ -834,6 +835,7 @@ type UploadRepoFileForm struct { CommitChoice string `binding:"Required;MaxSize(50)"` NewBranchName string `binding:"GitRefName;MaxSize(100)"` Files []string + CommitMailID int64 `binding:"Required"` Signoff bool } @@ -868,6 +870,7 @@ type DeleteRepoFileForm struct { CommitChoice string `binding:"Required;MaxSize(50)"` NewBranchName string `binding:"GitRefName;MaxSize(100)"` LastCommit string + CommitMailID int64 `binding:"Required"` Signoff bool } diff --git a/services/repository/files/upload.go b/services/repository/files/upload.go index cbfaf49d13..e8c149f77f 100644 --- a/services/repository/files/upload.go +++ b/services/repository/files/upload.go @@ -25,6 +25,8 @@ type UploadRepoFileOptions struct { NewBranch string TreePath string Message string + Author *IdentityOptions + Committer *IdentityOptions Files []string // In UUID format. Signoff bool } @@ -128,9 +130,7 @@ func UploadRepoFiles(ctx context.Context, repo *repo_model.Repository, doer *use return err } - // make author and committer the doer - author := doer - committer := doer + author, committer := GetAuthorAndCommitterUsers(opts.Author, opts.Committer, doer) // Now commit the tree commitHash, err := t.CommitTree(opts.LastCommitID, author, committer, treeHash, opts.Message, opts.Signoff) diff --git a/tests/integration/editor_test.go b/tests/integration/editor_test.go index 5f005a7320..f2b2716a80 100644 --- a/tests/integration/editor_test.go +++ b/tests/integration/editor_test.go @@ -4,7 +4,10 @@ package integration import ( + "bytes" "fmt" + "io" + "mime/multipart" "net/http" "net/http/httptest" "net/url" @@ -21,6 +24,7 @@ import ( "code.gitea.io/gitea/tests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestCreateFile(t *testing.T) { @@ -183,135 +187,316 @@ func TestEditFileToNewBranch(t *testing.T) { }) } -func TestEditFileCommitMail(t *testing.T) { +func TestCommitMail(t *testing.T) { onGiteaRun(t, func(t *testing.T, _ *url.URL) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + // Require that the user has KeepEmailPrivate enabled, because it needs + // to be tested that even with this setting enabled, it will use the + // provided mail and not revert to the placeholder one. + assert.True(t, user.KeepEmailPrivate) - session := loginUser(t, user.Name) - link := path.Join("user2", "repo1", "_edit", "master", "README.md") + inactivatedMail := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 35, UID: user.ID}) + assert.False(t, inactivatedMail.IsActivated) - lastCommitAndCSRF := func() (string, string) { - req := NewRequest(t, "GET", link) - resp := session.MakeRequest(t, req, http.StatusOK) + otherEmail := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 1, IsActivated: true}) + assert.NotEqualValues(t, otherEmail.UID, user.ID) - htmlDoc := NewHTMLParser(t, resp.Body) - lastCommit := htmlDoc.GetInputValueByName("last_commit") - assert.NotEmpty(t, lastCommit) - - return lastCommit, htmlDoc.GetCSRF() - } - - t.Run("Not activated", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - email := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 35, UID: user.ID}) - assert.False(t, email.IsActivated) - - lastCommit, csrf := lastCommitAndCSRF() - req := NewRequestWithValues(t, "POST", link, map[string]string{ - "_csrf": csrf, - "last_commit": lastCommit, - "tree_path": "README.md", - "content": "new_content", - "commit_choice": "direct", - "commit_mail_id": fmt.Sprintf("%d", email.ID), - }) - resp := session.MakeRequest(t, req, http.StatusOK) - - htmlDoc := NewHTMLParser(t, resp.Body) - assert.Contains(t, - htmlDoc.doc.Find(".ui.negative.message").Text(), - translation.NewLocale("en-US").Tr("repo.editor.invalid_commit_mail"), - ) - }) - - t.Run("Not belong to user", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() - - email := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 1, IsActivated: true}) - assert.NotEqualValues(t, email.UID, user.ID) - - lastCommit, csrf := lastCommitAndCSRF() - req := NewRequestWithValues(t, "POST", link, map[string]string{ - "_csrf": csrf, - "last_commit": lastCommit, - "tree_path": "README.md", - "content": "new_content", - "commit_choice": "direct", - "commit_mail_id": fmt.Sprintf("%d", email.ID), - }) - resp := session.MakeRequest(t, req, http.StatusOK) - - htmlDoc := NewHTMLParser(t, resp.Body) - assert.Contains(t, - htmlDoc.doc.Find(".ui.negative.message").Text(), - translation.NewLocale("en-US").Tr("repo.editor.invalid_commit_mail"), - ) - }) + primaryEmail := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 3, UID: user.ID, IsActivated: true}) repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) gitRepo, _ := git.OpenRepository(git.DefaultContext, repo1.RepoPath()) defer gitRepo.Close() - t.Run("Placeholder mail", func(t *testing.T) { - defer tests.PrintCurrentTest(t)() + session := loginUser(t, user.Name) - lastCommit, csrf := lastCommitAndCSRF() - req := NewRequestWithValues(t, "POST", link, map[string]string{ - "_csrf": csrf, - "last_commit": lastCommit, - "tree_path": "README.md", - "content": "authored by placeholder mail", - "commit_choice": "direct", - "commit_mail_id": "-1", + lastCommitAndCSRF := func(t *testing.T, link string, skipLastCommit bool) (string, string) { + t.Helper() + + req := NewRequest(t, "GET", link) + resp := session.MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + lastCommit := htmlDoc.GetInputValueByName("last_commit") + if !skipLastCommit { + assert.NotEmpty(t, lastCommit) + } + + return lastCommit, htmlDoc.GetCSRF() + } + + type caseOpts struct { + link string + fileName string + base map[string]string + skipLastCommit bool + } + + // Base2 should have different content, so we can test two 'correct' operations + // without the second becoming a noop because no content was changed. If needed, + // link2 can point to a new file that's used with base2. + assertCase := func(t *testing.T, case1, case2 caseOpts) { + t.Helper() + + t.Run("Not activated", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + lastCommit, csrf := lastCommitAndCSRF(t, case1.link, case1.skipLastCommit) + baseCopy := case1.base + baseCopy["_csrf"] = csrf + baseCopy["last_commit"] = lastCommit + baseCopy["commit_mail_id"] = fmt.Sprintf("%d", inactivatedMail.ID) + + req := NewRequestWithValues(t, "POST", case1.link, baseCopy) + resp := session.MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + assert.Contains(t, + htmlDoc.doc.Find(".ui.negative.message").Text(), + translation.NewLocale("en-US").Tr("repo.editor.invalid_commit_mail"), + ) }) - session.MakeRequest(t, req, http.StatusSeeOther) - commit, err := gitRepo.GetCommitByPath("README.md") - assert.NoError(t, err) + t.Run("Not belong to user", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() - fileContent, err := commit.GetFileContent("README.md", 64) - assert.NoError(t, err) - assert.EqualValues(t, "authored by placeholder mail", fileContent) + lastCommit, csrf := lastCommitAndCSRF(t, case1.link, case1.skipLastCommit) + baseCopy := case1.base + baseCopy["_csrf"] = csrf + baseCopy["last_commit"] = lastCommit + baseCopy["commit_mail_id"] = fmt.Sprintf("%d", otherEmail.ID) - assert.EqualValues(t, "user2", commit.Author.Name) - assert.EqualValues(t, "user2@noreply.example.org", commit.Author.Email) - assert.EqualValues(t, "user2", commit.Committer.Name) - assert.EqualValues(t, "user2@noreply.example.org", commit.Committer.Email) + req := NewRequestWithValues(t, "POST", case1.link, baseCopy) + resp := session.MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + assert.Contains(t, + htmlDoc.doc.Find(".ui.negative.message").Text(), + translation.NewLocale("en-US").Tr("repo.editor.invalid_commit_mail"), + ) + }) + + t.Run("Placeholder mail", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + lastCommit, csrf := lastCommitAndCSRF(t, case1.link, case1.skipLastCommit) + baseCopy := case1.base + baseCopy["_csrf"] = csrf + baseCopy["last_commit"] = lastCommit + baseCopy["commit_mail_id"] = "-1" + + req := NewRequestWithValues(t, "POST", case1.link, baseCopy) + session.MakeRequest(t, req, http.StatusSeeOther) + if !case2.skipLastCommit { + newlastCommit, _ := lastCommitAndCSRF(t, case1.link, false) + assert.NotEqualValues(t, newlastCommit, lastCommit) + } + + commit, err := gitRepo.GetCommitByPath(case1.fileName) + assert.NoError(t, err) + + assert.EqualValues(t, "user2", commit.Author.Name) + assert.EqualValues(t, "user2@noreply.example.org", commit.Author.Email) + assert.EqualValues(t, "user2", commit.Committer.Name) + assert.EqualValues(t, "user2@noreply.example.org", commit.Committer.Email) + }) + + t.Run("Normal", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + lastCommit, csrf := lastCommitAndCSRF(t, case2.link, case2.skipLastCommit) + baseCopy := case2.base + baseCopy["_csrf"] = csrf + baseCopy["last_commit"] = lastCommit + baseCopy["commit_mail_id"] = fmt.Sprintf("%d", primaryEmail.ID) + + req := NewRequestWithValues(t, "POST", case2.link, baseCopy) + session.MakeRequest(t, req, http.StatusSeeOther) + if !case2.skipLastCommit { + newlastCommit, _ := lastCommitAndCSRF(t, case2.link, false) + assert.NotEqualValues(t, newlastCommit, lastCommit) + } + + commit, err := gitRepo.GetCommitByPath(case2.fileName) + assert.NoError(t, err) + + assert.EqualValues(t, "user2", commit.Author.Name) + assert.EqualValues(t, primaryEmail.Email, commit.Author.Email) + assert.EqualValues(t, "user2", commit.Committer.Name) + assert.EqualValues(t, primaryEmail.Email, commit.Committer.Email) + }) + } + + t.Run("New", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + assertCase(t, caseOpts{ + fileName: "new_file", + link: "user2/repo1/_new/master", + base: map[string]string{ + "tree_path": "new_file", + "content": "new_content", + "commit_choice": "direct", + }, + }, caseOpts{ + fileName: "new_file_2", + link: "user2/repo1/_new/master", + base: map[string]string{ + "tree_path": "new_file_2", + "content": "new_content", + "commit_choice": "direct", + }, + }, + ) }) - t.Run("Normal", func(t *testing.T) { + t.Run("Edit", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + assertCase(t, caseOpts{ + fileName: "README.md", + link: "user2/repo1/_edit/master/README.md", + base: map[string]string{ + "tree_path": "README.md", + "content": "Edit content", + "commit_choice": "direct", + }, + }, caseOpts{ + fileName: "README.md", + link: "user2/repo1/_edit/master/README.md", + base: map[string]string{ + "tree_path": "README.md", + "content": "Other content", + "commit_choice": "direct", + }, + }, + ) + }) + + t.Run("Delete", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + assertCase(t, caseOpts{ + fileName: "new_file", + link: "user2/repo1/_delete/master/new_file", + base: map[string]string{ + "commit_choice": "direct", + }, + }, caseOpts{ + fileName: "new_file_2", + link: "user2/repo1/_delete/master/new_file_2", + base: map[string]string{ + "commit_choice": "direct", + }, + }, + ) + }) + + t.Run("Upload", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - // Require that the user has KeepEmailPrivate enabled, because it needs - // to be tested that even with this setting enabled, it will use the - // provided mail and not revert to the placeholder one. - assert.True(t, user.KeepEmailPrivate) + // Upload two seperate times, so we have two different 'uploads' that can + // be used indepently of each other. + uploadFile := func(t *testing.T, name, content string) string { + t.Helper() - email := unittest.AssertExistsAndLoadBean(t, &user_model.EmailAddress{ID: 3, UID: user.ID, IsActivated: true}) + body := &bytes.Buffer{} + mpForm := multipart.NewWriter(body) + err := mpForm.WriteField("_csrf", GetCSRF(t, session, "/user2/repo1/_upload/master")) + require.NoError(t, err) - lastCommit, csrf := lastCommitAndCSRF() - req := NewRequestWithValues(t, "POST", link, map[string]string{ - "_csrf": csrf, - "last_commit": lastCommit, - "tree_path": "README.md", - "content": "authored by activated mail", - "commit_choice": "direct", - "commit_mail_id": fmt.Sprintf("%d", email.ID), + file, err := mpForm.CreateFormFile("file", name) + require.NoError(t, err) + + io.Copy(file, bytes.NewBufferString(content)) + require.NoError(t, mpForm.Close()) + + req := NewRequestWithBody(t, "POST", "/user2/repo1/upload-file", body) + req.Header.Add("Content-Type", mpForm.FormDataContentType()) + resp := session.MakeRequest(t, req, http.StatusOK) + + respMap := map[string]string{} + DecodeJSON(t, resp, &respMap) + return respMap["uuid"] + } + + file1UUID := uploadFile(t, "upload_file_1", "Uploaded a file!") + file2UUID := uploadFile(t, "upload_file_2", "Uploaded another file!") + + assertCase(t, caseOpts{ + fileName: "upload_file_1", + link: "user2/repo1/_upload/master", + skipLastCommit: true, + base: map[string]string{ + "commit_choice": "direct", + "files": file1UUID, + }, + }, caseOpts{ + fileName: "upload_file_2", + link: "user2/repo1/_upload/master", + skipLastCommit: true, + base: map[string]string{ + "commit_choice": "direct", + "files": file2UUID, + }, + }, + ) + }) + + t.Run("Apply patch", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + assertCase(t, caseOpts{ + fileName: "diff-file-1.txt", + link: "user2/repo1/_diffpatch/master", + base: map[string]string{ + "tree_path": "patch", + "commit_choice": "direct", + "content": `diff --git a/diff-file-1.txt b/diff-file-1.txt +new file mode 100644 +index 0000000000..50fcd26d6c +--- /dev/null ++++ b/diff-file-1.txt +@@ -0,0 +1 @@ ++File 1 +`, + }, + }, caseOpts{ + fileName: "diff-file-2.txt", + link: "user2/repo1/_diffpatch/master", + base: map[string]string{ + "tree_path": "patch", + "commit_choice": "direct", + "content": `diff --git a/diff-file-2.txt b/diff-file-2.txt +new file mode 100644 +index 0000000000..4475433e27 +--- /dev/null ++++ b/diff-file-2.txt +@@ -0,0 +1 @@ ++File 2 +`, + }, }) - session.MakeRequest(t, req, http.StatusSeeOther) + }) - commit, err := gitRepo.GetCommitByPath("README.md") + t.Run("Cherry pick", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + + commitID1, err := gitRepo.GetCommitByPath("diff-file-1.txt") + assert.NoError(t, err) + commitID2, err := gitRepo.GetCommitByPath("diff-file-2.txt") assert.NoError(t, err) - fileContent, err := commit.GetFileContent("README.md", 64) - assert.NoError(t, err) - assert.EqualValues(t, "authored by activated mail", fileContent) - - assert.EqualValues(t, "user2", commit.Author.Name) - assert.EqualValues(t, email.Email, commit.Author.Email) - assert.EqualValues(t, "user2", commit.Committer.Name) - assert.EqualValues(t, email.Email, commit.Committer.Email) + assertCase(t, caseOpts{ + fileName: "diff-file-1.txt", + link: "user2/repo1/_cherrypick/" + commitID1.ID.String() + "/master", + base: map[string]string{ + "commit_choice": "direct", + "revert": "true", + }, + }, caseOpts{ + fileName: "diff-file-2.txt", + link: "user2/repo1/_cherrypick/" + commitID2.ID.String() + "/master", + base: map[string]string{ + "commit_choice": "direct", + "revert": "true", + }, + }) }) }) } diff --git a/tests/integration/empty_repo_test.go b/tests/integration/empty_repo_test.go index d036c5c2ec..8ab755c4fa 100644 --- a/tests/integration/empty_repo_test.go +++ b/tests/integration/empty_repo_test.go @@ -89,10 +89,11 @@ func TestEmptyRepoUploadFile(t *testing.T) { assert.NoError(t, json.Unmarshal(resp.Body.Bytes(), &respMap)) req = NewRequestWithValues(t, "POST", "/user30/empty/_upload/"+setting.Repository.DefaultBranch, map[string]string{ - "_csrf": GetCSRF(t, session, "/user/settings"), - "commit_choice": "direct", - "files": respMap["uuid"], - "tree_path": "", + "_csrf": GetCSRF(t, session, "/user/settings"), + "commit_choice": "direct", + "files": respMap["uuid"], + "tree_path": "", + "commit_mail_id": "-1", }) resp = session.MakeRequest(t, req, http.StatusSeeOther) redirect := test.RedirectURL(resp) diff --git a/tests/integration/repo_mergecommit_revert_test.go b/tests/integration/repo_mergecommit_revert_test.go index 4d612bdcdb..7041861f11 100644 --- a/tests/integration/repo_mergecommit_revert_test.go +++ b/tests/integration/repo_mergecommit_revert_test.go @@ -26,6 +26,7 @@ func TestRepoMergeCommitRevert(t *testing.T) { "commit_message": "test message", "commit_choice": "direct", "new_branch_name": "test-revert-branch-1", + "commit_mail_id": "-1", }) resp = session.MakeRequest(t, req, http.StatusSeeOther) From 0c4872f8397fec1aa167241673eaeda31e9a0924 Mon Sep 17 00:00:00 2001 From: Gusted Date: Tue, 20 Feb 2024 23:22:30 +0100 Subject: [PATCH 253/271] [CHORE] Update `connect-go` to maintained fork - Update github.com/bufbuild/connect-go to https://github.com/connectrpc/connect-go. - This is a fork that's actively maintained and is recommend by the original library. Looking at the recent release notes, it looks like going in the right direction what one would expect of a library, no strange features being added, lots of improvements. - There's still an indirect dependency by `code.gitea.io/actions-proto-go` on a old version of `connect-go`. --- assets/go-licenses.json | 10 +++++----- go.mod | 10 +++++----- go.sum | 24 +++++++++++------------ routers/api/actions/ping/ping.go | 2 +- routers/api/actions/ping/ping_test.go | 2 +- routers/api/actions/runner/interceptor.go | 2 +- routers/api/actions/runner/runner.go | 2 +- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/assets/go-licenses.json b/assets/go-licenses.json index 48dfabaeae..2aab21595b 100644 --- a/assets/go-licenses.json +++ b/assets/go-licenses.json @@ -24,6 +24,11 @@ "path": "codeberg.org/gusted/mcaptcha/LICENSE", "licenseText": "Copyright © 2022 William Zijl\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n" }, + { + "name": "connectrpc.com/connect", + "path": "connectrpc.com/connect/LICENSE", + "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2021-2024 The Connect Authors\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" + }, { "name": "dario.cat/mergo", "path": "dario.cat/mergo/LICENSE", @@ -229,11 +234,6 @@ "path": "github.com/bradfitz/gomemcache/memcache/LICENSE", "licenseText": "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" }, - { - "name": "github.com/bufbuild/connect-go", - "path": "github.com/bufbuild/connect-go/LICENSE", - "licenseText": " Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright 2021-2022 Buf Technologies, Inc.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n" - }, { "name": "github.com/buildkite/terminal-to-html/v3", "path": "github.com/buildkite/terminal-to-html/v3/LICENSE", diff --git a/go.mod b/go.mod index 3741a413e1..0924b9fdc0 100644 --- a/go.mod +++ b/go.mod @@ -3,10 +3,11 @@ module code.gitea.io/gitea go 1.21 require ( - code.gitea.io/actions-proto-go v0.3.1 + code.gitea.io/actions-proto-go v0.4.0 code.gitea.io/gitea-vet v0.2.3 code.gitea.io/sdk/gitea v0.17.1 codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 + connectrpc.com/connect v1.15.0 gitea.com/go-chi/binding v0.0.0-20230415142243-04b515c6d669 gitea.com/go-chi/cache v0.2.0 gitea.com/go-chi/captcha v0.0.0-20230415143339-2c0754df4384 @@ -19,7 +20,6 @@ require ( github.com/alecthomas/chroma/v2 v2.12.0 github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb github.com/blevesearch/bleve/v2 v2.3.10 - github.com/bufbuild/connect-go v1.10.0 github.com/buildkite/terminal-to-html/v3 v3.10.1 github.com/caddyserver/certmagic v0.20.0 github.com/chi-middleware/proxy v1.1.1 @@ -101,11 +101,11 @@ require ( github.com/yuin/goldmark v1.6.0 github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc github.com/yuin/goldmark-meta v1.1.0 - golang.org/x/crypto v0.18.0 + golang.org/x/crypto v0.19.0 golang.org/x/image v0.15.0 - golang.org/x/net v0.20.0 + golang.org/x/net v0.21.0 golang.org/x/oauth2 v0.16.0 - golang.org/x/sys v0.16.0 + golang.org/x/sys v0.17.0 golang.org/x/text v0.14.0 golang.org/x/tools v0.17.0 google.golang.org/grpc v1.60.1 diff --git a/go.sum b/go.sum index c43b51cd38..f8bf0567de 100644 --- a/go.sum +++ b/go.sum @@ -35,14 +35,16 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -code.gitea.io/actions-proto-go v0.3.1 h1:PMyiQtBKb8dNnpEO2R5rcZdXSis+UQZVo/SciMtR1aU= -code.gitea.io/actions-proto-go v0.3.1/go.mod h1:00ys5QDo1iHN1tHNvvddAcy2W/g+425hQya1cCSvq9A= +code.gitea.io/actions-proto-go v0.4.0 h1:OsPBPhodXuQnsspG1sQ4eRE1PeoZyofd7+i73zCwnsU= +code.gitea.io/actions-proto-go v0.4.0/go.mod h1:mn7Wkqz6JbnTOHQpot3yDeHx+O5C9EGhMEE+htvHBas= code.gitea.io/gitea-vet v0.2.3 h1:gdFmm6WOTM65rE8FUBTRzeQZYzXePKSSB1+r574hWwI= code.gitea.io/gitea-vet v0.2.3/go.mod h1:zcNbT/aJEmivCAhfmkHOlT645KNOf9W2KnkLgFjGGfE= code.gitea.io/sdk/gitea v0.17.1 h1:3jCPOG2ojbl8AcfaUCRYLT5MUcBMFwS0OSK2mA5Zok8= code.gitea.io/sdk/gitea v0.17.1/go.mod h1:aCnBqhHpoEWA180gMbaCtdX9Pl6BWBAuuP2miadoTNM= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570 h1:TXbikPqa7YRtfU9vS6QJBg77pUvbEb6StRdZO8t1bEY= codeberg.org/gusted/mcaptcha v0.0.0-20220723083913-4f3072e1d570/go.mod h1:IIAjsijsd8q1isWX8MACefDEgTQslQ4stk2AeeTt3kM= +connectrpc.com/connect v1.15.0 h1:lFdeCbZrVVDydAqwr4xGV2y+ULn+0Z73s5JBj2LikWo= +connectrpc.com/connect v1.15.0/go.mod h1:bQmjpDY8xItMnttnurVgOkHUBMRT9cpsNi2O4AjKhmA= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -173,8 +175,6 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= -github.com/bufbuild/connect-go v1.10.0 h1:QAJ3G9A1OYQW2Jbk3DeoJbkCxuKArrvZgDt47mjdTbg= -github.com/bufbuild/connect-go v1.10.0/go.mod h1:CAIePUgkDR5pAFaylSMtNK45ANQjp9JvpluG20rhpV8= github.com/buildkite/terminal-to-html/v3 v3.10.1 h1:znT9eD26LQ59dDJJEpMCwkP4wEptEAPi74hsTBuHdEo= github.com/buildkite/terminal-to-html/v3 v3.10.1/go.mod h1:qtuRyYs6/Sw3FS9jUyVEaANHgHGqZsGqMknPLyau5cQ= github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= @@ -898,8 +898,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= -golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -981,8 +981,8 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= -golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1061,8 +1061,8 @@ golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1073,8 +1073,8 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= -golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/routers/api/actions/ping/ping.go b/routers/api/actions/ping/ping.go index 55219fe12b..13985c93a3 100644 --- a/routers/api/actions/ping/ping.go +++ b/routers/api/actions/ping/ping.go @@ -12,7 +12,7 @@ import ( pingv1 "code.gitea.io/actions-proto-go/ping/v1" "code.gitea.io/actions-proto-go/ping/v1/pingv1connect" - "github.com/bufbuild/connect-go" + "connectrpc.com/connect" ) func NewPingServiceHandler() (string, http.Handler) { diff --git a/routers/api/actions/ping/ping_test.go b/routers/api/actions/ping/ping_test.go index f39e94a1f3..098b003ea2 100644 --- a/routers/api/actions/ping/ping_test.go +++ b/routers/api/actions/ping/ping_test.go @@ -11,7 +11,7 @@ import ( pingv1 "code.gitea.io/actions-proto-go/ping/v1" "code.gitea.io/actions-proto-go/ping/v1/pingv1connect" - "github.com/bufbuild/connect-go" + "connectrpc.com/connect" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/routers/api/actions/runner/interceptor.go b/routers/api/actions/runner/interceptor.go index ddc754dbc7..c2f4ade174 100644 --- a/routers/api/actions/runner/interceptor.go +++ b/routers/api/actions/runner/interceptor.go @@ -15,7 +15,7 @@ import ( "code.gitea.io/gitea/modules/timeutil" "code.gitea.io/gitea/modules/util" - "github.com/bufbuild/connect-go" + "connectrpc.com/connect" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) diff --git a/routers/api/actions/runner/runner.go b/routers/api/actions/runner/runner.go index 8df6f297ce..caaad2b83b 100644 --- a/routers/api/actions/runner/runner.go +++ b/routers/api/actions/runner/runner.go @@ -16,7 +16,7 @@ import ( runnerv1 "code.gitea.io/actions-proto-go/runner/v1" "code.gitea.io/actions-proto-go/runner/v1/runnerv1connect" - "github.com/bufbuild/connect-go" + "connectrpc.com/connect" gouuid "github.com/google/uuid" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" From 1a8f579b6eab2121ec4c5b03c144dabdb4769c22 Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Wed, 28 Feb 2024 10:27:44 +0000 Subject: [PATCH 254/271] [I18N] Translations update from Weblate (#2479) Translations update from [Weblate](https://translate.codeberg.org) for [Forgejo/forgejo](https://translate.codeberg.org/projects/forgejo/forgejo/). Current translation status: ![Weblate translation status](https://translate.codeberg.org/widget/forgejo/forgejo/horizontal-auto.svg) Co-authored-by: b1nar10 Co-authored-by: Gusted Co-authored-by: Salif Mehmed Co-authored-by: 0ko <0ko@users.noreply.translate.codeberg.org> Co-authored-by: mondstern Co-authored-by: Xinayder Co-authored-by: earl-warren Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2479 Co-authored-by: Codeberg Translate Co-committed-by: Codeberg Translate --- options/locale/locale_bg.ini | 730 ++++++++++++++++++++++++++++++-- options/locale/locale_cs-CZ.ini | 3 + options/locale/locale_de-DE.ini | 1 + options/locale/locale_el-GR.ini | 3 + options/locale/locale_es-ES.ini | 1 + options/locale/locale_fr-FR.ini | 51 ++- options/locale/locale_gl.ini | 143 +++++++ options/locale/locale_ja-JP.ini | 3 + options/locale/locale_nl-NL.ini | 170 ++++---- options/locale/locale_pt-BR.ini | 45 +- options/locale/locale_pt-PT.ini | 2 + options/locale/locale_ru-RU.ini | 146 ++++--- options/locale/locale_sl.ini | 45 +- options/locale/locale_tr-TR.ini | 3 + 14 files changed, 1120 insertions(+), 226 deletions(-) create mode 100644 options/locale/locale_gl.ini diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index 08554a5b8d..de2c40c174 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -9,18 +9,18 @@ visibility = Видимост на потребителя location = Местоположение password = Парола appearance = Облик -new_password = Нова Парола +new_password = Нова парола oauth2_application_edit = Редактиране repos = Хранилища can_write_info = Писане delete = Изтриване на акаунта -social = Социални Акаунти -twofa = Двуфакторно Удостоверяване (TOTP) +social = Социални акаунти +twofa = Двуфакторно удостоверяване (TOTP) update_theme = Обновяване на темата can_read_info = Четене access_token_deletion_confirm_action = Изтриване website = Уебсайт -cancel = Отмяна +cancel = Отказ delete_token = Изтриване uid = UID language = Език @@ -28,7 +28,7 @@ save_application = Запазване privacy = Поверителност avatar = Профилна снимка add_key = Добавяне на ключ -account_link = Свързани Акаунти +account_link = Свързани акаунти delete_email = Премахване update_language = Обновяване на езика organization = Организации @@ -37,8 +37,8 @@ add_new_gpg_key = Добавяне на GPG ключ manage_gpg_keys = Управление на GPG ключовете manage_ssh_keys = Управление на SSH ключовете old_password = Текуща парола -public_profile = Публичен Профил -full_name = Пълно Име +public_profile = Публичен профил +full_name = Пълно име security = Сигурност add_new_key = Добавяне на SSH ключ account = Акаунт @@ -51,15 +51,102 @@ update_password = Обновяване на паролата biography_placeholder = Разкажете ни малко за себе си! (Можете да използвате Markdown) orgs = Управление на организациите continue = Продължаване -blocked_users = Блокирани Потребители +blocked_users = Блокирани потребители emails = Адреси на ел. поща update_profile = Обновяване на профила profile = Профил +change_password = Промяна на паролата +retype_new_password = Потвърдете новата парола +choose_new_avatar = Изберете нова профилна снимка +delete_current_avatar = Изтриване на текущата профилна снимка +gpg_key_deletion_success = GPG ключът е премахнат. +permission_no_access = Без достъп +ssh_key_deletion_success = SSH ключът е премахнат. +comment_type_group_project = Проект +update_language_success = Езикът е обновен. +add_key_success = SSH ключът "%s" е добавен. +add_gpg_key_success = GPG ключът "%s" е добавен. +user_unblock_success = Потребителят е отблокиран успешно. +user_block_success = Потребителят е блокиран успешно. +update_profile_success = Профилът ви е обновен. +update_user_avatar_success = Профилната снимка на потребителя е обновена. +remove_oauth2_application_success = Приложението е изтрито. +email_deletion_success = Адресът на ел. поща е премахнат. +update_avatar_success = Профилната ви снимка е обновена. +change_username = Потребителското ви име е променено. +comment_type_group_assignee = Изпълнител +enable_custom_avatar = Използване на персонализирана профилна снимка +requires_activation = Изисква активиране +activated = Активиран +primary = Основен +email_deletion = Премахване на адреса на ел. поща +add_new_email = Добавяне на нов адрес на ел. поща +add_email = Добавяне на адрес на ел. поща +key_content_gpg_placeholder = Започва с "-----BEGIN PGP PUBLIC KEY BLOCK-----" +comment_type_group_title = Заглавие +comment_type_group_label = Етикет +change_username_prompt = Забележка: Промяната на потребителското ви име променя също URL на вашия акаунт. +update_language_not_found = Езикът "%s" не е наличен. +keep_activity_private_popup = Прави дейността видима само за вас и администраторите +uploaded_avatar_not_a_image = Каченият файл не е изображение. +uploaded_avatar_is_too_big = Размерът на качения файл (%d KiB) надвишава максималния размер (%d KiB). +change_password_success = Паролата ви е обновена. Влизайте с новата си парола от сега нататък. +manage_themes = Избор на тема по подразбиране +manage_openid = Управление на OpenID адресите +primary_email = Да е основен +keep_email_private = Скриване на адреса на ел. поща +theme_update_error = Избраната тема не съществува. +theme_update_success = Темата ви е обновена. +key_content_ssh_placeholder = Започва с "ssh-ed25519", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521", "sk-ecdsa-sha2-nistp256@openssh.com", или "sk-ssh-ed25519@openssh.com" +hide_openid = Скриване от профила +key_content = Съдържание +ssh_key_deletion = Премахване на SSH ключ +gpg_key_deletion = Премахване на GPG ключ +key_name = Име на ключа +key_id = ID на ключа +show_openid = Показване в профила +visibility.public = Публична +visibility.limited = Ограничена +visibility.private = Частна +location_placeholder = Споделете приблизителното си местоположение с другите +key_signature_gpg_placeholder = Започва с "-----BEGIN PGP SIGNATURE-----" +key_signature_ssh_placeholder = Започва с "-----BEGIN SSH SIGNATURE-----" +saved_successfully = Настройките бяха запазени успешно. +no_activity = Няма скорошна дейност +theme_desc = Това ще бъде вашата тема по подразбиране в целия сайт. +keep_activity_private = Скриване на дейността от профилната страница +lookup_avatar_by_mail = Търсене на профилна снимка по адреса на ел. поща +password_incorrect = Текущата парола е неправилна. +change_username_redirect_prompt = Старото потребителско име ще се пренасочва, докато някой не го вземе. +principal_content = Съдържание +manage_ssh_principals = Управление на SSH Certificate Principals +twofa_disabled = Двуфакторното удостоверяване е изключено. +orgs_none = Не сте участник в никакви организации. +repos_none = Не притежавате никакви хранилища. +blocked_users_none = Няма блокирани потребители. +profile_desc = Контролирайте как вашият профил се показва на другите потребители. Вашият основен адрес на ел. поща ще се използва за известия, възстановяване на паролата и уеб базирани Git операции. +permission_write = Четене и Писане +twofa_disable = Изключване на двуфакторното удостоверяване +twofa_enroll = Включване на двуфакторно удостоверяване [packages] container.labels.value = Стойност alpine.repository.repositories = Хранилища dependency.version = Версия +title = Пакети +empty = Все още няма пакети. +empty.documentation = За повече информация относно регистъра на пакетите вижте документацията. +container.labels.key = Ключ +requirements = Изисквания +details = Подробности +details.license = Лиценз +container.labels = Етикети +versions = Версии +empty.repo = Качихте ли пакет, но той не се показва тук? Отидете в настройките за пакети и го свържете към това хранилище. +keywords = Ключови думи +details.author = Автор +about = Относно този пакет +settings.delete.success = Пакетът бе изтрит. [tool] hours = %d часа @@ -97,7 +184,7 @@ your_settings = Настройки mirrors = Огледала explore = Разглеждане write = Писане -twofa = Двуфакторно Удостоверяване +twofa = Двуфакторно удостоверяване version = Версия copy_success = Копирано! help = Помощ @@ -119,30 +206,30 @@ dashboard = Табло logo = Лого toc = Съдържание copy_url = Копиране на URL -new_mirror = Ново Огледало -re_type = Потвърждение на паролата +new_mirror = Ново огледало +re_type = Потвърдете паролата copy = Копиране enabled = Включено -new_org = Нова Организация +new_org = Нова организация milestones = Етапи rss_feed = RSS Емисия never = Никога -new_project = Нов Проект +new_project = Нов проект your_starred = Отбелязани value = Стойност sources = Източници notifications = Известия repository = Хранилище add_all = Добавяне на всичко -new_project_column = Нова Колона +new_project_column = Нова колона add = Добавяне organization = Организация -new_migrate = Нова Миграция +new_migrate = Нова миграция save = Запазване sign_in_with_provider = Влизане с %s ok = Добре manage_org = Управление на организациите -new_repo = Ново Хранилище +new_repo = Ново хранилище register = Регистрация mirror = Огледало username = Потребителско име @@ -156,6 +243,16 @@ email = Адрес на ел. поща issues = Задачи retry = Повторен опит remove = Премахване +admin_panel = Администриране на сайта +account_settings = Настройки на акаунта +powered_by = Осъществено от %s +pull_requests = Заявки за сливане +collaborative = Съвместни +all = Всички +activities = Дейности +new_fork = Ново разклонение на хранилище +unpin = Откачване +pin = Закачване [repo] issues.context.edit = Редактиране @@ -173,11 +270,11 @@ settings.update_settings = Обновяване на настройките visibility = Видимост settings.site = Уебсайт watchers = Наблюдаващи -projects.new = Нов Проект +projects.new = Нов проект issues.dependency.remove = Премахване issues.filter_sort.moststars = Най-много звезди desc.template = Шаблон -activity.new_issues_count_n = Нови Задачи +activity.new_issues_count_n = Нови задачи settings.options = Хранилище activity.overview = Обзор fork = Разклоняване @@ -187,7 +284,7 @@ projects.description_placeholder = Описание issues.filter_sort.feweststars = Най-малко звезди code = Код repo_desc = Описание -no_desc = Няма Описание +no_desc = Няма описание milestones = Етапи issues.label_description = Описание wiki.page = Страница @@ -198,10 +295,10 @@ issues.filter_sort.fewestforks = Най-малко разклонения star = Отбелязване issues.edit = Редактиране issues.create = Създаване на задача -activity.new_issues_count_1 = Нова Задача +activity.new_issues_count_1 = Нова задача milestones.desc = Описание issues.new.milestone = Етап -issues.new = Нова Задача +issues.new = Нова задача release.source_code = Програмен код settings = Настройки forks = Разклонения @@ -226,8 +323,440 @@ issues.label.filter_sort.alphabetically = По азбучен ред settings.event_repository = Хранилище issues.label.filter_sort.reverse_alphabetically = По низходящ азбучен ред issues.filter_sort.oldest = Най-стари -issues.filter_sort = Сортиране +issues.filter_sort = Подреждане issues.filter_sort.latest = Най-нови +projects.column.new = Нова колона +issues.closed_title = Затворени +issues.context.quote_reply = Цитиране в отговор +issues.context.reference_issue = Препратка в нова задача +milestones.title = Заглавие +packages = Пакети +settings.title = Заглавие +pulls.status_checks_details = Подробности +issues.context.copy_link = Копиране на връзката +projects.column.new_submit = Създаване на колона +projects = Проекти +projects.create = Създаване на проект +projects.title = Заглавие +issues.filter_sort.recentupdate = Наскоро обновени +issues.filter_sort.leastupdate = Отдавна обновени +issues.next = Следваща +issues.open_title = Отворени +pulls.made_using_agit = AGit +issues.num_comments = %d коментара +issues.filter_sort.leastcomment = Най-малко коментирани +issues.filter_sort.mostcomment = Най-много коментирани +issues.keyword_search_unavailable = В момента търсенето по ключова дума не е налично. Моля, свържете се с вашия администратор на сайта. +repo_desc_helper = Въведете кратко описание (опционално) +mirror_address = Клониране от URL +owner_helper = Някои организации може да не се показват в падащото меню поради ограничение за максимален брой хранилища. +new_repo_helper = Хранилище съдържа всички файлове на проекта, включително хронологията на ревизиите. Вече хоствате хранилище другаде? Мигрирайте хранилище. +repo_name_helper = Добрите имена на хранилища използват кратки, запомнящи се и уникални ключови думи. +migrated_from = Мигрирано от %[2]s +visibility_description = Само притежателят или участниците в организацията, ако имат права, ще могат да го видят. +projects.description = Описание (опционално) +template_select = Изберете шаблон. +visibility_helper = Хранилището да е частно +license = Лиценз +license_helper = Изберете лицензионен файл. +readme = README +migrate.clone_address = Мигриране / Клониране от URL +migrated_from_fake = Мигрирано от %[1]s +migrate.migrate = Мигриране от %s +settings.search_user_placeholder = Потърсете потребител… +issues.new_label = Нов етикет +issues.new_label_placeholder = Име на етикета +issues.label_count = %d етикета +issues.new.labels = Етикети +issues.new.clear_labels = Изчистване на етикетите +issues.create_label = Създаване на етикет +issues.filter_label_no_select = Всички етикети +milestones.filter_sort.least_issues = Най-малко задачи +milestones.filter_sort.most_issues = Най-много задачи +settings.add_webhook = Добавяне на уеб-кука +template.webhooks = Уеб-куки +issues.label_templates.info = Все още няма етикети. Създайте етикет с "Нов етикет" или използвайте предварително зададен набор от етикети: +labels = Етикети +license_helper_desc = Лицензът определя какво могат и какво не могат да правят другите с вашия код. Не сте сигурни кой е подходящ за вашия проект? Вижте Избиране на лиценз. +issues.choose.blank = По подразбиране +settings.hooks = Уеб-куки +issue_labels = Етикети за задачите +issue_labels_helper = Изберете набор от етикети за задачите. +readme_helper_desc = Това е мястото, където можете да напишете пълно описание на вашия проект. +repo_gitignore_helper = Изберете .gitignore шаблони. +auto_init = Да се инициализира хранилище (Добавя .gitignore, Лиценз и README) +template.issue_labels = Етикети за задачите +migrate_items_labels = Етикети +issues.label_templates.title = Зареждане на предварително зададен набор от етикети +issues.label_templates.helper = Изберете набор от етикети +projects.template.desc = Шаблон +projects.card_type.text_only = Само текст +projects.card_type.images_and_text = Изображения и текст +wiki = Уики +wiki.welcome = Добре дошли в Уикито. +wiki.create_first_page = Създаване на първата страница +editor.upload_file = Качване на файл +projects.column.color = Цвят +editor.cancel_lower = Отказ +pulls = Заявки за сливане +editor.upload_files_to_dir = Качване на файлове в "%s" +settings.slack_color = Цвят +issues.label_color = Цвят +create_new_repo_command = Създаване на ново хранилище в командния ред +editor.new_file = Нов файл +wiki.welcome_desc = Уикито ви позволява да пишете и споделяте документация със сътрудници. +wiki.cancel = Отказ +projects.template.desc_helper = Изберете шаблон за проекта, за да започнете +issues.cancel = Отказ +settings.transfer_owner = Нов притежател +wiki.new_page_button = Нова страница +commit_graph.color = Цвят +projects.create_success = Проектът "%s" е създаден. +projects.type.none = Няма +projects.new_subheader = Координирайте, проследявайте и обновявайте работата си на едно място, така че проектите да останат прозрачни и по график. +projects.open = Отваряне +projects.close = Затваряне +milestones.new = Нов етап +milestones.cancel = Отказ +settings.http_method = HTTP Метод +clone_helper = Нуждаете се от помощ за клониране? Посетете Помощ. +migrate_items_pullrequests = Заявки за сливане +migrate_items_wiki = Уики +quick_guide = Бързо ръководство +clone_this_repo = Клонирайте това хранилище +push_exist_repo = Изтласкване на съществуващо хранилище от командния ред +editor.cancel = Отказ +projects.column.new_title = Име +projects.column.edit_title = Име +fork_from = Разклоняване от +diff.comment.placeholder = Оставете коментар +projects.edit = Редактиране на проекта +projects.modify = Редактиране на проекта +issues.new.no_label = Няма етикет +issues.new.title_empty = Заглавието не може да бъде празно +issues.new.projects = Проекти +issues.new.clear_projects = Изчистване на проектите +issues.new.no_projects = Няма проект +issues.new.open_projects = Отворени проекти +issues.new.closed_projects = Затворени проекти +milestones.update_ago = Обновен %s +issues.filter_type.assigned_to_you = Възложени на вас +issues.filter_type.created_by_you = Създадени от вас +issues.filter_type.mentioning_you = Споменаващи ви +issues.filter_sort.farduedate = Най-далечен краен срок +issues.filter_sort.nearduedate = Най-близък краен срок +wiki.edit_page_button = Редактиране +activity.period.monthly = 1 месец +activity.period.quarterly = 3 месеца +activity.period.semiyearly = 6 месеца +activity.title.user_1 = %d потребител +activity.title.user_n = %d потребители +activity.title.prs_n = %d Заявки за сливане +activity.merged_prs_count_1 = Слята заявка за сливане +activity.merged_prs_count_n = Слети заявки за сливане +activity.active_issues_count_1 = %d Активна задача +activity.active_issues_count_n = %d Активни задачи +activity.closed_issues_count_1 = Затворена задача +activity.closed_issues_count_n = Затворени задачи +activity.title.issues_1 = %d Задача +activity.title.issues_n = %d Задачи +wiki.pages = Страници +activity.git_stats_author_1 = %d автор +activity.git_stats_and_deletions = и +project_board = Проекти +wiki.save_page = Запазване на страницата +activity.git_stats_author_n = %d автори +wiki.delete_page_button = Изтриване на страницата +activity.title.prs_1 = %d Заявка за сливане +activity.active_prs_count_n = %d Активни заявки за сливане +activity.period.filter_label = Период: +activity.period.daily = 1 ден +activity.period.halfweekly = 3 дни +activity.period.weekly = 1 седмица +activity.period.yearly = 1 година +activity.active_prs_count_1 = %d Активна заявка за сливане +wiki.page_title = Заглавие на страницата +wiki.page_content = Съдържание на страницата +wiki.filter_page = Филтриране на страница +wiki.back_to_wiki = Обратно към уики страницата +wiki.wiki_page_revisions = Ревизии на уики страницата +wiki.file_revision = Ревизия на страницата +activity.title.issues_created_by = %s създадена от %s +wiki.delete_page_notice_1 = Изтриването на уики страницата "%s" не може да бъде отменено. Продължаване? +wiki.page_name_desc = Въведете име за тази уики страница. Някои специални имена са: "Home", "_Sidebar" и "_Footer". +wiki.page_already_exists = Вече съществува уики страница със същото име. +wiki.reserved_page = Името на уики страницата "%s" е резервирано. +wiki.last_updated = Последно обновяване %s +settings.event_release = Издание +wiki.desc = Пишете и споделяйте документация със сътрудници. +wiki.default_commit_message = Напишете бележка относно това обновяване на страницата (опционално). +release.releases = Издания +wiki.last_commit_info = %s редактира тази страница %s +migrate_items_releases = Издания +release = Издание +releases = Издания +settings.desc = Настройките са мястото, където можете да управлявате настройките за хранилището +settings.external_wiki_url_error = URL на външното уики не е валиден URL. +settings.external_wiki_url = URL на външно уики +settings.confirm_wiki_delete = Изтриване на данните на уикито +settings.delete = Изтриване на това хранилище +settings.deletion_success = Хранилището е изтрито. +settings.update_settings_success = Настройките на хранилището са обновени. +settings.confirm_delete = Изтриване на хранилището +settings.add_collaborator_success = Сътрудникът е добавен. +settings.event_wiki = Уики +settings.delete_desc = Изтриването на хранилище е перманентно и не може да бъде отменено. +settings.advanced_settings = Разширени настройки +settings.delete_notices_1 = - Тази операция НЕ МОЖЕ да бъде отменена. +settings.enter_repo_name = Въведете имената на притежателя и хранилището точно както е показано: +settings.use_external_wiki = Използване на външно уики +settings.wiki_delete_desc = Изтриването на данните на уикито на хранилището е перманентно и не може да бъде отменено. +settings.wiki_delete = Изтриване на данните на уикито +settings.collaboration.admin = Администратор +settings.collaboration = Сътрудници +settings.collaboration.write = Писане +settings.collaboration.read = Четене +settings.collaboration.owner = Притежател +settings.basic_settings = Основни настройки +settings.wiki_desc = Включване на уики на хранилището +settings.use_internal_wiki = Използване на вграденото уики +settings.wiki_globally_editable = Позволяване на всеки да редактира уикито +settings.add_collaborator = Добавяне на сътрудник +repo_size = Размер на хранилището +settings.danger_zone = Опасна зона +issues.closed_by = от %[3]s бе затворена %[1]s +issues.delete_comment_confirm = Сигурни ли сте, че искате да изтриете този коментар? +issues.author_helper = Този потребител е авторът. +issues.ref_closed_from = `затвори тази задача %[4]s %[2]s` +milestones.due_date = Краен срок (опционално) +settings.wiki_delete_notices_1 = - Това ще изтрие перманентно и ще деактивира уикито на хранилището %s. +settings.wiki_deletion_success = Данните на уикито на хранилището са изтрити. +settings.collaborator_deletion = Премахване на сътрудника +settings.remove_collaborator_success = Сътрудникът е премахнат. +settings.archive.header = Архивиране на това хранилище +issues.filter_poster = Автор +issues.commented_at = `коментира %s` +settings.transfer_desc = Прехвърлете това хранилище на потребител или на организация, за които имате администраторски права. +settings.archive.button = Архивиране на хранилището +issues.role.owner_helper = Този потребител е притежателят на това хранилище. +settings.delete_notices_2 = - Тази операция ще изтрие перманентно хранилището %s, включително кода, задачите, коментарите, данните на уикито и настройките за сътрудници. +settings.admin_settings = Администраторски настройки +issues.role.owner = Притежател +settings.transfer = Прехвърляне на притежанието +issues.author = Автор +issues.closed_at = `затвори тази задача %[2]s` +settings.collaborator_deletion_desc = Премахването на сътрудник ще отнеме достъпа му до това хранилище. Продължаване? +commits.message = Съобщение +issues.due_date_not_set = Няма зададен краен срок. +issues.subscribe = Записване +issues.no_content = Няма предоставено описание. +issues.unsubscribe = Отписване +issues.new.no_milestone = Няма етап +issues.action_close = Затваряне +issues.action_label = Етикет +issues.action_milestone = Етап +issues.opened_by_fake = отворена %[1]s от %[2]s +issues.closed_by_fake = от %[2]s бе затворена %[1]s +issues.due_date = Краен срок +milestones.no_due_date = Няма краен срок +commits.author = Автор +commits.date = Дата +issues.filter_project_all = Всички проекти +issues.filter_label_select_no_label = Без етикет +issues.filter_label = Етикет +issues.filter_type.all_issues = Всички задачи +issues.filter_poster_no_select = Всички автори +issues.opened_by = отворена %[1]s от %[3]s +issues.action_open = Отваряне +pulls.closed_at = `затвори тази заявка за сливане %[2]s` +pulls.reopened_at = `отвори наново тази заявка за сливане %[2]s` +issues.reopened_at = `отвори наново тази задача %[2]s` +projects.column.edit = Редактиране на колоната +issues.close = Затваряне на задачата +issues.ref_reopened_from = `отвори наново тази задача %[4]s %[2]s` +projects.deletion = Изтриване на проекта +projects.edit_success = Проектът "%s" е обновен. +projects.deletion_success = Проектът е изтрит. +issues.create_comment = Коментиране +unescape_control_characters = Отекраниране +editor.file_delete_success = Файлът "%s" е изтрит. +projects.type.uncategorized = Некатегоризирано +projects.column.set_default = Задаване по подразбиране +projects.column.assigned_to = Възложено на +issues.reopen_comment_issue = Коментиране и Отваряне +issues.reopen_issue = Отваряне наново +issues.close_comment_issue = Коментиране и Затваряне +milestones.filter_sort.latest_due_date = Най-късен краен срок +diff.view_file = Преглед на файла +release.deletion_success = Изданието е изтрито. +projects.column.delete = Изтриване на колоната +migrate.migrating = Мигриране от %s ... +escape_control_characters = Екраниране +issues.label_deletion_success = Етикетът е изтрит. +pulls.is_closed = Заявката за сливане е затворена. +milestones.filter_sort.earliest_due_data = Най-ранен краен срок +issues.filter_type = Тип +issues.filter_milestones = Филтриране на етап +issues.filter_assignees = Филтриране на изпълнител +issues.filter_projects = Филтриране на проект +issues.filter_labels = Филтриране на етикет +issues.new.clear_milestone = Изчистване на етапа +issues.new.open_milestone = Отворени етапи +issues.new.closed_milestone = Затворени етапи +issues.new.assignees = Възложена на +issues.new.no_items = Няма елементи +issues.new.no_assignees = Няма изпълнители +issues.new.clear_assignees = Изчистване на изпълнителите +issues.filter_project_none = Без проект +issues.filter_milestone = Етап +issues.filter_milestone_all = Всички етапи +issues.filter_milestone_open = Отворени етапи +issues.filter_milestone_none = Без етапи +issues.filter_project = Проект +issues.num_participants = %d Участващи +issues.filter_assignee = Изпълнител +issues.filter_milestone_closed = Затворени етапи +issues.filter_assginee_no_select = Всички изпълнители +issues.filter_assginee_no_assignee = Без изпълнител +activity.opened_prs_count_1 = Предложена заявка за сливане +activity.opened_prs_count_n = Предложени заявки за сливане +activity.title.prs_merged_by = %s слята от %s +activity.merged_prs_label = Слята +activity.opened_prs_label = Предложена +activity.title.issues_closed_from = %s затворена от %s +activity.closed_issue_label = Затворена +activity.new_issue_label = Отворена +activity.title.releases_1 = %d Издание +activity.title.releases_n = %d Издания +milestones.completeness = %d%% Завършен +activity.title.prs_opened_by = %s предложена от %s +issues.action_milestone_no_select = Без етап +issues.action_assignee_no_select = Без изпълнител +milestones.edit = Редактиране на етапа +milestones.create_success = Етапът "%s" е създаден. +milestones.create = Създаване на етап +milestones.clear = Изчистване +milestones.deletion = Изтриване на етапа +milestones.edit_success = Етапът "%s" е обновен. +milestones.modify = Обновяване на етапа +milestones.deletion_success = Етапът е изтрит. +milestones.filter_sort.most_complete = Най-много завършен +milestones.filter_sort.least_complete = Най-малко завършен +activity.git_stats_file_n = %d файла +activity.git_stats_file_1 = %d файл +issues.action_assignee = Изпълнител +milestones.closed = Затворен %s +milestones.open = Отваряне +milestones.close = Затваряне +issues.label_templates.use = Използване на набор от етикети +issues.add_milestone_at = `добави това към етапа %s %s` +issues.add_label = добави етикета %s %s +issues.add_labels = добави етикетите %s %s +issues.remove_label = премахна етикета %s %s +issues.remove_labels = премахна етикетите %s %s +issues.add_remove_labels = добави етикетите %s и премахна %s %s +issues.add_project_at = `добави това към проекта %s %s` +issues.remove_project_at = `премахна това от проекта %s %s` +issues.remove_milestone_at = `премахна това от етапа %s %s` +issues.change_title_at = `промени заглавието от %s на %s %s` +template.avatar = Профилна снимка +desc.sha256 = SHA256 +editor.filename_cannot_be_empty = Името на файла не може да бъде празно. +release.title_empty = Заглавието не може да бъде празно. +settings.webhook.payload = Съдържание +settings.deploy_key_content = Съдържание +clone_in_vsc = Клониране във VS Code +use_template = Използване на този шаблон +download_file = Изтегляне на файла +editor.add_file = Добавяне на файл +editor.edit_file = Редактиране на файла +editor.this_file_locked = Файлът е заключен +editor.delete_this_file = Изтриване на файла +clone_in_vscodium = Клониране във VSCodium +download_zip = Изтегляне на ZIP +download_tar = Изтегляне на TAR.GZ +desc.public = Публично +desc.archived = Архивирано +desc.internal = Вътрешно +migrate_items_merge_requests = Заявки за Merge +migrate_items_issues = Задачи +fork_guest_user = Влезте, за да разклоните това хранилище. +actions = Действия +more_operations = Още операции +download_archive = Изтегляне на хранилището +branch = Клон +tree = Дърво +branches = Клонове +tags = Тагове +tag = Таг +filter_branch_and_tag = Филтриране на клон или таг +symbolic_link = Символна връзка +executable_file = Изпълним файл +blame = Авторство +editor.patch = Прилагане на кръпка +editor.new_patch = Нова кръпка +signing.wont_sign.not_signed_in = Не сте влезли. +settings.tags = Тагове +release.tags = Тагове +star_guest_user = Влезте, за отбелязване на това хранилище със звезда. +download_bundle = Изтегляне на BUNDLE +desc.private = Частно +settings.branches = Клонове +editor.name_your_file = Име на файла… +issues.label.filter_sort.by_size = Най-малък размер +issues.delete.title = Изтриване на тази задача? +pulls.new = Нова заявка за сливане +pulls.no_results = Няма намерени резултати. +release.title = Заглавие на изданието +issues.unpin_issue = Откачване на задачата +issues.pin_comment = закачи това %s +issues.lock = Заключване на обсъждането +issues.lock_confirm = Заключване +issues.unlock_confirm = Отключване +issues.due_date_form_edit = Редактиране +issues.due_date_form_remove = Премахване +issues.due_date_modified = промени крайния срок от %[2]s на %[1]s %[3]s +pulls.compare_changes = Нова заявка за сливане +activity.title.releases_published_by = %s публикувано от %s +topic.manage_topics = Управление на темите +topic.done = Готово +find_file.go_to_file = Отиване към файл +reactions_more = и още %d +issues.unpin_comment = откачи това %s +lines = реда +line = ред +editor.edit_this_file = Редактиране на файла +editor.preview_changes = Преглеждане на промените +default_branch = Стандартен клон +default_branch_label = стандартен +template.topics = Теми +editor.branch_does_not_exist = Клонът "%s" не съществува в това хранилище. +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.remove_assignee_at = `е премахнат като изпълнител от %s %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 +issues.label_deletion = Изтриване на етикета +issues.label_modify = Редактиране на етикета +issues.due_date_added = добави крайния срок %s %s +issues.due_date_remove = премахна крайния срок %s %s +release.new_release = Ново издание +release.tag_helper_existing = Съществуващ таг. +release.tag_name = Име на тага +issues.no_ref = Няма указан Клон/Таг +issues.lock.reason = Причина за заключването +pulls.create = Създаване на заявка за сливане +issues.label.filter_sort.reverse_by_size = Най-голям размер +issues.unlock = Отключване на обсъждането +issues.due_date_form_add = Добавяне на краен срок +release.save_draft = Запазване като чернова +release.add_tag = Създаване само на таг +release.publish = Публикуване на издание [modal] confirm = Потвърждаване @@ -250,6 +779,7 @@ buttons.mention.tooltip = Споменаване на потребител ил buttons.italic.tooltip = Добавяне на курсив текст buttons.link.tooltip = Добавяне на връзка buttons.disable_monospace_font = Изключване на равноширокия шрифт +buttons.ref.tooltip = Препратка към задача или заявка за сливане [org] teams.write_access = Писане @@ -271,7 +801,7 @@ code = Код members.remove = Премахване teams.all_repositories = Всички хранилища teams.update_settings = Обновяване на настройките -settings.full_name = Пълно Име +settings.full_name = Пълно име members.leave = Напускане members.leave.detail = Напускане на %s? teams.read_access = Четене @@ -281,6 +811,33 @@ settings.visibility.public = Публична settings.visibility.limited_shortname = Ограничена settings.visibility.private_shortname = Частна settings.permission = Разрешения +settings.visibility.limited = Ограничена (Видима само за удостоверени потребители) +settings.visibility.private = Частна (Видима само за участниците в организацията) +org_name_helper = Имената на организациите е добре да са кратки и запомнящи се. +org_full_name_holder = Пълно име на организацията +teams = Екипи +lower_members = участници +lower_repositories = хранилища +settings.repoadminchangeteam = Админ. на хранилището да може да добавя и премахва достъп за екипи +settings.email = Ел. поща за връзка +settings.delete_account = Изтриване на тази организация +settings.delete_org_title = Изтриване на организацията +settings.confirm_delete_account = Потвърждаване на изтриването +create_new_team = Нов екип +create_team = Създаване на екип +team_name = Име на екипа +team_name_helper = Имената на екипите е добре да са кратки и запомнящи се. +members = Участници +team_desc_helper = Опишете предназначението или ролята на екипа. +settings.delete = Изтриване на организацията +follow_blocked_user = Не можете да следвате тази организация, защото тя ви е блокирала. +settings.delete_prompt = Организацията ще бъде премахната завинаги. Това НЕ МОЖЕ да бъде отменено! +settings.labels_desc = Добавете етикети, които могат да се използват за задачи за всички хранилища в тази организация. +teams.none_access = Без достъп +teams.members.none = Няма участници в този екип. +repo_updated = Обновено +teams.delete_team_success = Екипът е изтрит. +teams.search_repo_placeholder = Потърсете хранилище… [install] admin_password = Парола @@ -293,9 +850,31 @@ ssl_mode = SSL install = Инсталация install_btn_confirm = Инсталиране на Forgejo app_name = Заглавие на сайта -admin_name = Потребителско име за администратор -confirm_password = Потвърждение на паролата -title = Първоначална Конфигурация +admin_name = Потреб. име за администратор +confirm_password = Потвърдете паролата +title = Първоначална конфигурация +domain = Домейн на сървъра +require_db_desc = Forgejo изисква MySQL, PostgreSQL, MSSQL, SQLite3 или TiDB (MySQL протокол). +general_title = Общи настройки +email_title = Настройки на ел. поща +db_schema = Схема +db_title = Настройки на базата данни +db_type = Тип база данни +db_name = Име на база данни +optional_title = Опционални настройки +mailer_user = SMTP Потребителско име +mailer_password = SMTP Парола +disable_gravatar = Изключване на Gravatar +smtp_addr = SMTP Хост +smtp_port = SMTP Порт +app_name_helper = Можете да въведете името на компанията си тук. +admin_title = Настройки на администраторския акаунт +err_empty_admin_password = Администраторската парола не може да бъде празна. +docker_helper = Ако стартирате Forgejo в Docker, моля, прочетете документацията преди да промените настройки. +sqlite_helper = Път на файла за SQLite3 базата данни.
      Въведете абсолютен път, ако стартирате Forgejo като service. +err_empty_admin_email = Администраторският адрес на ел. поща не може да бъде празен. +password_algorithm = Алгоритъм за хеш. на паролите +default_keep_email_private = Скриване на адресите на ел. поща по подразбиране [filter] string.asc = А - Я @@ -314,33 +893,39 @@ release.note = Бележка: hi_user_x = Здравейте %s, admin.new_user.user_info = Информация за потребителя register_notify = Добре дошли във Forgejo +issue.action.new = @%[1]s създаде #%[2]d. +issue.action.review = @%[1]s коментира в тази заявка за сливане. +issue.action.reopen = @%[1]s отвори наново #%[2]d. [user] joined_on = Присъединен на %s user_bio = Биография repositories = Хранилища -activity = Публична Дейност +activity = Публична дейност projects = Проекти code = Код overview = Обзор -watched = Наблюдавани Хранилища +watched = Наблюдавани хранилища unfollow = Прекратяване на следването block = Блокиране -settings = Потребителски Настройки -starred = Отбелязани Хранилища +settings = Потребителски настройки +starred = Отбелязани хранилища following = Следвани unblock = Прекратяване на блокирането follow = Последване followers = Последователи block_user = Блокиране на потребителя change_avatar = Променете профилната си снимка… +email_visibility.limited = Вашият адрес на ел. поща е видим за всички удостоверени потребители +disabled_public_activity = Този потребител е изключил публичната видимост на дейността. +email_visibility.private = Вашият адрес на ел. поща е видим само за вас и администраторите [home] -filter = Други Филтри +filter = Други филтри show_archived = Архивирани search_repos = Намиране на хранилище… -my_orgs = Моите Организации -uname_holder = Потребителско име или Адрес на ел. поща +my_orgs = Моите организации +uname_holder = Потреб. име или Адрес на ел. поща my_repos = Хранилища show_both_archived_unarchived = Показване на и архивирани и неархивирани feed_of = Емисия на "%s" @@ -351,14 +936,17 @@ show_private = Частни password_holder = Парола show_more_repos = Показване на повече хранилища… show_only_unarchived = Показване само на неархивирани -my_mirrors = Моите Огледала +my_mirrors = Моите огледала show_only_archived = Показване само на архивирани view_home = Преглед на %s +collaborative_repos = Съвместни хранилища +switch_dashboard_context = Превключване на контекста на таблото +show_only_public = Показване само на публични [admin] packages.version = Версия packages.name = Име -users.full_name = Пълно Име +users.full_name = Пълно име dashboard = Табло repositories = Хранилища users.name = Потребителско име @@ -375,6 +963,20 @@ first_page = Първа config.app_name = Заглавие на сайта packages.repository = Хранилище notices.type_1 = Хранилище +config.domain = Домейн на сървъра +users.max_repo_creation = Максимален брой хранилища +defaulthooks = Уеб-куки по подразбиране +auths.sspi_default_language = Потребителски език по подразбиране +hooks = Уеб-куки +systemhooks = Системни уеб-куки +orgs.new_orga = Нова организация +config.https_only = Само HTTPS +users.update_profile_success = Потребителският акаунт бе обновен. +users.new_success = Потребителският акаунт "%s" бе създаден. +users.deletion_success = Потребителският акаунт бе изтрит. +last_page = Последна +config.test_email_placeholder = Ел. поща (напр. test@example.com) +users.cannot_delete_self = Не можете да изтриете себе си [error] not_found = Целта не може да бъде намерена. @@ -384,18 +986,47 @@ occurred = Възникна грешка [form] UserName = Потребителско име -Email = Адрес на е-поща +Email = Адрес на ел. поща Password = Парола RepoName = Име на хранилището +username_been_taken = Потребителското име вече е заето. +SSPIDefaultLanguage = Език по подразбиране +password_not_match = Паролите не съвпадат. +captcha_incorrect = CAPTCHA кодът е неправилен. +username_change_not_local_user = Потребители, които не са локални не могат да променят потребителското си име. +username_password_incorrect = Неправилно потребителско име или парола. +user_not_exist = Потребителят не съществува. +lang_select_error = Изберете език от списъка. +HttpsUrl = HTTPS URL +require_error = ` не може да бъде празно.` +Retype = Потвърдете паролата +url_error = `"%s" не е валиден URL.` +Content = Съдържание +team_not_exist = Екипът не съществува. +TeamName = Име на екипа +email_error = ` не е валиден адрес на ел. поща.` +email_invalid = Адресът на ел. поща е невалиден. [action] close_issue = `затвори задача %[3]s#%[2]s` rename_repo = преименува хранилище от %[1]s на %[3]s review_dismissed_reason = Причина: -comment_issue = `коментира задача %[3]s#%[2]s` +comment_issue = `коментира в задача %[3]s#%[2]s` starred_repo = отбеляза със звезда %[2]s create_repo = създаде хранилище %s create_issue = `отвори задача %[3]s#%[2]s` +reopen_pull_request = `отвори наново заявка за сливане %[3]s#%[2]s` +create_pull_request = `създаде заявка за сливане %[3]s#%[2]s` +reopen_issue = `отвори наново задача %[3]s#%[2]s` +commit_repo = изтласка към %[3]s на %[4]s +close_pull_request = `затвори заявка за сливане %[3]s#%[2]s` +comment_pull = `коментира в заявка за сливане %[3]s#%[2]s` +merge_pull_request = `сля заявка за сливане %[3]s#%[2]s` +auto_merge_pull_request = `сля автоматично заявка за сливане %[3]s#%[2]s` +watched_repo = започна да наблюдава %[2]s +delete_tag = изтри таг %[2]s от %[3]s +delete_branch = изтри клон %[2]s от %[3]s +create_branch = създаде клон %[3]s на %[4]s [auth] login_openid = OpenID @@ -404,7 +1035,7 @@ sign_up_successful = Акаунтът е създаден успешно. Доб login_userpass = Влизане forgot_password = Забравена парола? sign_up_now = Нуждаете се от акаунт? Регистрирайте се сега. -forgot_password_title = Забравена Парола +forgot_password_title = Забравена парола openid_register_title = Създаване на нов акаунт account_activated = Акаунтът е активиран social_register_helper_msg = Вече имате акаунт? Свържете го сега! @@ -415,19 +1046,27 @@ register_helper_msg = Вече имате акаунт? Влезте сега! reset_password = Възстановяване на акаунта disable_register_prompt = Регистрирането е изключено. Моля, свържете се с вашия администратор на сайта. remember_me = Запомни ме +openid_signin_desc = Въведете своя OpenID URI. Например: alice.openid.example.org или https://openid.example.org/alice. +disable_register_mail = Потвърждението по ел. поща за регистрация е изключено. +manual_activation_only = Свържете се с вашия администратор на сайта, за да завършите активирането. +must_change_password = Обновете паролата си +password_too_short = Дължината на паролата не може да бъде по-малка от %d знака. [aria] -footer.software = Относно Софтуера +footer.software = Относно софтуера footer.links = Връзки footer = Долен колонтитул [startpage] -install = Лесно за инсталиране +install = Лесен за инсталиране lightweight = Лек -license = Отворен Код +license = Отворен код install_desc = Просто стартирайте двоичния файл за вашата платформа, използвайте Docker, или го получете пакетирано. app_desc = Безпроблемна Git услуга със самостоятелен хостинг platform = Междуплатформен +lightweight_desc = Forgejo има ниски минимални изисквания и може да работи на икономичен Raspberry Pi. Спестете енергията на вашата машина! +platform_desc = Forgejo работи навсякъде, където Go може да се компилира: Windows, macOS, Linux, ARM, и т.н. Изберете, което харесвате! +license_desc = Вземете Forgejo! Присъединете се към нас, допринасяйки, за да направите този проект още по-добър. Не се колебайте да сътрудничите! [notification] subscriptions = Абонаменти @@ -451,10 +1090,15 @@ search = Търсене code = Код organizations = Организации code_last_indexed_at = Последно индексиран %s +repo_no_results = Няма намерени съответстващи хранилища. +user_no_results = Няма намерени съответстващи потребители. +org_no_results = Няма намерени съответстващи организации. [actions] runners.version = Версия variables = Променливи +runners.labels = Етикети +actions = Действия [heatmap] less = По-малко @@ -469,3 +1113,7 @@ normal_file = Обикновен файл executable_file = Изпълним файл changed_filemode = %[1]s → %[2]s submodule = Подмодул + + +[dropzone] +default_message = Пуснете файлове тук или щракнете, за качване. \ No newline at end of file diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index 954295b5cd..fdb91f987f 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -3525,6 +3525,9 @@ variables.creation.failed=Přidání proměnné se nezdařilo. variables.creation.success=Proměnná „%s“ byla přidána. variables.update.failed=Úprava proměnné se nezdařila. variables.update.success=Proměnná byla upravena. +runs.no_workflows.quick_start = Nevíte jak začít s Gitea Action? Podívejte se na průvodce rychlým startem. +variables.id_not_exist = Proměnná s id %d neexistuje. +runs.no_workflows.documentation = Další informace o Gitea Action, viz dokumentace. [projects] type-1.display_name=Samostatný projekt diff --git a/options/locale/locale_de-DE.ini b/options/locale/locale_de-DE.ini index fb2bb337a1..5b288c0090 100644 --- a/options/locale/locale_de-DE.ini +++ b/options/locale/locale_de-DE.ini @@ -3660,6 +3660,7 @@ runs.no_matching_online_runner_helper = Es gibt keinen passenden Online-Runner m runs.no_workflows = Es gibt noch keine Workflows. runs.no_workflows.documentation = Für weitere Informationen über Forgejo Actions, siehe die Dokumentation. runs.empty_commit_message = (leere Commit-Nachricht) +variables.id_not_exist = Variable mit ID %d existiert nicht. [projects] type-1.display_name=Individuelles Projekt diff --git a/options/locale/locale_el-GR.ini b/options/locale/locale_el-GR.ini index 578846eb4d..ab5bcefc7c 100644 --- a/options/locale/locale_el-GR.ini +++ b/options/locale/locale_el-GR.ini @@ -3567,6 +3567,9 @@ variables.creation.failed=Αποτυχία προσθήκης μεταβλητή variables.creation.success=Η μεταβλητή "%s" έχει προστεθεί. variables.update.failed=Αποτυχία επεξεργασίας μεταβλητής. variables.update.success=Η μεταβλητή έχει τροποποιηθεί. +variables.id_not_exist = Η μεταβλητή με id %d δεν υπάρχει. +runs.no_workflows.documentation = Για περισσότερες πληροφορίες σχετικά με τη Δράση Gitea, ανατρέξτε στην τεκμηρίωση. +runs.no_workflows.quick_start = Δεν ξέρετε πώς να ξεκινήσετε με τις Δράσεις Gitea; Συμβουλευτείτε τον οδηγό για γρήγορη αρχή. [projects] type-1.display_name=Ατομικό Έργο diff --git a/options/locale/locale_es-ES.ini b/options/locale/locale_es-ES.ini index 7bd2cdad4b..57ab69b255 100644 --- a/options/locale/locale_es-ES.ini +++ b/options/locale/locale_es-ES.ini @@ -3577,6 +3577,7 @@ variables.creation.failed=No se pudo agregar la variable. variables.creation.success=La variable "%s" ha sido añadida. variables.update.failed=Error al editar la variable. variables.update.success=La variable ha sido editada. +variables.id_not_exist = Variable con id %d no existe. [projects] type-1.display_name=Proyecto individual diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index e547810320..98c524ac80 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -141,6 +141,7 @@ confirm_delete_selected=Êtes-vous sûr de vouloir supprimer tous les éléments name=Nom value=Valeur +confirm_delete_artifact = Êtes-vous certain de vouloir supprimer l'artefect "%s" ? [aria] navbar=Barre de navigation @@ -306,6 +307,7 @@ env_config_keys=Configuration de l'environnement env_config_keys_prompt=Les variables d'environnement suivantes seront également ajoutées à votre fichier de configuration : enable_update_checker_helper_forgejo = Vérifie la disponibilité de nouvelles versions de Forgejo en interrogeant l'enregistrement DNS TXT de release.forgejo.org. allow_dots_in_usernames = Les points sont autorisés dans les noms d'utilisateurs. Sans effet sur les comptes existants. +smtp_from_invalid = L'adresse "Envoyer un courriel en tant que" est invalide [home] uname_holder=Nom d’utilisateur ou adresse courriel @@ -587,14 +589,14 @@ invalid_ssh_key=Impossible de vérifier votre clé SSH : %s invalid_gpg_key=Impossible de vérifier votre clé GPG : %s invalid_ssh_principal=Principal invalide : %s must_use_public_key=La clé que vous avez fournie est une clé privée. Veuillez ne pas divulguer votre clé privée. Utilisez votre clé publique à la place. -unable_verify_ssh_key="Impossible de valider la clé SSH, vérifiez à nouveau s’il y a des erreurs." +unable_verify_ssh_key=Impossible de valider la clé SSH, vérifiez à nouveau s’il y a des erreurs. auth_failed=Échec d'authentification : %v -still_own_repo="Votre compte possède toujours un ou plusieurs dépôts, vous devez d’abord les supprimer ou les transférer." -still_has_org="Votre compte est un membre d’une ou plusieurs organisations, veuillez d’abord les quitter." -still_own_packages="Votre compte possède toujours un ou plusieurs paquets, vous devez d’abord les supprimer ou les transférer." -org_still_own_repo="Cette organisation possède encore un ou plusieurs dépôts. Vous devez d’abord les supprimer ou les transférer." -org_still_own_packages="Cette organisation possède encore un ou plusieurs paquets. Vous devez d’abord les supprimer." +still_own_repo=Votre compte possède toujours un ou plusieurs dépôts, vous devez d’abord les supprimer ou les transférer. +still_has_org=Votre compte est un membre d’une ou plusieurs organisations, veuillez d’abord les quitter. +still_own_packages=Votre compte possède toujours un ou plusieurs paquets, vous devez d’abord les supprimer ou les transférer. +org_still_own_repo=Cette organisation possède encore un ou plusieurs dépôts. Vous devez d’abord les supprimer ou les transférer. +org_still_own_packages=Cette organisation possède encore un ou plusieurs paquets. Vous devez d’abord les supprimer. target_branch_not_exist=La branche cible n'existe pas. username_error_no_dots = ` peut uniquement contenir des caractères alphanumériques ('0-9','a-z','A-Z'), tiret ('-') et souligné ('_'). Ne peut commencer ou terminer avec un caractère non-alphanumérique, et l'utilisation de caractères non-alphanumériques consécutifs n'est pas permise.` @@ -756,7 +758,7 @@ manage_gpg_keys=Gérer les clés GPG add_key=Ajouter une clé ssh_desc=Ces clefs SSH publiques sont associées à votre compte. Les clefs privées correspondantes permettent l'accès complet à vos repos. Les clés SSH qui ont été vérifiées peuvent aussi être utilisées pour vérifier des commits Git signés par SSH. principal_desc=Ces Principaux de certificats SSH sont associés à votre compte et permettent un accès complet à vos dépôts. -gpg_desc=Ces clés GPG sont associées à votre compte. Conservez-les en lieu sûr, car elles permettent de vérifier vos révisions. +gpg_desc=Ces clés GPG sont associées à votre compte et utilisées pour vérifier vos révisions. Conservez les clés privées en lieu sûr, car elles permettent de signer vos révisions. ssh_helper=Besoin d'aide ? Consultez le guide de GitHub pour créer vos propres clés SSH ou résoudre les problèmes courants que vous pourriez rencontrer en utilisant SSH. gpg_helper=Besoin d'aide ? Consultez le guide de GitHub sur GPG. add_new_key=Ajouter une clé SSH @@ -879,7 +881,7 @@ oauth2_application_remove_description=La suppression d'une application OAuth2 l' oauth2_application_locked=Forgejo préinstalle des applications OAuth2 au démarrage si elles sont activées dans la configuration. Pour éviter des comportements inattendus, celles-ci ne peuvent être éditées ni supprimées. Veuillez vous référer à la documentation OAuth2 pour plus d'informations. authorized_oauth2_applications=Applications OAuth2 autorisées -authorized_oauth2_applications_description=Vous avez autorisé l'accès à votre compte personnel Forgejo à ces applications tierces. Veuillez révoquer l'accès aux applications dont vous n'avez plus besoin. +authorized_oauth2_applications_description=Vous avez autorisé l'accès à votre compte personnel Forgejo à ces applications tierces. Veuillez révoquer l'accès aux applications qui ne sont plus utilisées. revoke_key=Révoquer revoke_oauth2_grant=Révoquer l'accès revoke_oauth2_grant_description=La révocation de l'accès à cette application tierce l'empêchera d'accéder à vos données. Vous êtes sûr ? @@ -946,10 +948,11 @@ visibility.limited_tooltip=Visible uniquement pour les utilisateurs authentifié visibility.private=Privé visibility.private_tooltip=Visible uniquement aux membres des organisations que vous avez rejointes blocked_users = Utilisateurs bloqués -blocked_users_none = Vous n'avez bloqué aucun utilisateur. +blocked_users_none = Il n'y a aucun utilisateur bloqué. blocked_since = Bloqué depuis %s user_unblock_success = Cet utilisateur a été débloqué avec succès. user_block_success = Cet utilisateur a été bloqué avec succès. +change_password = Modifier le mot de passe [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. @@ -1347,7 +1350,7 @@ projects.edit_success=Le projet "%s" a été mis à jour. projects.type.none=Aucun projects.type.basic_kanban=Kanban basique projects.type.bug_triage=Bug à trier -projects.template.desc=Modèle de projet +projects.template.desc=Modèle projects.template.desc_helper=Sélectionnez un modèle de projet pour débuter projects.type.uncategorized=Non catégorisé projects.column.edit=Modifier la colonne @@ -1570,8 +1573,8 @@ issues.subscribe=S’abonner issues.unsubscribe=Se désabonner issues.unpin_issue=Désépingler le ticket issues.max_pinned=Vous ne pouvez pas épingler plus de tickets -issues.pin_comment=a épinglé ça %s. -issues.unpin_comment=a désépinglé ça %s. +issues.pin_comment=a épinglé ça %s +issues.unpin_comment=a désépinglé ça %s issues.lock=Verrouiller la conversation issues.unlock=Déverrouiller la conversation issues.lock.unknown_reason=Impossible de verrouiller un ticket avec une raison inconnue. @@ -1651,7 +1654,7 @@ issues.dependency.issue_closing_blockedby=La fermeture de ce ticket est bloquée issues.dependency.issue_close_blocks=Cette demande d'ajout empêche la clôture des tickets suivants issues.dependency.pr_close_blocks=Cette demande d'ajout empêche la clôture des tickets suivants issues.dependency.issue_close_blocked=Vous devez fermer tous les tickets qui bloquent ce ticket avant de pouvoir le fermer. -issues.dependency.issue_batch_close_blocked=Impossible de fermer tous les tickets que vous avez choisis, car le ticket #%d a toujours des dépendances ouvertes. +issues.dependency.issue_batch_close_blocked=Impossible de fermer tous les tickets que vous avez choisis, car le ticket #%d a toujours des dépendances ouvertes issues.dependency.pr_close_blocked=Vous devez fermer tous les tickets qui bloquent cette demande d'ajout avant de pouvoir la fusionner. issues.dependency.blocks_short=Bloque issues.dependency.blocked_by_short=Dépend de @@ -1765,7 +1768,7 @@ pulls.required_status_check_missing=Certains contrôles requis sont manquants. pulls.required_status_check_administrator=En tant qu'administrateur, vous pouvez toujours fusionner cette requête de pull. pulls.blocked_by_approvals=Cette demande d'ajout n’est pas suffisamment approuvée. %d approbations obtenues sur %d. pulls.blocked_by_rejection=Cette demande d’ajout nécessite des corrections sollicitées par un évaluateur officiel. -pulls.blocked_by_official_review_requests="Cette demande d’ajout est bloquée car il manque une ou plusieurs approbations officielles." +pulls.blocked_by_official_review_requests=Cette demande d’ajout est bloquée car il manque une ou plusieurs approbations officielles. pulls.blocked_by_outdated_branch=Cette demande d’ajout est bloquée car elle est obsolète. pulls.blocked_by_changed_protected_files_1=Cette demande d'ajout est bloquée car elle modifie un fichier protégé : pulls.blocked_by_changed_protected_files_n=Cette demande d'ajout est bloquée car elle modifie des fichiers protégés : @@ -1776,8 +1779,8 @@ pulls.num_conflicting_files_1=%d fichier en conflit pulls.num_conflicting_files_n=%d fichiers en conflit pulls.approve_count_1=%d approuvé pulls.approve_count_n=%d approuvés -pulls.reject_count_1=%d changement requis -pulls.reject_count_n=%d changements requis +pulls.reject_count_1=%d demande de modifications +pulls.reject_count_n=%d demandes de modifications pulls.waiting_count_1=%d évaluation en attente pulls.waiting_count_n=%d évaluations en attente pulls.wrong_commit_id=l'ID de la révision doit être un ID de révision sur la branche cible @@ -2347,9 +2350,9 @@ settings.protect_branch_name_pattern=Motif de nom de branche protégé settings.protect_branch_name_pattern_desc=Motifs de nom de branche protégé. Consultez la documentation pour la syntaxe du motif. Exemples : main, release/** settings.protect_patterns=Motifs settings.protect_protected_file_patterns=Liste des fichiers et motifs protégés -settings.protect_protected_file_patterns_desc=Liste de fichiers et de motifs, séparés par un point-virgule « ; », qui ne pourront pas être modifiés même si les utilisateurs disposent des droits sur la branche. Voir la syntaxe glob. Exemples : .drone.yml ; /docs/**/*.txt. -settings.protect_unprotected_file_patterns=Liste des fichiers et motifs exclus -settings.protect_unprotected_file_patterns_desc=Liste de fichiers et de motifs globs, séparés par un point-virgule « ; », qui pourront être modifiés malgré la protection de branche, par les utilisateurs autorisés. Voir la syntaxe Glob. Exemples : .drone.yml ; /docs/**/*.txt. +settings.protect_protected_file_patterns_desc=Les fichiers protégés ne peuvent être modifiés, même si l'utilisateur a le droit d'ajouter, éditer ou supprimer des fichiers dans cette branche. Plusieurs motifs peuvent être séparés par un point-virgule (;). Voir la documentation de github.com/gobwas/glob pour la syntaxe des motifs. Exemples: .forgejo/workflows/test.yml, /docs/**/*.txt. ; », qui ne pourront pas être modifiés même si les utilisateurs disposent des droits sur la branche. Voir la syntaxe glob. Exemples : .drone.yml ; /docs/**/*.txt. +settings.protect_unprotected_file_patterns=Liste des fichiers et motifs exclus (séparés par un point virgule ';') : +settings.protect_unprotected_file_patterns_desc=Les fichiers non-protégés qui peuvent être modifiés si l'utilisateur a le droit d'écriture, prenant le pas sur les restrictions de push. Plusieurs motifs peuvent être séparés par un point-virgule (;). Voir la documentation de github.com/gobwas/glob pour la syntaxe des motifs. Exemples: .forgejo/workflows/test.yml, /docs/**/*.txt. ; », qui pourront être modifiés malgré la protection de branche, par les utilisateurs autorisés. Voir la syntaxe Glob. Exemples : .drone.yml ; /docs/**/*.txt. settings.add_protected_branch=Activer la protection settings.delete_protected_branch=Désactiver la protection settings.update_protect_branch_success=La règle de protection de branche "%s" a été mise à jour. @@ -2370,7 +2373,7 @@ settings.choose_branch=Choisissez une branche… settings.no_protected_branch=Il n'y a pas de branche protégée. settings.edit_protected_branch=Éditer settings.protected_branch_required_rule_name=Nom de la règle requise -settings.protected_branch_duplicate_rule_name=Nom de la règle en double +settings.protected_branch_duplicate_rule_name=Il existe déjà une règle pour cet ensemble de branches settings.protected_branch_required_approvals_min=Le nombre de revues nécessaires ne peut être négatif. settings.tags=Étiquettes settings.tags.protection=Protection d'étiquette @@ -2621,7 +2624,7 @@ settings.ignore_stale_approvals_desc = Ne pas prendre en compte les approbations settings.archive.mirrors_unavailable = Les mirroirs ne sont pas disponibles si le dépôt a été archivé. pulls.commit_ref_at = `a référencé cette pull request depuis le commit %[2]s` settings.new_owner_blocked_doer = Le nouveau propriétaire vous a bloqué. -settings.enter_repo_name = Confirmez en entrant le nom du dépôt: +settings.enter_repo_name = Confirmez en entrant le propriétaire et le nom du dépôt exactement comme affiché : settings.wiki_rename_branch_main = Normalise le nom de la branche du Wiki settings.wiki_rename_branch_main_desc = Renommer la branche utilisée en interne par le Wiki en "%s". Cette modification est permanente et ne peut être annulée. settings.wiki_rename_branch_main_notices_2 = Cela changera le nom de la branche interne du Wiki associé au dépôt %s de façon permanente. Les checkouts existant devront être mis à jour. @@ -2644,6 +2647,9 @@ contributors.contribution_type.commits = Commits contributors.contribution_type.additions = Ajouts contributors.contribution_type.filter_label = Type de contributeur : contributors.contribution_type.deletions = Suppressions +pulls.made_using_agit = AGit +activity.navbar.code_frequency = Fréquence de code +activity.navbar.recent_commits = Commits récents [graphs] @@ -3655,6 +3661,9 @@ variables.creation.failed=Impossible d'ajouter la variable. variables.creation.success=La variable « %s » a été ajoutée. variables.update.failed=Impossible d’éditer la variable. variables.update.success=La variable a bien été modifiée. +runs.no_workflows.quick_start = Vous ne savez pas comment commencer avec Forgejo Action ? Consultez le guide de démarrage rapide. +runs.no_workflows.documentation = Pour plus d’informations sur les Actions Forgejo, voir la documentation. +variables.id_not_exist = La variable numéro %d n’existe pas. [projects] type-1.display_name=Projet personnel diff --git a/options/locale/locale_gl.ini b/options/locale/locale_gl.ini new file mode 100644 index 0000000000..3ccdfc2bbc --- /dev/null +++ b/options/locale/locale_gl.ini @@ -0,0 +1,143 @@ + + + +[common] +home = Inicio +dashboard = Panel de Control +explore = Explorar +help = Axuda +logo = Logo +sign_in = Iniciar Sesión +sign_in_with_provider = Iniciar Sesión con %s +sign_in_or = ou +sign_out = Pechar Sesión +sign_up = Rexístrate +link_account = Ligazón a Conta +register = Rexistro +version = Vesión +powered_by = Desenvolvido por %s +page = Páxina +template = Modelo +notifications = Notificacións +active_stopwatch = Activar Rastrexador de Tempo +create_new = Crear… +user_profile_and_more = Perfil e Configuración… +signed_in_as = Inicia Sesión como +enable_javascript = Este sitio web require JavaScript. +toc = Táboa de Contidos +licenses = Licenzas +return_to_gitea = Regreso a Forgejo +username = Nome de Usuario +email = Enderezo Electrónico +password = Contrasinal +re_type = Confirme o Contrasinal +captcha = CAPTCHA +twofa = Autenticación de Dous Factores +passcode = Código +webauthn_insert_key = Insira a súa Chave de Seguranza +webauthn_press_button = Preme o botón da súa Chave de Seguranza… +webauthn_use_twofa = Use o Código de Dous Factores do seu Teléfono +webauthn_error = Non se Puido Ler a súa Chave de Seguranza. +webauthn_unsupported_browser = O seu Navegador non Soporta Actualmente WebAuthn. +webauthn_error_unknown = Produciuse un Erro Descoñecido.Téntao de novo. +webauthn_error_unable_to_process = O Servidor non Puido Procesar a súa Solicitude. +webauthn_error_duplicated = A Chave de Seguranza non está Permitida para esta Solicitude. Asegúrese de que a Chave non Estea Rexistrada. +webauthn_error_empty = Debes Definir un Nome para esta Chave. +webauthn_reload = Recarga +repository = Repositorio +organization = Organización +mirror = Espello +new_repo = Novo Repositorio +new_migrate = Nova Migración +new_mirror = Novo Espello +new_fork = Nova Bifurcación do Repositorio +new_org = Nova Organización +new_project = Novo Proxecto +new_project_column = Nova Columna +manage_org = Xestionar Organizacións +admin_panel = Administración do Sitio +account_settings = Axustes da Conta +settings = Configuración +your_profile = Perfil +your_starred = Destacado +your_settings = Configuracións +all = Todo +sources = Fontes +mirrors = Espellos +collaborative = Colaborativo +forks = Derivacións +pull_requests = Pull Requests +milestones = Fitos +ok = OK +cancel = Cancelar +retry = Volve Tentar +rerun = Volve Executar +rerun_all = Volve Executar Todos os Traballos +add = Engadir +add_all = Engadir Todo +remove_all = Quitar Todo +remove_label_str = Eliminar Elemento "%s" +edit = Editar +enabled = Activado +locked = Bloqueado +copy = Copiar +copy_url = Copiar URL +copy_branch = Copiar o Nome da Rama +copy_success = Copiado! +copy_error = Erro na Copia +write = Escribir +preview = Vista Previa +loading = Cargando… +error = Erro +go_back = Volver Atrás +never = Nunca +unknown = Descoñecido +rss_feed = Feed RSS +unpin = Desprender +artifacts = Artefactos +confirm_delete_artifact = Estás seguro de que queres eliminar o Artefacto '%s' ? +archived = Arquivado +concept_system_global = Global +access_token = Token de Acceso +activities = Actividades +save = Gardar +copy_content = Copiar Contido +language = Linguaxe +copy_hash = Copiar hash +twofa_scratch = Código Scratch de Dous Facetores +webauthn_sign_in = Preme o botón da súa Chave de Seguranza. Se a súa Chave de Seguranza non ten ningún botón, insírela de novo. +issues = Problemas +disabled = Desactivado +error404 = A páxina á que estás tentando acceder Non Existe ou Non tes Autorización para vela. +tracked_time_summary = Resumo do tempo de seguimento baseado nos filtros da lista de problemas +webauthn_error_insecure = WebAuthn só Admite Conexións Seguras. Para probar a través de HTTP, pode usar a orixe "localhost" ou "127.0.0.1" +webauthn_error_timeout = Alcanzouse o tempo de espera antes de que se Pidese Ler a súa Chave. Volve cargar esta Páxina e Téntao de Novo. +remove = Quitar +view = Vista +copy_type_unsupported = Este tipo de ficheiro non se pode copiar +concept_user_organization = Organización +show_timestamps = Mostrar M0arcas de Tempo +show_log_seconds = Mostrar Segundos +download_logs = Descargar Rexistros +name = Nome +value = Valor +confirm_delete_selected = Confirmar a eliminación de todos os elementos seleccionados? +show_full_screen = Mostrar Pantalla Completa + +[aria] +navbar = Barra de Navegación +footer = Pé de Páxina +footer.software = Sobre o Software +footer.links = Ligazóns + +[heatmap] +no_contributions = Sen Achegas +less = Menos +more = Máis +number_of_contributions_in_the_last_12_months = %s de contribucións nos últimos 12 meses + +[editor] +buttons.heading.tooltip = Engadir Título +buttons.italic.tooltip = Engade texto en cursiva +buttons.quote.tooltip = Texto de cita +buttons.bold.tooltip = Engadir texto en negriña \ No newline at end of file diff --git a/options/locale/locale_ja-JP.ini b/options/locale/locale_ja-JP.ini index 8b8cb352ff..2808ac6054 100644 --- a/options/locale/locale_ja-JP.ini +++ b/options/locale/locale_ja-JP.ini @@ -3589,6 +3589,9 @@ variables.creation.failed=変数を追加できませんでした。 variables.creation.success=変数 "%s" を追加しました。 variables.update.failed=変数を更新できませんでした。 variables.update.success=変数を更新しました。 +runs.no_workflows.quick_start = Forgejo Action の始め方がわからない? クイックスタートガイドをご覧ください。 +runs.no_workflows.documentation = Forgejo Action の詳細については、ドキュメントを参照してください。 +variables.id_not_exist = idが%dの変数は存在しません。 [projects] type-1.display_name=個人プロジェクト diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index cf16f3dd17..919edc0150 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -141,7 +141,7 @@ copy_type_unsupported = Dit bestandstype kan niet worden gekopieerd pin = Vastpinnen unpin = Ontpinnen remove_label_str = Verwijder punt "%s" -confirm_delete_artifact = Weet je zeker dat je het artefact '%s' wilt verwijderen? +confirm_delete_artifact = Weet u zeker dat u het artefact "%s" wilt verwijderen? [aria] navbar = Navigatiebalk @@ -182,6 +182,7 @@ invalid_csrf=Verkeerd verzoek: ongeldig CSRF-token not_found=Het doel kon niet worden gevonden. network_error=Netwerk fout report_message = Als je denkt dat dit een bug is in Forgejo, zoek dan naar issues op Codeberg of open een nieuwe issue als dat nodig is. +server_internal = Interne serverfout [startpage] app_desc=Een eenvoudige, self-hosted Git service @@ -276,12 +277,12 @@ admin_password=Wachtwoord confirm_password=Verifieer wachtwoord admin_email=E-mailadres install_btn_confirm=Installeer Forgejo -test_git_failed=Git test niet gelukt: 'git' commando %v -sqlite3_not_available=Deze Forgejo-versie biedt geen ondersteuning voor SQLite3. Download de officiële build van %s (niet de versie van de 'gobuild'). +test_git_failed=Git test niet gelukt: "git" commando %v +sqlite3_not_available=Deze Forgejo-versie biedt geen ondersteuning voor SQLite3. Download de officiële build van %s (niet de versie van de "gobuild"). invalid_db_setting=De database instelling zijn niet correct: %v invalid_repo_path=Het pad van de hoofdmap van de repository is ongeldig: %v invalid_app_data_path=Ongeldig app-gegevenspad: %v -run_user_not_match=De 'uitvoeren als' gebruikersnaam is niet de huidige gebruikersnaam: %s -> %s +run_user_not_match=De "uitvoeren als" gebruikersnaam is niet de huidige gebruikersnaam: %s -> %s internal_token_failed=Interne token genereren mislukt: %v secret_key_failed=Geheime sleutel genereren mislukt: %v save_config_failed=Kan de configuratie niet opslaan: %v @@ -294,7 +295,7 @@ default_allow_create_organization_popup=Standaard toestaan dat nieuwe gebruikers default_enable_timetracking=Tijdregistratie standaard inschakelen default_enable_timetracking_popup=Tijdsregistratie voor nieuwe repositories standaard inschakelen. no_reply_address=Verborgen e-maildomein -no_reply_address_helper=Domeinnaam voor gebruikers met een verborgen e-mailadres. Bijvoorbeeld zal de gebruikersnaam 'joe' in Git worden geregistreerd als 'joe@noreply.example.org' als het verborgen email domein is ingesteld op 'noreply.example.org'. +no_reply_address_helper=Domeinnaam voor gebruikers met een verborgen e-mailadres. Bijvoorbeeld zal de gebruikersnaam "joe" in Git worden geregistreerd als "joe@noreply.example.org" als het verborgen email domein is ingesteld op "noreply.example.org". password_algorithm=Wachtwoord Hash Algoritme env_config_keys = Configuratie Omgeving env_config_keys_prompt = De volgende omgevingsvariabelen worden ook toegepast op je configuratiebestand: @@ -307,6 +308,7 @@ run_user_helper = De gebruikersnaam van het besturingssysteem waaronder Forgejo require_sign_in_view_popup = Beperk de toegang tot de pagina's tot ingelogde gebruikers. Bezoekers zien alleen de aanmeldings- en registratiepagina's. enable_update_checker_helper_forgejo = Controleert periodiek op nieuwe versies van Forgejo door een DNS TXT-record op release.forgejo.org te controleren. enable_update_checker_helper = Controleert periodiek op nieuwe versies door verbinding te maken met gitea.io. +smtp_from_invalid = Het adres "E-mails versturen als" is ongeldig [home] uname_holder=Gebruikersnaam of e-mailadres @@ -532,8 +534,8 @@ SSPISeparatorReplacement=Scheidingsteken SSPIDefaultLanguage=Standaardtaal require_error=` kan niet leeg zijn.` -alpha_dash_error=` moet bevatten alleen alfanumerieke, dash ('-') en onderstrepingstekens ('_').` -alpha_dash_dot_error=` moet bevatten alleen alfanumerieke, dash ('-'), onderstrepingsteken ('_') en de dot ('. ') karakters.` +alpha_dash_error=` moet bevatten alleen alfanumerieke, dash ("-") en onderstrepingstekens ("_").` +alpha_dash_dot_error=` moet bevatten alleen alfanumerieke, dash ("-"), onderstrepingsteken ("_") en de dot (".") karakters.` git_ref_name_error=` moet een correct geformatteerde git referentie naam zijn.` size_error=moet groter zijn dan %s min_size_error=` moet minimaal %s karakters bevatten.` @@ -573,7 +575,7 @@ enterred_invalid_owner_name=De nieuwe eigenaarnaam is niet geldig. enterred_invalid_password=Het ingevoerde wachtwoord is onjuist. user_not_exist=De gebruiker bestaat niet. team_not_exist=Dit team bestaat niet. -last_org_owner=Je kunt de laatste eigenaar van een organisatie niet verwijderen. Er moet er minimaal één eigenaar in een organisatie zitten. +last_org_owner=U kunt de laatste gebruiker niet verwijderen uit het team "eigenaars". Er moet minstens één eigenaar zijn voor een organisatie. cannot_add_org_to_team=Een organisatie kan niet worden toegevoegd als een teamlid. invalid_ssh_key=Kan de SSH-sleutel niet verifiëren: %s @@ -585,7 +587,7 @@ auth_failed=Verificatie mislukt: %v target_branch_not_exist=Doel branch bestaat niet. url_error = `"%s" is niet een geldige URL.` include_error = ` moet de volgende tekst bevatten "%s".` -username_error = ` kan alleen alfanumerieke karakters ('0-9','a-z','A-Z'), streepje ('-'), liggend streepje ('_') en punt ('.') bevatten. Niet-alfanumerieke karakters aan het begin of eind zijn verboden en aaneenvolgende niet alfanumerieke karakters zijn ook verboden.` +username_error = ` kan alleen alfanumerieke karakters ("0-9","a-z","A-Z"), streepje ("-"), liggend streepje ("_") en punt (".") bevatten. Niet-alfanumerieke karakters aan het begin of eind zijn verboden en aaneenvolgende niet alfanumerieke karakters zijn ook verboden.` openid_been_used = De OpenID-adres "%s" is al in gebruik. username_has_not_been_changed = Gebruikersnaam is niet veranderd duplicate_invite_to_team = De gebruiker heeft al een uitnodiging ontvangen om deel te nemen aan deze team. @@ -596,7 +598,7 @@ must_use_public_key = De sleutel die u heeft aangeboden is een privésleutel. Al unable_verify_ssh_key = Kan de SSH-sleutel niet verifiëren, controleer deze voor fouten. still_own_repo = Uw account is eigenaar van één of meer repositories, verwijder of draag deze eerst over. admin_cannot_delete_self = U kan uzelf niet verwijderen als u een beheerder bent. Verwijder eerst uw beheerdersrechten. -username_error_no_dots = ` kan alleen alfanumerieke karakters ('0-9','a-z','A-Z'), streepje ('-') en liggend streepje ('_') bevatten. Niet-alfanumerieke karakters aan het begin of eind zijn verboden en aaneenvolgende niet alfanumerieke karakters zijn ook verboden.` +username_error_no_dots = ` kan alleen alfanumerieke karakters ("0-9","a-z","A-Z"), streepje ("-") en liggend streepje ("_") bevatten. Niet-alfanumerieke karakters aan het begin of eind zijn verboden en aaneenvolgende niet alfanumerieke karakters zijn ook verboden.` invalid_group_team_map_error = ` mapping is ongeldig: %s" org_still_own_repo = Deze organisatie is eigenaar van één of meer repositories, verwijder of draag deze eerst over. org_still_own_packages = Deze organisatie is eigenaar van één of meer pakketten, verwijder deze eerst. @@ -745,8 +747,8 @@ ssh_helper=Weet u niet hoe? Lees dan onze handleiding voor het gpg_helper=Hulp nodig? Neem een kijkje op de GitHub handleiding over GPG. add_new_key=SSH sleutel toevoegen add_new_gpg_key=GPG sleutel toevoegen -key_content_ssh_placeholder=Begint met 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', of 'sk-ssh-ed25519@openssh.com' -key_content_gpg_placeholder=Begint met '-----BEGIN PGP PUBLIC KEY BLOCK-----' +key_content_ssh_placeholder=Begint met "ssh-ed25519", "ssh-rsa", "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", "ecdsa-sha2-nistp521", "sk-ecdsa-sha2-nistp256@openssh.com", of "sk-ssh-ed25519@openssh.com" +key_content_gpg_placeholder=Begint met "-----BEGIN PGP PUBLIC KEY BLOCK-----" add_new_principal=Verantwoordelijke toevoegen ssh_key_been_used=Deze SSH-sleutel is al toegevoegd aan de server. ssh_key_name_used=Er bestaat al een SSH sleutel met dezelfde naam in uw account. @@ -764,7 +766,7 @@ gpg_token=Token gpg_token_help=U kunt een handtekening genereren met: gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature=Gepantserde GPG-handtekening -key_signature_gpg_placeholder=Begint met '-----BEGIN PGP SIGNATURE-----' +key_signature_gpg_placeholder=Begint met "-----BEGIN PGP SIGNATURE-----" ssh_key_verified=Geverifieerde sleutel ssh_key_verified_long=Sleutel is geverifieerd met een token en kan worden gebruikt om commits te verifiëren die overeenkomen met alle geactiveerde e-mailadressen voor deze gebruiker. ssh_key_verify=Verifiëren @@ -773,7 +775,7 @@ ssh_token_required=U moet een handtekening opgeven voor het onderstaande token ssh_token=Token ssh_token_help=U kunt een handtekening genereren door het volgende: ssh_token_signature=Gepantserde SSH handtekening -key_signature_ssh_placeholder=Begint met '-----BEGIN SSH SIGNATURE-----' +key_signature_ssh_placeholder=Begint met "-----BEGIN SSH SIGNATURE-----" subkeys=Subkeys key_id=Key-ID key_name=Sleutel naam @@ -805,9 +807,9 @@ manage_social=Beheer gekoppelde sociale accounts unbind=Ontkoppelen manage_access_token=Beheer toegangstokens -generate_new_token=Nieuw Token genereren +generate_new_token=Nieuw token genereren tokens_desc=Deze tokens geven toegang tot je account via de API van Forgejo. -token_name=Symbolische naam +token_name=Tokennaam generate_token=Token genereren generate_token_success=Je nieuwe token is gegenereerd. Kopieer hem nu, want hij kan niet opnieuw worden weergegeven. generate_token_name_duplicate=%s is al gebruikt als een applicatienaam. Gebruik een nieuwe. @@ -1004,7 +1006,7 @@ default_branch=Standaard branch default_branch_helper=De standaard branch is de basis branch voor pull requests en code commits. mirror_prune=Opschonen mirror_prune_desc=Verwijder verouderde remote-tracking-referenties -mirror_interval=Spiegel Interval (geldige tijdseenheden zijn 'h', 'm', 's'). 0 om automatische synchronisatie uit te schakelen (Minimum interval: %s) +mirror_interval=Spiegel Interval (geldige tijdseenheden zijn "h", "m", "s"). 0 om automatische synchronisatie uit te schakelen (Minimum interval: %s) mirror_interval_invalid=Kloon-interval is niet geldig. mirror_sync_on_commit=Synchroniseer wanneer commits gepusht worden mirror_address=Klonen van URL @@ -1071,7 +1073,7 @@ migrate_options_lfs=Migreer LFS bestanden migrate_options_lfs_endpoint.label=LFS Eindpunt migrate_options_lfs_endpoint.description=Migratie zal proberen om je Git remote te gebruiken om de LFS-server te bepalen. Je kan ook een aangepast eindpunt opgeven als de LFS-gegevens ergens anders zijn opgeslagen. migrate_options_lfs_endpoint.description.local=Een lokaal serverpad wordt ook ondersteund. -migrate_items=Migratie Items +migrate_items=Migratie items migrate_items_wiki=Wiki migrate_items_milestones=Mijlpalen migrate_items_labels=Labels @@ -1081,7 +1083,7 @@ migrate_items_merge_requests=Samenvoegen verzoeken migrate_items_releases=Releases migrate_repo=Migreer repository migrate.clone_address=Migreer / kloon van URL -migrate.clone_address_desc=De HTTP(s)- of 'git clone'-URL van een bestaande repository +migrate.clone_address_desc=De HTTP(S) of Git "kloon" URL van een bestaande repository migrate.github_token_desc=Je kunt hier een of meerdere tokens met komma gescheiden plaatsen om sneller te migreren door de GitHub API limiet te beperken. WAARSCHUWING: Het misbruik van deze functie kan in strijd zijn met het beleid van de serviceprovider en leiden tot het blokkeren van rekeningen. migrate.clone_local_path=of een lokaal pad migrate.permission_denied=U bent niet gemachtigd om deze lokale repositories te importeren. @@ -1143,7 +1145,7 @@ find_tag=Label zoeken branches=Branches tags=Labels issues=Issues -pulls=Pull-aanvragen +pulls=Pull Requests project_board=Projecten packages=Paketten labels=Labels @@ -1173,8 +1175,8 @@ escape_control_characters=Escape unescape_control_characters=Onescape file_copy_permalink=Permalink kopiëren view_git_blame=Bekijk Git Blame -video_not_supported_in_browser=Je browser ondersteunt de HTML5 'video'-tag niet. -audio_not_supported_in_browser=Je browser ondersteunt de HTML5 'audio'-tag niet. +video_not_supported_in_browser=Uw browser ondersteunt de HTML5 "video" element niet. +audio_not_supported_in_browser=Uw browser ondersteunt de HTML5 "audio" element niet. stored_lfs=Opgeslagen met Git LFS symbolic_link=Symbolische link commit_graph=Commit grafiek @@ -1202,19 +1204,19 @@ editor.fork_before_edit=Je moet deze repository forken om veranderingen te maken editor.delete_this_file=Verwijder bestand editor.must_have_write_access=U moet schrijftoegang hebben om aanpassingen te maken of voor te stellen in dit bestand. editor.name_your_file=Bestandsnaam… -editor.filename_help=Voeg een map toe door zijn naam te typen, gevolgd door een slash ('/'). Verwijder een map door op backspace te drukken aan het begin van het tekstveld. +editor.filename_help=Voeg een map toe door zijn naam te typen, gevolgd door een slash ("/"). Verwijder een map door op backspace te drukken aan het begin van het tekstveld. editor.or=of editor.cancel_lower=Annuleer editor.commit_signed_changes=Commit Ondertekende Wijzigingen editor.commit_changes=Wijzigingen doorvoeren -editor.add_tmpl='' toevoegen +editor.add_tmpl="" toevoegen editor.patch=Patch toepassen editor.patching=Patchen: editor.new_patch=Nieuwe Patch editor.commit_message_desc=Voeg een optionele uitgebreide omschrijving toe… editor.signoff_desc=Voeg een Signed-off-by toe aan het einde van het commit logbericht. editor.commit_directly_to_this_branch=Commit direct naar de branch '%s'. -editor.create_new_branch=Maak een nieuwe branch voor deze commit en start van een pull-aanvraag. +editor.create_new_branch=Maak een nieuwe branch voor deze commit en start van een pull request. editor.create_new_branch_np=Maak een nieuwe branch voor deze commit. editor.propose_file_change=Stel bestandswijziging voor editor.new_branch_name_desc=Nieuwe branch naam… @@ -1328,7 +1330,7 @@ issues.new_label_placeholder=Labelnaam issues.new_label_desc_placeholder=Beschrijving issues.create_label=Maak label issues.label_templates.title=Laad een vooraf gedefinieerde set labels -issues.label_templates.info=Er bestaan nog geen labels. Maak een nieuw label met 'Nieuw Label' of gebruik de standaardlabels: +issues.label_templates.info=Er bestaan nog geen labels. Maak een nieuw label met "Nieuw Label" of gebruik de standaardlabels: issues.label_templates.helper=Selecteer een labelset issues.label_templates.use=Label Set gebruiken issues.add_label=voegde het %s label %s toe @@ -1498,7 +1500,7 @@ issues.add_time_sum_to_small=Geen tijd opgegeven. issues.time_spent_total=Totaal besteedde tijd issues.time_spent_from_all_authors=`Totaal besteedde tijd: %s` issues.due_date=Vervaldatum -issues.invalid_due_date_format=Het formaat van de deadline is moet 'jjjj-mm-dd' zijn. +issues.invalid_due_date_format=Het formaat van de deadline is moet "jjjj-mm-dd" zijn. issues.error_modifying_due_date=Deadline aanpassen mislukt. issues.error_removing_due_date=Deadline verwijderen mislukt. issues.push_commit_1=toegevoegd %d commit %s @@ -1514,7 +1516,7 @@ issues.due_date_added=heeft %[2]s de deadline %[1]s toegevoegd issues.due_date_modified=de vervaldatum van %[2]s is gewijzigd naar %[1]s[3]s issues.due_date_remove=heeft %[2]s de deadline %[1]s verwijderd issues.due_date_overdue=Over tijd -issues.due_date_invalid=De deadline is ongeldig of buiten bereik. Gebruik het formaat 'jjjj-mm-dd'. +issues.due_date_invalid=De deadline is ongeldig of buiten bereik. Gebruik het formaat "jjjj-mm-dd'. issues.dependency.title=Afhankelijkheden issues.dependency.issue_no_dependencies=Geen afhankelijkheden ingesteld. issues.dependency.pr_no_dependencies=Geen afhankelijkheden ingesteld. @@ -1524,7 +1526,7 @@ issues.dependency.remove=Verwijder issues.dependency.remove_info=Verwijder afhankelijkheid issues.dependency.added_dependency=`voegde een nieuwe afhankelijkheid %s toe ` issues.dependency.removed_dependency=`verwijderde een afhankelijkheid %s` -issues.dependency.pr_closing_blockedby=Het sluiten van deze pull-aanvraag is geblokkeerd door de volgende issues +issues.dependency.pr_closing_blockedby=Het sluiten van deze pull request is geblokkeerd door de volgende issues issues.dependency.issue_closing_blockedby=Het sluiten van dit issue is geblokkeerd door de volgende problemen issues.dependency.issue_close_blocks=Deze issue blokkeert het sluiten van de volgende issues issues.dependency.pr_close_blocks=Deze pull request blokkeert het sluiten van de volgende issues @@ -1534,7 +1536,7 @@ issues.dependency.blocks_short=Blokkeert issues.dependency.blocked_by_short=Afhankelijk van issues.dependency.remove_header=Verwijder afhankelijkheid issues.dependency.issue_remove_text=Hiermee wordt de afhankelijkheid van deze kwestie verwijderd. Doorgaan? -issues.dependency.pr_remove_text=Hiermee wordt de afhankelijkheid van deze pull-aanvraag verwijderd. Doorgaan? +issues.dependency.pr_remove_text=Hiermee wordt de afhankelijkheid van deze pull request verwijderd. Doorgaan? issues.dependency.setting=Schakel afhankelijkheden voor issues en pull requests in issues.dependency.add_error_same_issue=Je kan een kwestie niet afhankelijk maken van zichzelf. issues.dependency.add_error_dep_issue_not_exist=De afhankelijke kwestie bestaat niet. @@ -1542,8 +1544,8 @@ issues.dependency.add_error_dep_not_exist=Afhankelijkheid bestaat niet. issues.dependency.add_error_dep_exists=Afhankelijkheid bestaat al. issues.dependency.add_error_cannot_create_circular=Je kan geen afhankelijkheid maken waarbij twee issues elkaar blokkeren. issues.dependency.add_error_dep_not_same_repo=Beide issues moeten in dezelfde repository zijn. -issues.review.self.approval=Je kan je eigen pull-aanvraag niet goedkeuren. -issues.review.self.rejection=Je kan geen wijzigingen aanvragen op je eigen pull-aanvraag. +issues.review.self.approval=U kunt niet uw eigen pull request goedkeuren. +issues.review.self.rejection=U kunt geen wijzigingen aanvragen op uw eigen pull request. issues.review.approve=heeft deze veranderingen %s goedgekeurd issues.review.comment=beoordeeld %s issues.review.dismissed=%s's beoordeling afgewezen %s @@ -1579,10 +1581,10 @@ issues.reference_link=Referentie: %s compare.compare_base=basis compare.compare_head=vergelijk -pulls.desc=Schakel pull-aanvragen en code-beoordelingen in. +pulls.desc=Schakel pull requets en code-beoordelingen in. pulls.new=Nieuwe Pull aanvraag pulls.view=Pull verzoek bekijken -pulls.compare_changes=Nieuwe pull-aanvraag +pulls.compare_changes=Nieuwe pull request pulls.allow_edits_from_maintainers=Bewerkingen toestaan van maintainers pulls.allow_edits_from_maintainers_desc=Gebruikers met schrijftoegang tot de basis branch kunnen ook pushen naar deze branch pulls.allow_edits_from_maintainers_err=Updaten mislukt @@ -1596,7 +1598,7 @@ pulls.switch_comparison_type=Wissel vergelijking type pulls.switch_head_and_base=Verwissel hoofd en basis pulls.filter_branch=Filter branch pulls.no_results=Geen resultaten gevonden. -pulls.nothing_to_compare=Deze branches zijn gelijk. Er is geen pull-aanvraag nodig. +pulls.nothing_to_compare=Deze branches zijn gelijk. Er is geen pull request nodig. pulls.nothing_to_compare_and_allow_empty_pr=Deze branches zijn gelijk. Deze pull verzoek zal leeg zijn. pulls.has_pull_request=`Een pull-verzoek tussen deze branches bestaat al: %[2]s#%[3]d` pulls.create=Pull verzoek aanmaken @@ -1607,25 +1609,25 @@ pulls.tab_conversation=Discussie pulls.tab_commits=Commits pulls.tab_files=Bestanden gewijzigd pulls.reopen_to_merge=Heropen deze pull request om een merge uit te voeren. -pulls.cant_reopen_deleted_branch=Deze pull-aanvraag kan niet opnieuw worden geopend omdat de branch is verwijderd. +pulls.cant_reopen_deleted_branch=Deze pull request kan niet opnieuw worden geopend omdat de branch is verwijderd. pulls.merged=Samengevoegd pulls.manually_merged=Handmatig samengevoegd -pulls.is_closed=Deze pull-aanvraag is gesloten. -pulls.title_wip_desc=`Start de titel met %s om te voorkomen dat deze pull-aanvraag per ongeluk wordt samengevoegd.` +pulls.is_closed=Deze pull request is gesloten. +pulls.title_wip_desc=`Start de titel met %s om te voorkomen dat deze pull request per ongeluk wordt samengevoegd.` pulls.cannot_merge_work_in_progress=Dit pull request is gemarkeerd als werk in uitvoering. pulls.still_in_progress=Nog steeds bezig? pulls.add_prefix=Voeg %s prefix toe pulls.remove_prefix=Verwijder %s prefix -pulls.data_broken=Deze pull-aanvraag is ongeldig wegens missende fork-informatie. +pulls.data_broken=Deze pull request is ongeldig wegens missende fork-informatie. pulls.files_conflicted=Dit pull request heeft wijzigingen die strijdig zijn met de doel branch. pulls.is_checking=Controle op merge conflicten is nog bezig. Probeer later nog een keer. pulls.is_ancestor=Deze branch is al opgenomen in de toegewezen branch. Er is niets om samen te voegen. pulls.is_empty=De wijzigingen in deze branch bevinden zich al in de toegewezen branch. Dit zal een lege commit zijn. pulls.required_status_check_failed=Sommige vereiste controles waren niet succesvol. pulls.required_status_check_missing=Er ontbreken enkele vereiste controles. -pulls.required_status_check_administrator=Als een beheerder kunt u deze pull-aanvraag nog samenvoegen. +pulls.required_status_check_administrator=Als een beheerder kunt u deze pull request nog samenvoegen. pulls.can_auto_merge_desc=Dit pull-request kan automatisch samengevoegd worden. -pulls.cannot_auto_merge_desc=Deze pull-aanvraag kan niet automatisch worden samengevoegd wegens conflicten. +pulls.cannot_auto_merge_desc=Deze pull request kan niet automatisch worden samengevoegd wegens conflicten. pulls.cannot_auto_merge_helper=Voeg handmatig samen om de conflicten op te lossen. pulls.num_conflicting_files_1=%d conflicterend bestand pulls.num_conflicting_files_n=%d conflicterende bestanden @@ -1637,11 +1639,11 @@ pulls.waiting_count_1=%d wachtende beoordeling pulls.waiting_count_n=%d wachtende beoordelingen pulls.wrong_commit_id=commit id moet een commit id zijn op de doelbranch -pulls.no_merge_desc=Deze pull-aanvraag kan niet worden samengevoegd, omdat alle samenvoegingsopties zijn uitgeschakeld. -pulls.no_merge_helper=Zet samenvoegingsopties aan in de repositoryinstellingen of voeg de pull-aanvraag handmatig samen. -pulls.no_merge_wip=Deze pull-aanvraag kan niet worden samengevoegd omdat hij als "work in progress" is gemarkeerd. -pulls.no_merge_not_ready=Deze pull-aanvraag is niet klaar om samen te voegen, controleer de status en status controles. -pulls.no_merge_access=Je bent niet gemachtigd om deze pull-aanvraag samen te voegen. +pulls.no_merge_desc=Deze pull request kan niet worden samengevoegd, omdat alle samenvoegingsopties zijn uitgeschakeld. +pulls.no_merge_helper=Zet samenvoegingsopties aan in de repositoryinstellingen of voeg de pull request handmatig samen. +pulls.no_merge_wip=Deze pull request kan niet worden samengevoegd omdat hij als "work in progress" is gemarkeerd. +pulls.no_merge_not_ready=Deze pull request is niet klaar om samen te voegen, controleer de status en status controles. +pulls.no_merge_access=Je bent niet gemachtigd om deze pull request samen te voegen. pulls.merge_pull_request=Maak samenvoeg-commit pulls.rebase_merge_pull_request=Herbaseren dan snel-voorwaarts pulls.rebase_merge_commit_pull_request=Herbaseren dan samenvoeg-commit maken @@ -1650,7 +1652,7 @@ pulls.merge_manually=Handmatig samengevoegd pulls.merge_commit_id=De merge commit ID pulls.require_signed_wont_sign=De branch heeft ondertekende commits nodig, maar deze merge zal niet worden ondertekend -pulls.invalid_merge_option=Je kan de samenvoegingsoptie niet gebruiken voor deze pull-aanvraag. +pulls.invalid_merge_option=Je kan de samenvoegingsoptie niet gebruiken voor deze pull request. pulls.merge_conflict=Samenvoegen mislukt: Er was een conflict tijdens het samenvoegen. Hint: Probeer een andere strategie pulls.merge_conflict_summary=Foutmelding pulls.rebase_conflict=Samenvoegen mislukt: Er was een conflict tijdens het rebasen van commit: %[1]s. Hint: Probeer een andere strategie @@ -1661,7 +1663,7 @@ pulls.head_out_of_date=Samenvoegen mislukt: tijdens het genereren van de samenvo pulls.push_rejected=Samenvoegen mislukt: De push is geweigerd. Controleer de Git Hooks voor deze repository. pulls.push_rejected_summary=Volledig afwijzingsbericht pulls.push_rejected_no_message=Samenvoegen mislukt: De push is afgewezen, maar er was geen extern bericht.
      Controleer de Git Hooks voor deze repository -pulls.open_unmerged_pull_exists=`Je kan deze pull-aanvraag niet opnieuw openen omdat er een andere (#%d) met identieke eigenschappen open staat.` +pulls.open_unmerged_pull_exists=`Je kan deze pull request niet opnieuw openen omdat er een andere (#%d) met identieke eigenschappen open staat.` pulls.status_checking=Sommige controles zijn in behandeling pulls.status_checks_success=Alle checks waren succesvol pulls.status_checks_warning=Sommige controles hebben waarschuwingen gerapporteerd @@ -1683,8 +1685,8 @@ pulls.auto_merge_newly_scheduled=De pull-verzoek was gepland om samen te voegen pulls.auto_merge_has_pending_schedule=%[1]s heeft deze pull-verzoek automatisch samengevoegd wanneer alle checks succesvol zijn geweest %[2]s. pulls.auto_merge_cancel_schedule=Automatisch samenvoegen annuleren -pulls.auto_merge_not_scheduled=Deze pull-aanvraag is niet gepland om automatisch samen te voegen. -pulls.auto_merge_canceled_schedule=De automatisch samenvoegen is geannuleerd voor deze pull-aanvraag. +pulls.auto_merge_not_scheduled=Deze pull request is niet gepland om automatisch samen te voegen. +pulls.auto_merge_canceled_schedule=De automatisch samenvoegen is geannuleerd voor deze pull request. pulls.delete.title=Deze pull-verzoek verwijderen? @@ -1703,7 +1705,7 @@ milestones.title=Titel milestones.desc=Beschrijving milestones.due_date=Vervaldatum (optioneel) milestones.clear=Leegmaken -milestones.invalid_due_date_format=Het formaat van de deadline is moet 'jjjj-mm-dd' zijn. +milestones.invalid_due_date_format=Het formaat van de deadline is moet "jjjj-mm-dd" zijn. milestones.edit=Bewerk mijlpaal milestones.edit_subheader=Gebruik mijlpalen om issues te organiseren en om voortgang bij te houden. milestones.cancel=Annuleer @@ -1740,7 +1742,7 @@ wiki.delete_page_button=Verwijder pagina wiki.page_already_exists=Er bestaat al een wiki-pagina met deze naam. wiki.pages=Pagina’s wiki.last_updated=Laatst bijgewerkt: %s -wiki.page_name_desc=Voer een naam in voor deze Wiki pagina. Sommige speciale namen zijn: 'Home', '_Sidebar' en '_Footer'. +wiki.page_name_desc=Voer een naam in voor deze Wiki pagina. Sommige speciale namen zijn: "Home", "_Sidebar" and "_Footer". activity=Activiteit activity.period.filter_label=Periode: @@ -1752,12 +1754,12 @@ activity.period.quarterly=3 maanden activity.period.semiyearly=6 maanden activity.period.yearly=1 jaar activity.overview=Overzicht -activity.active_prs_count_1=%d actieve pull-aanvragen +activity.active_prs_count_1=%d actieve pull requests activity.active_prs_count_n=%d Actieve Pull Requests -activity.merged_prs_count_1=Samengevoegde pull-aanvraag -activity.merged_prs_count_n=Samengevoegde pull-aanvragen -activity.opened_prs_count_1=Voorgestelde Pull-aanvraag -activity.opened_prs_count_n=Voorgestelde pull-aanvragen +activity.merged_prs_count_1=Samengevoegde pull request +activity.merged_prs_count_n=Samengevoegde pull requests +activity.opened_prs_count_1=Voorgestelde pull request +activity.opened_prs_count_n=Voorgestelde pull requests activity.title.user_1=%d gebruiker activity.title.user_n=%d gebruikers activity.title.prs_1=%d Pull aanvraag @@ -1866,7 +1868,7 @@ settings.tracker_issue_style.regexp_pattern=Reguliere expressie patroon settings.tracker_url_format_desc=Gebruik de aanduidingen {user}, {repo} en {index} voor de gebruikersnaam, repositorynaam en kwestie-index. settings.enable_timetracker=Tijdregistratie inschakelen settings.allow_only_contributors_to_track_time=Sta alleen bijdragers toe tijdregistratie te gebruiken -settings.pulls_desc=Repository-pull-aanvragen inschakelen +settings.pulls_desc=Repository pull requests inschakelen settings.pulls.ignore_whitespace=Witruimte negeren voor conflicten settings.trust_model.collaborator.long=Medewerker: Vertrouw handtekeningen door medewerkers settings.trust_model.committer=Committer @@ -1967,7 +1969,7 @@ settings.event_pull_request=Pull request settings.event_pull_request_desc=Pull request geopend, gesloten, heropend of bewerkt. settings.event_pull_request_assign=Pull request toegewezen settings.event_pull_request_assign_desc=Pull request toegewezen of niet-toegewezen. -settings.event_pull_request_label=Pull-aanvraag gelabeld +settings.event_pull_request_label=Pull request gelabeld settings.event_pull_request_label_desc=Pull request labels bijgewerkt of gewist. settings.event_pull_request_milestone=Pull Request gemilestoned settings.event_pull_request_milestone_desc=Pull Reguest gemilestoned of gedemilestoned. @@ -2024,7 +2026,7 @@ settings.protected_branch=Branch bescherming settings.protected_branch_can_push=Push toestaan? settings.protected_branch_can_push_yes=U mag pushen settings.protected_branch_can_push_no=U mag niet pushen -settings.branch_protection=Branch Bescherming voor branch '%s' +settings.branch_protection=Branch Bescherming voor branch "%s" settings.protect_this_branch=Branch bescherming inschakelen settings.protect_this_branch_desc=Voorkomt verwijdering en beperkt Git pushing en samenvoegen tot de branch. settings.protect_disable_push=Push uitschakelen @@ -2100,7 +2102,7 @@ settings.lfs_findcommits=Vind commits settings.lfs_lfs_file_no_commits=Geen Commits gevonden voor dit LFS-bestand settings.lfs_noattribute=Dit pad heeft niet het vergrendelbare attribuut in de standaard branch settings.lfs_delete=LFS-bestand met OID %s verwijderen -settings.lfs_delete_warning=Het verwijderen van een LFS bestand kan leiden tot 'object bestaat niet' fouten bij het uitchecken. Weet u het zeker? +settings.lfs_delete_warning=Het verwijderen van een LFS bestand kan leiden tot "object bestaat niet" fouten bij het uitchecken. Weet u het zeker? settings.lfs_findpointerfiles=Zoek pointer bestanden settings.lfs_locks=Vergrendeld settings.lfs_invalid_locking_path=Ongeldig pad: %s @@ -2229,7 +2231,7 @@ settings.ignore_stale_approvals = Negeer verouderde goedkeuringen settings.ignore_stale_approvals_desc = Tel goedkeuringen gemaakt op oudere commits (verouderde reviews) niet mee voor het aantal goedkeuringen dat het PR heeft. Irrelevant als verouderde reviews al afgekeurd zijn. settings.protect_branch_name_pattern_desc = Beschermd branch naam patronen. Zie de documentatie voor patroon syntax. Bijvoorbeeld: main, release/** settings.protect_patterns = Patronen -settings.protect_protected_file_patterns = Beschermde bestand patronen (gescheiden door een puntkomma ';'): +settings.protect_protected_file_patterns = Beschermde bestand patronen (gescheiden door een puntkomma ";"): issues.no_content = Geen beschrijving gegeven. issues.close = Issue sluiten issues.comment_pull_merged_at = commit %[1]s samengevoegd in %[2]s %[3]s @@ -2279,7 +2281,7 @@ branch.new_branch = Creëer nieuwe branch branch.new_branch_from = Creëer nieuwe branch van "%s" branch.renamed = Branch %s is hernoemd naar %s. tag.create_success = Tag "%s" is gecreëerd. -topic.format_prompt = Onderwerpen moeten beginnen met een letter of cijfer, kunnen streepjes ('-') en puntjes ('.') bevatten en mogen maximaal 35 tekens lang zijn. Letters moeten kleine letters zijn. +topic.format_prompt = Onderwerpen moeten beginnen met een letter of cijfer, kunnen streepjes ("-") en puntjes (".") bevatten en mogen maximaal 35 tekens lang zijn. Letters moeten kleine letters zijn. find_file.go_to_file = Ga naar bestand find_file.no_matching = Geen overeenkomstige bestanden gevonden error.csv.too_large = Kan dit bestand niet renderen omdat het te groot is. @@ -2438,7 +2440,7 @@ settings.transfer = Eigendom overdragen settings.transfer.success = Repository overdracht was succesvol. settings.transfer_abort = Overdracht annuleren settings.transfer_abort_success = De repository overdracht naar %s is succesvol geannuleerd. -settings.enter_repo_name = Voer de naam van de repository in ter bevestiging: +settings.enter_repo_name = Voer de eigenaar en de naam van de repository in precies zoals weergegeven: settings.transfer_in_progress = Er is momenteel een overdracht aan de gang. Annuleer deze als je deze repository wilt overdragen aan een andere gebruiker. settings.transfer_notices_1 = - Je verliest de toegang tot het archief als je het overdraagt aan een individuele gebruiker. settings.transfer_succeed = De repository is overgedragen. @@ -2482,7 +2484,7 @@ settings.admin_stats_indexer = Code statistieken indexer settings.new_owner_blocked_doer = De nieuwe eigenaar heeft u geblokkeerd. settings.transfer_notices_2 = - Je behoudt toegang tot de repository als je het overdraagt aan een organisatie waarvan je (mede-)eigenaar bent. commits.search.tooltip = U kunt zoektermen voorvoegen met "author:", "committer:", "after:", of "before:", bijvoorbeeld: "revert author:Alice before:2019-01-13". -projects.column.deletion_desc = Het verwijderen van een projectkolom verplaatst alle issues naar 'Ongecategoriseerd'. Wilt u doorgaan? +projects.column.deletion_desc = Het verwijderen van een projectkolom verplaatst alle issues naar "Ongecategoriseerd". Wilt u doorgaan? projects.column.set_default_desc = Stel deze kolom in als standaard voor ongecategoriseerde issues and pulls issues.action_check = Aanvinken/uitvinken issues.dependency.issue_batch_close_blocked = Het is niet mogelijk om de issues die u gekozen heeft in bulk te sluiten, omdat issue #%d nog open afhankelijkheden heeft @@ -2527,9 +2529,9 @@ diff.review.self_approve = Auteurs van een pull request kunnen hun eigen pull re diff.review.self_reject = Auteurs van een pull request kunnen geen wijzigingen aanvragen op hun eigen pull request branch.already_exists = Een branch genaamd "%s" bestaat al. settings.protected_branch_required_rule_name = Vereiste regelnaam -settings.protect_unprotected_file_patterns_desc = Onbeschermde bestanden die direct gewijzigd mogen worden als een gebruiker schrijftoegang heeft, waarbij pushbeperking omzeild zal worden. Meerdere patronen kunnen gescheiden worden d.m.v. een puntkomma (';'). Zie github.com/gobwas/glob documentatie voor patroon syntax. Bijvoorbeeld: .drone.yml, /docs/**/*.txt. +settings.protect_unprotected_file_patterns_desc = Onbeschermde bestanden die direct gewijzigd mogen worden als een gebruiker schrijftoegang heeft, waarbij pushbeperking omzeild zal worden. Meerdere patronen kunnen gescheiden worden d.m.v. een puntkomma (";"). Zie github.com/gobwas/glob documentatie voor patroon syntax. Bijvoorbeeld: .drone.yml, /docs/**/*.txt. settings.tags.protection.pattern.description = U kunt een enkele naam, glob patroon of reguliere expressie gebruiken om tags te matchen. Lees meer in de beschermde tags gids. -settings.protect_unprotected_file_patterns = Onbeschermde bestandspatronen (gescheiden d.m.v. een puntkomma ';'): +settings.protect_unprotected_file_patterns = Onbeschermde bestandspatronen (gescheiden d.m.v. een puntkomma ";"): branch.delete_desc = Het verwijderen van een branch is permanent. Hoewel de verwijderde branch kan blijven bestaan voor een korte tijd voordat het daadwerkelijk wordt verwijderd, kan het in de meeste gevallen NIET ongedaan gemaakt worden. Wilt u doorgaan? release.deletion_desc = Het verwijderen van een release zal het alleen verwijderen van Forgejo. Het zal niet de Git tag, de inhoud van uw repository of de geschiedenis ervan beïnvloeden. Wilt u doorgaan? release.deletion_tag_desc = Verwijdert deze tag uit de repository. De inhoud van de repository en de geschiedenis ervan zullen ongewijzigd blijven. Wilt u doorgaan? @@ -2598,7 +2600,7 @@ settings.wiki_rename_branch_main_notices_2 = Dit zal de interne branch van %s's settings.trust_model.collaborator.desc = Geldige handtekeningen van samenwerkers van deze repository worden als "vertrouwd" gemarkeerd - (of ze nu overeenkomen met de committer of niet). Anders worden geldige handtekeningen gemarkeerd als "niet-vertrouwd" als de handtekening overeenkomt met de committer en "niet-gematcht" als dat niet het geval is. settings.trust_model.committer.desc = Geldige handtekeningen zullen alleen "vertrouwd" gemarkeerd worden als ze overeenkomen met de committer, anders zullen ze gemarkeerd worden als "ongeëvenaard". Dit dwingt Forgejo om de committer te zijn op ondertekende commits met de werkelijke committer gemarkeerd als Co-authored-by: en Co-committed-by: aanhanger in de commit. De standaard Forgejo sleutel moet overeenkomen met een gebruiker in de database. settings.pulls.enable_autodetect_manual_merge = Handmatig samenvoegen met autodetectie inschakelen (Opmerking: In sommige speciale gevallen kunnen hierdoor verkeerde beoordelingen optreden) -settings.protect_protected_file_patterns_desc = Beschermde bestanden mogen niet direct gewijzigd worden, zelfs als de gebruiker rechten heeft om bestanden in deze branch toe te voegen, te bewerken of te verwijderen. Meerdere patronen kunnen gescheiden worden met een puntkomma (';'). Zie github.com/gobwas/glob documentatie voor patroon syntax. Voorbeelden: .drone.yml, /docs/**/*.txt. +settings.protect_protected_file_patterns_desc = Beschermde bestanden mogen niet direct gewijzigd worden, zelfs als de gebruiker rechten heeft om bestanden in deze branch toe te voegen, te bewerken of te verwijderen. Meerdere patronen kunnen gescheiden worden met een puntkomma (";"). Zie github.com/gobwas/glob documentatie voor patroon syntax. Voorbeelden: .drone.yml, /docs/**/*.txt. wiki.delete_page_notice_1 = Het verwijderen van de wikipagina "%s" kan niet ongedaan worden gemaakt. Doorgaan? wiki.reserved_page = De wikipaginanaam "%s" is gereserveerd. activity.navbar.pulse = Puls @@ -2634,7 +2636,7 @@ search.fuzzy.tooltip = Neem resultaten op die ook sterk overeenkomen met de zoek search.match.tooltip = Alleen resultaten opnemen die exact overeenkomen met de zoekterm settings.wiki_rename_branch_main = Normaliseer de Wiki branch naam settings.event_pull_request_approvals = Pull Request Goedkeuringen -settings.protected_branch_duplicate_rule_name = Duplicaat regelnaam +settings.protected_branch_duplicate_rule_name = Er is al een regel voor deze reeks branches settings.convert_fork_succeed = De fork is omgezet in een reguliere repository. settings.convert_fork_notices_1 = Deze bewerking zet de fork om in een reguliere repository en kan niet ongedaan worden gemaakt. settings.convert_fork_desc = Je kunt deze fork omzetten in een reguliere repository. Dit kan niet ongedaan worden gemaakt. @@ -2643,6 +2645,10 @@ settings.convert_confirm = Repository Omzetten settings.convert_succeed = De mirror is omgezet in een reguliere repository. settings.convert_desc = Je kunt deze mirror omzetten in een reguliere repository. Dit kan niet ongedaan worden gemaakt. settings.convert_notices_1 = Deze bewerking zet de mirror om in een reguliere repository en kan niet ongedaan worden gemaakt. +pulls.agit_explanation = Gemaakt met behulp van de AGit workflow. AGit laat bijdragers wijzigingen voorstellen met "git push" zonder een fork of een nieuwe branch aan te maken. +settings.confirmation_string = Confirmatie string +activity.navbar.code_frequency = Code Frequentie +activity.navbar.recent_commits = Recente commits @@ -2817,9 +2823,9 @@ dashboard.archive_cleanup=Verwijder oude repositories archieven dashboard.deleted_branches_cleanup=Verwijderde branches opschonen dashboard.update_migration_poster_id=Werk migratie-poster IDs bij dashboard.git_gc_repos=Voer garbage collectie uit voor alle repositories -dashboard.resync_all_sshkeys=Werk de '.ssh/authorized_keys' bestand bij met Forgejo SSH sleutels. -dashboard.resync_all_sshprincipals=Update het '.ssh/authorized_principals' bestand met Forgejo SSH verantwoordelijken. -dashboard.resync_all_hooks=Opnieuw synchroniseren van pre-ontvangst, bewerk en post-ontvangst hooks voor alle repositories. +dashboard.resync_all_sshkeys=Werk de ".ssh/authorized_keys" bestand bij met Forgejo SSH sleutels. +dashboard.resync_all_sshprincipals=Update het ".ssh/authorized_principals" bestand met Forgejo SSH verantwoordelijken. +dashboard.resync_all_hooks=Opnieuw synchroniseren van pre-ontvangst, bewerk en post-ontvangst hooks voor alle repositories dashboard.reinit_missing_repos=Herinitialiseer alle ontbrekende Git repositories waarvoor records bestaan dashboard.sync_external_users=Externe gebruikersgegevens synchroniseren dashboard.server_uptime=Uptime server @@ -2964,7 +2970,7 @@ auths.search_page_size=Paginagrootte auths.filter=Gebruikersfilter auths.admin_filter=Beheerdersfilter auths.restricted_filter=Beperkt filter -auths.restricted_filter_helper=Laat leeg om geen gebruikers als beperkt in te stellen. Gebruik een asterisk ('*') om alle gebruikers die niet overeenkomen met Admin Filter als beperkt in te stellen. +auths.restricted_filter_helper=Laat leeg om geen gebruikers als beperkt in te stellen. Gebruik een asterisk ("*") om alle gebruikers die niet overeenkomen met Admin Filter als beperkt in te stellen. auths.group_search_base=Groep zoekbasis DN auths.group_attribute_list_users=Groep Attribuut met lijst van gebruikers auths.user_attribute_in_group=Gebruikerskenmerken vermeld in groep @@ -2972,7 +2978,7 @@ auths.smtp_auth=SMTP-authenticatietype auths.smtphost=SMTP host auths.smtpport=SMTP poort auths.allowed_domains=Toegelaten domeinen -auths.allowed_domains_helper=Laat leeg om alle domeinen toe te staan. Meerdere domeinen scheiden met een komma (','). +auths.allowed_domains_helper=Laat leeg om alle domeinen toe te staan. Meerdere domeinen scheiden met een komma (","). auths.skip_tls_verify=TLS-verificatie overslaan auths.pam_service_name=PAM servicenaam auths.oauth2_provider=OAuth2 Provider @@ -3038,7 +3044,7 @@ config.ssh_port=Poort config.ssh_listen_port=Luister op poort config.ssh_root_path=Root-pad config.ssh_key_test_path=Pad voor key-tests -config.ssh_keygen_path=Pad van keygen ('ssh-keygen') +config.ssh_keygen_path=Pad van keygen ("ssh-keygen") config.ssh_minimum_key_size_check=Controleer minimale key-lengte config.ssh_minimum_key_sizes=Minimale key-lengtes @@ -3257,7 +3263,7 @@ auths.helo_hostname = HELO Hostnaam settings = Beheerdersinstellingen dashboard.task.cancelled = Taak: %[1]s geannuleerd: %[3]s auths.force_smtps = SMTPS Forceren -dashboard.sync_repo_branches = Synchroniseren gemiste branches van git data naar databases +dashboard.sync_repo_branches = Synchroniseren gemiste branches van git data naar database monitor.processes_count = %d Processen monitor.process.children = Kinderen self_check.database_inconsistent_collation_columns = Database gebruikt collatie %s, maar deze kolommen gebruiken onjuiste collaties. Dit kan onverwachte problemen veroorzaken. @@ -3276,7 +3282,7 @@ auths.oauth2_icon_url = Pictogram URL auths.pam_email_domain = PAM e-maildomein (optioneel) auths.tip.gitea = Registreer een nieuwe OAuth2-toepassing. De handleiding is te vinden op https://docs.gitea.com/development/oauth2-provider auths.tip.discord = Registreer een nieuwe toepassing op https://discordapp.com/developers/applications/me -auths.tip.bitbucket = Registreer een nieuwe OAuth consumer op https://bitbucket.org/account/user//oauth-consumers/new en voeg de rechten 'Account' - 'Read' +auths.tip.bitbucket = Registreer een nieuwe OAuth consumer op https://bitbucket.org/account/user//oauth-consumers/new en voeg de rechten "Account" - "Read" auths.tips.oauth2.general.tip = Bij het registreren van een nieuwe OAuth2-authenticatie moet de callback/redirect URL zijn: config.ssh_domain = SSH-server domein auths.login_source_of_type_exist = Er bestaat al een authenticatiebron van dit type. @@ -3413,7 +3419,7 @@ owner.settings.cleanuprules.remove.days = Verwijder versies ouder dan owner.settings.cleanuprules.remove.pattern = Verwijder versies die overeenkomen met owner.settings.cleanuprules.success.update = Opruimregel is bijgewerkt. owner.settings.cleanuprules.success.delete = Opruimregel is verwijderd. -owner.settings.chef.title = Chef Register +owner.settings.chef.title = Chef register owner.settings.chef.keypair = Genereer sleutelpaar owner.settings.cleanuprules.remove.title = Versies die overeenkomen met deze regels worden verwijderd, tenzij een regel hierboven zegt dat ze bewaard moeten worden. owner.settings.cleanuprules.keep.pattern.container = De laatste versie wordt altijd bewaard voor Container pakketten. @@ -3485,7 +3491,7 @@ npm.dependencies = Afhankelijkheden npm.dependencies.development = Ontwikkelings Afhankelijkheden npm.dependencies.peer = Peer afhankelijkheden npm.dependencies.optional = Optionele afhankelijkheden -owner.settings.cargo.title = Cargo Register Index +owner.settings.cargo.title = Cargo register index owner.settings.cargo.initialize = Index initialiseren owner.settings.cargo.initialize.error = Cargo index is niet geïnitialiseerd: %v owner.settings.cargo.initialize.success = De Cargo index is met succes aangemaakt. @@ -3624,7 +3630,7 @@ runs.no_runs = De workflow heeft nog geen runs. runs.empty_commit_message = (leeg commit bericht) workflow.disable = Workflow uitschakelen workflow.enable = Workflow inschakelen -workflow.enable_success = Workflow "%s' is succesvol ingeschakeld. +workflow.enable_success = Workflow "%s" is succesvol ingeschakeld. workflow.disabled = Workflow is uitgeschakeld. need_approval_desc = Heb goedkeuring nodig om workflows uit te voeren voor fork pull request. variables = Variabelen @@ -3646,7 +3652,7 @@ runs.no_results = Geen resultaten gevonden. runs.no_workflows = Er zijn nog geen workflows. unit.desc = Beheer actions runs.no_workflows.documentation = Voor meer informatie over Forgejo acties, zie de documentatie. -workflow.disable_success = Workflow "%s' is succesvol uitgeschakeld. +workflow.disable_success = Workflow "%s" is succesvol uitgeschakeld. variables.none = Er zijn nog geen variabelen. runners.task_list = Recente taken op deze runner runners.delete_runner_notice = Als er een taak op deze runner draait, wordt deze beëindigd en gemarkeerd als mislukt. Dit kan het bouwen van de workflow onderbreken. @@ -3680,3 +3686,5 @@ component_failed_to_load = Er is een onverwachte fout opgetreden. contributors.what = bijdragen component_loading_failed = %s kon niet worden geladen component_loading = Bezig met laden van %s... +code_frequency.what = code frequentie +recent_commits.what = recente commits diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index f9651e89b7..a55f466bd9 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -141,6 +141,7 @@ go_back = Voltar view = Visualizar copy_hash = Copiar hash tracked_time_summary = Resumo do tempo de rastreamento baseado em filtros da lista de issues +confirm_delete_artifact = Tem certeza de que deseja excluir o artefato "%s"? [aria] navbar=Barra de navegação @@ -429,6 +430,7 @@ change_unconfirmed_email_error = Erro ao alterar o endereço de e-mail: %v change_unconfirmed_email_summary = Alterar o endereço de e-mail que o e-mail de ativação será enviado para. last_admin = Não é possível remover o último administrador. Deve haver ao menos um usuário administrador. change_unconfirmed_email = Se você colocou o endereço de e-mail errado durante o cadastro, você pode alterá-lo abaixo, e uma confirmação será enviada para o novo endereço. +remember_me.compromised = O token de login foi invalidado, o que pode indicar que a sua conta foi comprometida. Verifique se não há atividades suspeitas em sua conta. [mail] view_it_on=Veja em %s @@ -948,6 +950,7 @@ webauthn_key_loss_warning = Caso perca as suas chaves de segurança, você perde blocked_users_none = Nenhum usuário bloqueado. access_token_desc = As permissões selecionadas para o token limitam o acesso apenas às rotas da API correspondentes. Veja a documentação para mais informações. webauthn_alternative_tip = Você talvez queira configurar um método adicional de autenticação. +change_password = Alterar senha [repo] owner=Proprietário @@ -1343,7 +1346,7 @@ projects.column.edit=Editar coluna projects.column.edit_title=Nome projects.column.new_title=Nome projects.column.new_submit=Criar coluna -projects.column.new=Nova Coluna +projects.column.new=Adicionar coluna projects.column.set_default=Atribuir como padrão projects.column.set_default_desc=Definir esta coluna como padrão para pull e issues sem categoria projects.column.unset_default=Desatribuir padrão @@ -1355,7 +1358,7 @@ projects.open=Abrir projects.close=Fechar projects.column.assigned_to=Atribuído a projects.card_type.desc=Pré-visualizações de Cards -projects.card_type.images_and_text=Imagens e Texto +projects.card_type.images_and_text=Imagens e texto projects.card_type.text_only=Somente texto issues.desc=Organize relatórios de bugs, tarefas e marcos. @@ -1549,8 +1552,8 @@ issues.subscribe=Inscrever-se issues.unsubscribe=Desinscrever issues.unpin_issue=Desfixar issue issues.max_pinned=Você não pode fixar mais issues -issues.pin_comment=fixou isto %s -issues.unpin_comment=desafixou isto %s +issues.pin_comment=afixou este %s +issues.unpin_comment=desafixou este %s issues.lock=Bloquear conversação issues.unlock=Desbloquear conversação issues.lock.unknown_reason=Não pode-se bloquear uma issue com um motivo desconhecido. @@ -2546,7 +2549,7 @@ issues.role.collaborator_helper = Este usuário foi convidado para colaborar nes pulls.cmd_instruction_checkout_title = Checkout settings.wiki_globally_editable = Permitir que qualquer pessoa possa editar a wiki settings.transfer_abort_success = A transferência de repositório para %s foi cancelada. -settings.enter_repo_name = Insira o nome do repositório para confirmar: +settings.enter_repo_name = Digite os nomes do dono e do repositório exatamente neste formato: issues.blocked_by_user = Você não pode criar uma questão neste repositório porque você foi bloqueado pelo dono do repositório. settings.new_owner_blocked_doer = Você foi bloqueado pelo novo dono do repositório. settings.wiki_rename_branch_main_notices_1 = Esta ação NÃO PODERÁ ser desfeita. @@ -2568,6 +2571,18 @@ admin.failed_to_replace_flags = Falha ao substituir os sinalizadores do reposit editor.invalid_commit_mail = E-mail inválido para criar um commit. issues.role.contributor_helper = Este usuário fez commits para o repositório anteriormente. issues.choose.invalid_config = A configuração de issue contém erros: +pulls.made_using_agit = AGit +contributors.contribution_type.filter_label = Tipo de contribuição: +contributors.contribution_type.commits = Commits +settings.webhook.test_delivery_desc_disabled = Ative este webhook para testá-lo com um evento simulado. +activity.navbar.contributors = Contribuidores +issues.label_archive_tooltip = Rótulos arquivados não serão exibidos nas sugestões de pesquisa de rótulos por padrão. +activity.navbar.pulse = Recente +settings.units.overview = Geral +settings.units.add_more = Adicionar mais... +pulls.commit_ref_at = `referenciou este pedido de mesclagem no commit %[2]s` +pulls.cmd_instruction_merge_title = Mesclar +settings.units.units = Funcionalidades [graphs] @@ -3206,6 +3221,8 @@ users.reserved = Reservado emails.change_email_text = Tem certeza de que deseja atualizar este endereço de e-mail? self_check = Autodiagnóstico auths.tip.gitea = Registre um novo aplicativo OAuth2. A documentação pode ser encontrada em https://forgejo.org/docs/latest/user/oauth2-provider/ +dashboard.sync_tag.started = Sincronização de etiquetas iniciada +self_check.no_problem_found = Por enquanto não há algum problema. [action] @@ -3444,6 +3461,7 @@ owner.settings.chef.title=Registro Chef owner.settings.chef.keypair=Gerar par de chaves rpm.repository.architectures = Arquiteturas rpm.repository = Informações do repositório +rpm.repository.multiple_groups = Este pacote está disponível em vários grupos. [secrets] secrets=Segredos @@ -3527,6 +3545,16 @@ runs.scheduled = Programadas variables.creation = Adicionar variável variables.deletion = Remover variável variables.management = Gerenciamento de variáveis +runs.actors_no_select = Todos os atores +variables.none = Ainda não há variáveis. +variables.update.failed = Falha ao editar a variável. +runs.actor = Ator +runs.status_no_select = Todos os estados +runs.empty_commit_message = (mensagem de commit vazia) +variables = Variáveis +variables.id_not_exist = A variável com o ID %d não existe. +variables.deletion.failed = Falha ao remover a variável. +variables.creation.failed = Falha ao adicionar a variável. [projects] @@ -3543,3 +3571,10 @@ normal_file = Arquivo normal submodule = Submódulo executable_file = Arquivo executável + + +[graphs] +component_loading = Carregando %s... +component_loading_failed = Não foi possível carregar o(a) %s +component_loading_info = Pode demorar um pouco… +contributors.what = contribuições \ No newline at end of file diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index 1d2335dbab..649ddfd819 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -3603,6 +3603,8 @@ variables.creation.failed=Falha ao adicionar a variável. variables.creation.success=A variável "%s" foi adicionada. variables.update.failed=Falha ao editar a variável. variables.update.success=A variável foi editada. +runs.no_workflows.documentation = Para mais informação sobre o Forgejo Action, veja a documentação. +runs.no_workflows.quick_start = Não sabe como começar com o Forgejo Action? Veja o guia de iniciação rápida. [projects] type-1.display_name=Planeamento individual diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index a7234f958c..31b7b5d605 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -75,7 +75,7 @@ collaborative=Совместные forks=Форки activities=Активности -pull_requests=Запросы на слияние +pull_requests=Слияния issues=Задачи milestones=Этапы @@ -182,6 +182,7 @@ missing_csrf=Некорректный запрос: отсутствует то invalid_csrf=Некорректный запрос: неверный токен CSRF not_found=Цель не найдена. network_error=Ошибка сети +server_internal = Внутренняя ошибка сервера [startpage] app_desc=Удобный, самостоятельный хостинг Git-репозиториев @@ -278,8 +279,8 @@ admin_password=Пароль confirm_password=Подтверждение пароля admin_email=Адрес эл. почты install_btn_confirm=Установить Forgejo -test_git_failed=Не удалось проверить 'git' команду: %v -sqlite3_not_available=Эта версия Forgejo не поддерживает SQLite3. Пожалуйста, загрузите официальную бинарную версию из %s (не 'go build' версия). +test_git_failed=Не удалось проверить команду «git»: %v +sqlite3_not_available=Эта версия Forgejo не поддерживает SQLite3. Пожалуйста, загрузите официальную бинарную сборку из %s (не сборку «gobuild»). invalid_db_setting=Некорректные настройки базы данных: %v invalid_db_table=Таблица «%s» базы данных некорректна: %v invalid_repo_path=Недопустимый путь к корню репозитория: %v @@ -297,7 +298,7 @@ default_allow_create_organization_popup=Разрешить новым учётн default_enable_timetracking=Включить отслеживание времени по умолчанию default_enable_timetracking_popup=Включить отслеживание времени для новых репозиториев по умолчанию. no_reply_address=Скрытый почтовый домен -no_reply_address_helper=Доменное имя для пользователей со скрытым адресом эл. почты. Например, пользователь 'joe' будет зарегистрирован в Git как 'joe@noreply.example.org', если скрытый домен эл. почты задан как 'noreply.example.org'. +no_reply_address_helper=Доменное имя для пользователей со скрытым адресом эл. почты. Например, пользователь «joe» будет зарегистрирован в Git как «joe@noreply.example.org», если скрытый домен эл. почты задан как «noreply.example.org». password_algorithm=Алгоритм хеширования пароля invalid_password_algorithm=Некорректный алгоритм хеширования пароля password_algorithm_helper=Задайте алгоритм хеширования паролей. Алгоритмы имеют различные требования и стойкость. Алгоритм argon2 довольно безопасен, но он использует много памяти и может не подходить для слабых систем. @@ -491,7 +492,7 @@ repo.transfer.to_you=вам repo.transfer.body=Чтобы принять или отклонить передачу, перейдите по ссылке %s или просто проигнорируйте этот запрос. repo.collaborator.added.subject=%s добавил(а) вас в %s -repo.collaborator.added.text=Вы были добавлены в качестве соавтора репозитория: +repo.collaborator.added.text=Вы были добавлены в качестве соучастника репозитория: team_invite.subject=%[1]s приглашает вас присоединиться к организации %[2]s team_invite.text_1=%[1]s приглашает вас присоединиться к команде %[2]s в организации %[3]s. @@ -532,7 +533,7 @@ SSPISeparatorReplacement=Разделитель SSPIDefaultLanguage=Язык по умолчанию require_error=` не может быть пустым.` -alpha_dash_error=` должен содержать только буквенно-цифровые символы, тире (' - ') и подчеркивания ('_').` +alpha_dash_error=` должен содержать только буквенно-цифровые символы, тире («-») и подчеркивания («_»).` alpha_dash_dot_error=` должен содержать только буквенно-цифровые символы, тире ('-'), подчеркивания ('_') и точки ('.').` git_ref_name_error=` должно быть правильным ссылочным именем Git.` size_error=` должен быть размер %s.` @@ -579,8 +580,8 @@ enterred_invalid_owner_name=Имя нового владельца некорр enterred_invalid_password=Введённый пароль неверен. user_not_exist=Пользователь не существует. team_not_exist=Команда не существует. -last_org_owner=Вы не можете удалить последнего пользователя из команды 'Владельцы'. Для организации должен быть хотя бы один владелец. -cannot_add_org_to_team=Организацию нельзя добавить в качестве члена команды. +last_org_owner=Вы не можете удалить единственного пользователя из команды «Владельцы». У организации должен быть хотя бы один владелец. +cannot_add_org_to_team=Организацию нельзя добавить в качестве участника команды. duplicate_invite_to_team=Пользователь уже был приглашен в качестве участника команды. organization_leave_success=Вы успешно покинули организацию %s. @@ -592,14 +593,14 @@ unable_verify_ssh_key=Не удаётся верифицировать ключ auth_failed=Ошибка аутентификации: %v still_own_repo=Ваша учётная запись владеет одним или несколькими репозиториями, сначала удалите или передайте их. -still_has_org=Ваша учётная запись является членом одной или нескольких организаций, сначала покиньте их. +still_has_org=Вы состоите в одной или нескольких организациях. Сначала покиньте их. still_own_packages=Ваша учётная запись владеет одним или несколькими пакетами, сначала удалите их. org_still_own_repo=Эта организация всё ещё владеет одним или несколькими репозиториями, сначала удалите или передайте их. org_still_own_packages=Эта организация всё ещё владеет одним или несколькими пакетами, сначала удалите их. target_branch_not_exist=Целевая ветка не существует. admin_cannot_delete_self = Вы не можете удалить свою учётную запись, будучи администратором. Сперва снимите с себя роль администратора. -username_error_no_dots = ` может состоять только из латинских букв ('a-z','A-Z'), цифр ('0-9'), знаков минуса ('-') и нижнего подчёркивания ('_'). Знаки не могут стоять в начале или в конце, а также идти подряд.` +username_error_no_dots = ` может состоять только из латинских букв («a-z», «A-Z»), цифр («0-9»), знаков минуса («-») и нижнего подчёркивания («_»). Знаки не могут стоять в начале или в конце, а также идти подряд.` [user] @@ -633,7 +634,7 @@ block_user.detail = Учтите, что блокировка этого пол follow_blocked_user = Вы не можете подписаться на этого пользователя, т.к. вы его заблокировали, либо он вас. block_user = Заблокировать пользователя block_user.detail_1 = Вы будете отписаны от этого пользователя. -block_user.detail_3 = Вы не сможете добавлять друг друга в качестве соавторов репозиториев. +block_user.detail_3 = Вы не сможете добавлять друг друга в качестве соучастников репозиториев. [settings] profile=Профиль @@ -654,7 +655,7 @@ organization=Организации uid=UID webauthn=Ключи безопасности -public_profile=Открытый профиль +public_profile=Публичный профиль biography_placeholder=Расскажите немного о себе! (Можно использовать Markdown) location_placeholder=Поделитесь своим приблизительным местоположением с другими profile_desc=Как ваш профиль будет отображаться для других пользователей. Ваш основной адрес эл. почты будет использоваться для уведомлений, восстановления пароля и веб-операций с Git. @@ -700,7 +701,7 @@ keep_activity_private_popup=Делает активность видимой т lookup_avatar_by_mail=Найти аватар по адресу эл. почты federated_avatar_lookup=Найти внешний аватар -enable_custom_avatar=Включить собственный аватар +enable_custom_avatar=Использовать собственный аватар choose_new_avatar=Выбрать новый аватар update_avatar=Обновить аватар delete_current_avatar=Удалить текущий аватар @@ -761,8 +762,8 @@ ssh_helper=Нужна помощь? Ознакомьтесь с gpg_helper=Нужна помощь? Взгляните на руководство GitHub по GPG. add_new_key=Добавить ключ SSH add_new_gpg_key=Добавить ключ GPG -key_content_ssh_placeholder=Начинается с 'ssh-ed25519', 'ssh-rsa', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384', 'ecdsa-sha2-nistp521', 'sk-ecdsa-sha2-nistp256@openssh.com', или 'sk-ssh-ed25519@openssh.com' -key_content_gpg_placeholder=Начинается с '-----BEGIN PGP PUBLIC KEY BLOCK-----' +key_content_ssh_placeholder=Начинается с «ssh-ed25519», «ssh-rsa», «ecdsa-sha2-nistp256», «ecdsa-sha2-nistp384», «ecdsa-sha2-nistp521», «sk-ecdsa-sha2-nistp256@openssh.com» или «sk-ssh-ed25519@openssh.com» +key_content_gpg_placeholder=Начинается с «-----BEGIN PGP PUBLIC KEY BLOCK-----» add_new_principal=Добавить принципала ssh_key_been_used=Этот ключ SSH уже был добавлен на сервер. ssh_key_name_used=Ключ SSH с таким именем уже есть в вашей учётной записи. @@ -780,7 +781,7 @@ gpg_token=Токен gpg_token_help=Вы можете сгенерировать подпись с помощью: gpg_token_code=echo "%s" | gpg -a --default-key %s --detach-sig gpg_token_signature=Текстовая подпись GPG -key_signature_gpg_placeholder=Начинается с '-----BEGIN PGP SIGNATURE-----' +key_signature_gpg_placeholder=Начинается с «-----BEGIN PGP SIGNATURE-----» verify_gpg_key_success=Ключ GPG «%s» верифицирован. ssh_key_verified=Проверенный ключ ssh_key_verified_long=Ключ был проверен токеном и может быть использован для проверки коммитов, соответствующих любым активным адресом эл. почты этого пользователя. @@ -790,7 +791,7 @@ ssh_token_required=Вы должны предоставить подпись д ssh_token=Токен ssh_token_help=Вы можете сгенерировать подпись с помощью: ssh_token_signature=Текстовая подпись SSH -key_signature_ssh_placeholder=Начинается с '-----BEGIN SSH SIGNATURE-----' +key_signature_ssh_placeholder=Начинается с «-----BEGIN SSH SIGNATURE-----» verify_ssh_key_success=Ключ SSH «%s» верифицирован. subkeys=Подключи key_id=ИД ключа @@ -939,7 +940,7 @@ visibility.public_tooltip=Видимый для всех visibility.limited=Ограниченный visibility.limited_tooltip=Виден только выполнившим вход пользователям visibility.private=Приватный -visibility.private_tooltip=Виден только членам организаций, к которым вы присоединились +visibility.private_tooltip=Видно только участникам организаций, к которым вы присоединились blocked_users_none = Заблокированных пользователей нет. user_block_success = Пользователь заблокирован. oauth2_application_locked = Forgejo предварительно регистрирует некоторые приложения OAuth2 при запуске, если это включено в конфигурации. Для избежания неожиданного поведения их нельзя удалять или редактировать. Ознакомиться с подробностями можно в документации OAuth2. @@ -964,7 +965,7 @@ template_select=Выбрать шаблон. template_helper=Сделать репозиторий шаблоном template_description=Шаблонные репозитории дают возможность пользователям создавать новые репозитории с той же структурой каталогов, файлами и дополнительными настройками. visibility=Видимость -visibility_description=Только владелец или члены организации, при наличии прав, смогут увидеть это. +visibility_description=Это увидят только владелец организации или участники при наличии прав. visibility_helper=Сделать репозиторий приватным visibility_helper_forced=Администратор сайта настроил параметр видимости новых репозиториев. Репозиторий приватный по умолчанию. visibility_fork_helper=(Изменение этого повлияет на все форки.) @@ -998,9 +999,9 @@ readme_helper=Выберите шаблон README. readme_helper_desc=Это место, где вы можете написать подробное описание вашего проекта. auto_init=Инициализировать репозиторий (Добавляет .gitignore, LICENSE and README) trust_model_helper=Выберите модель доверия для проверки подписи. Возможные варианты: -trust_model_helper_collaborator=Соавтор: доверять подписям соавторов +trust_model_helper_collaborator=Соучастник: доверять подписям соучастников trust_model_helper_committer=Автор коммита: доверять подписям, соответствующим авторам коммитов -trust_model_helper_collaborator_committer=Соавтор+Коммитер: доверять подписям соавторов, которые соответствуют автору коммита +trust_model_helper_collaborator_committer=Соучастник+Коммитер: доверять подписям соучастников, которые соответствуют автору коммита trust_model_helper_default=По умолчанию: используйте модель доверия по умолчанию для этой установки create_repo=Создать репозиторий default_branch=Ветка по умолчанию @@ -1159,7 +1160,7 @@ find_tag=Найти тег branches=ветки tags=теги issues=Задачи -pulls=Запросы на слияние +pulls=Слияния project_board=Проекты packages=Пакеты actions=Действия @@ -1193,8 +1194,8 @@ escape_control_characters=Экранировать unescape_control_characters=Убрать экранирование file_copy_permalink=Копировать постоянную ссылку view_git_blame=Показать git blame -video_not_supported_in_browser=Ваш браузер не поддерживает HTML5 'video' тэг. -audio_not_supported_in_browser=Ваш браузер не поддерживает HTML5 'audio' тэг. +video_not_supported_in_browser=Ваш браузер не поддерживает тэг HTML5 «video». +audio_not_supported_in_browser=Ваш браузер не поддерживает тэг HTML5 «audio». stored_lfs=Хранится Git LFS symbolic_link=Символическая ссылка executable_file=Исполняемый файл @@ -1228,12 +1229,12 @@ editor.delete_this_file=Удалить файл editor.must_have_write_access=Вам необходимо иметь права на запись, чтобы вносить или предлагать изменения этого файла. editor.file_delete_success=Файл «%s» удалён. editor.name_your_file=Назовите свой файл… -editor.filename_help=Чтобы добавить каталог, введите название и нажмите '/'. Чтобы удалить, перейдите к началу поля и нажмите клавишу backspace. +editor.filename_help=Чтобы добавить каталог, введите название и нажмите «/». Чтобы удалить, перейдите к началу поля и нажмите клавишу backspace. editor.or=или editor.cancel_lower=Отменить editor.commit_signed_changes=Зафиксировать подписанные изменения editor.commit_changes=Сохранить правки -editor.add_tmpl=Добавить '' +editor.add_tmpl=Добавить «» editor.add=Добавить %s editor.update=Обновить %s editor.delete=Удалить %s @@ -1396,7 +1397,7 @@ issues.new_label_placeholder=Имя метки issues.new_label_desc_placeholder=Описание issues.create_label=Добавить метку issues.label_templates.title=Загрузить набор предопределённых меток -issues.label_templates.info=Меток пока не существует. Создайте метку или используйте набор меток: +issues.label_templates.info=Меток пока нет. Создайте новую метку или используйте этот набор меток: issues.label_templates.helper=Выберите метку issues.label_templates.use=Использовать набор меток issues.label_templates.fail_to_load_file=Не удалось загрузить файл шаблона меток «%s»: %v @@ -1512,12 +1513,12 @@ issues.author_helper=Этот пользователь является авто issues.role.owner=Владелец issues.role.owner_helper=Этот пользователь является владельцем репозитория. issues.role.member=Участник -issues.role.member_helper=Этот пользователь является членом организации, владеющей этим репозиторием. -issues.role.collaborator=Соавтор +issues.role.member_helper=Этот пользователь является участником организации, владеющей этим репозиторием. +issues.role.collaborator=Соучастник issues.role.collaborator_helper=Этот пользователь был приглашен сотрудничать в репозитории. issues.role.first_time_contributor=Новый участник issues.role.first_time_contributor_helper=Это первое участие пользователя в репозитории. -issues.role.contributor=Участник +issues.role.contributor=Соавтор issues.re_request_review=Повторить запрос на отзыв issues.is_stale=Со времени этого обзора в этот PR были внесены некоторые изменения issues.remove_request_review=Удалить запрос на отзыв @@ -1549,7 +1550,7 @@ issues.label.filter_sort.alphabetically=По алфавиту issues.label.filter_sort.reverse_alphabetically=С конца алфавита issues.label.filter_sort.by_size=Минимальный размер issues.label.filter_sort.reverse_by_size=Максимальный размер -issues.num_participants=%d участников +issues.num_participants=%d участвующих issues.attachment.open_tab=`Нажмите, чтобы увидеть «%s» в новой вкладке` issues.attachment.download=`Нажмите, чтобы скачать «%s»` issues.subscribe=Подписаться @@ -1563,13 +1564,13 @@ issues.unlock=Снять ограничение issues.lock.unknown_reason=Для ограничения обсуждения необходимо указать причину. issues.lock_duplicate=Обсуждение задачи уже ограничено. issues.unlock_error=Невозможно снять несуществующее ограничение обсуждения. -issues.lock_with_reason=заблокировано как %s и ограничено обсуждение для соучастников %s -issues.lock_no_reason=ограничил(а) обсуждение задачи до соавторов %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=- Другие пользователи не могут добавлять новые комментарии к этой задаче. -issues.lock.notice_2=- Вы и другие соавторы с доступом к этому репозиторию могут оставлять комментарии, которые могут видеть другие. +issues.lock.notice_1=- Другие пользователи не смогут добавлять новые комментарии к этой задаче. +issues.lock.notice_2=- Вы и другие соучастники с доступом к этому репозиторию сможете оставлять публичные комментарии. issues.lock.notice_3=- Вы всегда можете снять ограничение с обсуждения этой задачи. issues.unlock.notice_1=- Все снова смогут принять участие в обсуждении данной задачи. issues.unlock.notice_2=- Вы всегда можете снова наложить ограничение на обсуждение этой задачи. @@ -1872,8 +1873,8 @@ ext_wiki.desc=Ссылка на внешнюю вики. wiki=Вики wiki.welcome=Добро пожаловать в вики. -wiki.welcome_desc=Вики позволяет писать и делиться документацией с сотрудниками. -wiki.desc=Пишите и делитесь документацией с соавторами. +wiki.welcome_desc=Вики позволяет писать документацию и делиться ей с соучастниками. +wiki.desc=Пишите и делитесь документацией с соучастниками. wiki.create_first_page=Создать первую страницу wiki.page=Страница wiki.filter_page=Фильтр страницы @@ -1894,7 +1895,7 @@ wiki.page_already_exists=Страница вики с таким именем у wiki.reserved_page=Имя страницы вики «%s» зарезервировано. 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=Период: @@ -1978,7 +1979,7 @@ search.code_search_unavailable=В настоящее время поиск по settings=Настройки settings.desc=В настройках вы можете менять различные параметры этого репозитория settings.options=Репозиторий -settings.collaboration=Соавторы +settings.collaboration=Соучастники settings.collaboration.admin=Администратор settings.collaboration.write=Запись settings.collaboration.read=Просмотр @@ -2040,7 +2041,7 @@ settings.tracker_issue_style.regexp_pattern=Шаблон регулярного settings.tracker_issue_style.regexp_pattern_desc=Вместо {index} будет использоваться первая захваченная группа. 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=Включить автоопределение ручного слияния (Примечание: в некоторых особых случаях могут возникнуть ошибки) @@ -2092,15 +2093,15 @@ settings.signing_settings=Настройки подписи верификаци settings.trust_model=Модель доверия подписи settings.trust_model.default=Модель доверия по умолчанию settings.trust_model.default.desc=Использовать стандартную модель доверия репозитория для этой установки. -settings.trust_model.collaborator=Соавтор -settings.trust_model.collaborator.long=Соавтор: доверять подписям соавторов -settings.trust_model.collaborator.desc=Действительные подписи соавторов этого репозитория будут помечены как «доверенные» (независимо от того, соответствуют ли они автору коммита). В остальных случаях действительные подписи будут помечены как «недоверенные», если подпись соответствует автору коммита, и «не совпадающие», если нет. +settings.trust_model.collaborator=Соучастник +settings.trust_model.collaborator.long=Соучастник: доверять подписям соучастников +settings.trust_model.collaborator.desc=Действительные подписи соучастников этого репозитория будут помечены как «доверенные» (независимо от того, соответствуют ли они автору коммита). В остальных случаях действительные подписи будут помечены как «недоверенные», если подпись соответствует автору коммита, и «не совпадающие», если нет. settings.trust_model.committer=Коммитер settings.trust_model.committer.long=Коммитер: доверять подписям, соответствующим коммитерам (соответствует GitHub и требует коммиты, подписанные Forgejo, иметь Forgejo в качестве коммитера) settings.trust_model.committer.desc=Действительные подписи будут помечены «доверенными», только если они соответствуют автору коммита, в противном случае они будут помечены «не совпадающими». Это заставит Forgejo быть автором подписанных коммитов, а фактический автор будет обозначен в трейлерах Co-Authored-By: и Co-Committed-By: коммита. Ключ Forgejo по умолчанию должен соответствовать пользователю в базе данных. -settings.trust_model.collaboratorcommitter=Соавтор+Коммитер -settings.trust_model.collaboratorcommitter.long=Соавтор+Коммитер: доверять подписям соавторов, которые соответствуют автору коммита -settings.trust_model.collaboratorcommitter.desc=Действительные подписи соавторов этого репозитория будут помечены «доверенными», если они соответствуют автору коммита. Действительные подписи будут помечены как «недоверенные», если подпись соответствует автору коммита, и «не совпадающие» впротивном случае. Это заставит Forgejo быть отмеченным в качестве автора подписанного коммита, а фактический автор будет указан в трейлерах Co-Authored-By: и Co-Committed-By: коммита. Ключ Forgejo по умолчанию должен соответствовать пользователю в базе данных. +settings.trust_model.collaboratorcommitter=Соучастник+Коммитер +settings.trust_model.collaboratorcommitter.long=Соучастник+Коммитер: доверять подписям соучастников, которые соответствуют автору коммита +settings.trust_model.collaboratorcommitter.desc=Действительные подписи соучастников этого репозитория будут помечены «доверенными», если они соответствуют автору коммита. Действительные подписи будут помечены как «недоверенные», если подпись соответствует автору коммита, и «не совпадающие» впротивном случае. Это заставит Forgejo быть отмеченным в качестве автора подписанного коммита, а фактический автор будет указан в трейлерах Co-Authored-By: и Co-Committed-By: коммита. Ключ Forgejo по умолчанию должен соответствовать пользователю в базе данных. settings.wiki_delete=Стереть данные вики settings.wiki_delete_desc=Будьте внимательны! Как только вы удалите вики — пути назад не будет. settings.wiki_delete_notices_1=- Это безвозвратно удалит и отключит вики для %s. @@ -2115,17 +2116,17 @@ settings.deletion_success=Репозиторий удалён. settings.update_settings_success=Настройки репозитория обновлены. settings.update_settings_no_unit=Должно быть разрешено хоть какое-то взаимодействие с репозиторием. settings.confirm_delete=Удалить репозиторий -settings.add_collaborator=Добавить соавтора -settings.add_collaborator_success=Соавтор добавлен. -settings.add_collaborator_inactive_user=Невозможно добавить неактивного пользователя как соавтора. -settings.add_collaborator_owner=Невозможно добавить владельца в качестве соавтора. -settings.add_collaborator_duplicate=Соавтор уже добавлен в этот репозиторий. +settings.add_collaborator=Добавить соучастника +settings.add_collaborator_success=Соучастник добавлен. +settings.add_collaborator_inactive_user=Невозможно добавить неактивного пользователя в качестве соучастника. +settings.add_collaborator_owner=Невозможно добавить владельца в качестве соучастника. +settings.add_collaborator_duplicate=Соучастник уже добавлен в этот репозиторий. settings.delete_collaborator=Удалить -settings.collaborator_deletion=Удалить соавтора +settings.collaborator_deletion=Удалить соучастника settings.collaborator_deletion_desc=Этот пользователь больше не будет иметь доступа для совместной работы в этом репозитории после удаления. Вы хотите продолжить? -settings.remove_collaborator_success=Соавтор удалён. +settings.remove_collaborator_success=Соучастник удалён. settings.search_user_placeholder=Поиск пользователя… -settings.org_not_allowed_to_be_collaborator=Организации не могут быть добавлены как соавторы. +settings.org_not_allowed_to_be_collaborator=Организации не могут быть добавлены как соучастники. settings.change_team_access_not_allowed=Доступ к репозиторию команде ограничен владельцем организации settings.team_not_in_organization=Команда не в той же организации, что и репозиторий settings.teams=Команды @@ -2271,7 +2272,7 @@ settings.protected_branch.delete_rule=Удалить правило settings.protected_branch_can_push=Разрешить отправку? settings.protected_branch_can_push_yes=Вы можете выполнять отправку settings.protected_branch_can_push_no=Вы не можете выполнять отправку -settings.branch_protection=Защита ветки %s +settings.branch_protection=Правила защиты ветки «%s» settings.protect_this_branch=Защитить эту ветку settings.protect_this_branch_desc=Предотвращает удаление, ограничивает Push и слияние Git в ветку. settings.protect_disable_push=Отключить отправку @@ -2336,7 +2337,7 @@ settings.choose_branch=Выберите ветку… settings.no_protected_branch=Нет защищённых веток. settings.edit_protected_branch=Редактировать settings.protected_branch_required_rule_name=Необходимо имя для правила -settings.protected_branch_duplicate_rule_name=Повторяющееся имя правила +settings.protected_branch_duplicate_rule_name=Для этого набора веток уже есть правило settings.protected_branch_required_approvals_min=Число необходимых одобрений не может быть отрицательным. settings.tags=Теги settings.tags.protection=Защита тегов @@ -2559,7 +2560,7 @@ new_repo_helper = Репозиторий содержит все файлы пр mirror_address_url_invalid = Эта ссылка недействительна. Необходимо правильно указать все части адреса. issues.comment.blocked_by_user = Вы не можете комментировать под этой задачей, т.к. вы заблокированы владельцем репозитория или автором задачи. pulls.blocked_by_user = Невозможно создать запрос на слияние в этом репозитории, т.к. вы заблокированы его владельцем. -settings.add_collaborator_blocked_our = Невозможно добавить соавтора, т.к. он заблокирован в этом репозитории. +settings.add_collaborator_blocked_our = Невозможно добавить соучастника, т.к. он заблокирован в этом репозитории. admin.enabled_flags = Включенные флаги репозитория: admin.failed_to_replace_flags = Не удалось заменить флаги репозитория admin.flags_replaced = Флаги репозитория заменены @@ -2572,7 +2573,7 @@ mirror_sync = синхронизирован blame.ignore_revs = Правки в .git-blame-ignore-revs проигнорированы. Нажмите здесь, чтобы обойти этот файл и просмотреть авторов полноценно. issues.blocked_by_user = Невозможно создать задачу в этом репозитории, т.к. вы заблокированы его владельцем. settings.new_owner_blocked_doer = Вы заблокированы новым владельцем. -settings.add_collaborator_blocked_them = Невозможно добавить соавтора, т.к. им заблокирован владелец репозитория. +settings.add_collaborator_blocked_them = Невозможно добавить соучастника, т.к. им заблокирован владелец репозитория. pulls.blocked_by_changed_protected_files_1 = Этот запрос на слияние заблокирован, т.к. им изменяется защищённый файл: object_format_helper = Формат объектов в репозитории. Невозможно изменить в дальнейшем. SHA1 даёт наибольшую совместимость. pulls.blocked_by_outdated_branch = Этот запрос на слияние заблокирован, т.к. он устарел. @@ -2604,7 +2605,7 @@ settings.unarchive.error = При разархивации репозитори settings.archive.mirrors_unavailable = Зеркалирование недоступно для архивированных репозиториев. issues.role.contributor_helper = В репозитории присутствуют коммиты за авторством этого пользователя. settings.wiki_rename_branch_main = Нормализовать название ветки вики -settings.wiki_rename_branch_main_notices_2 = Внутренняя ветка вики репозитория будет переименована в "%s". Несохранённые изменения потребуют обновления. +settings.wiki_rename_branch_main_notices_2 = Внутренняя ветка вики репозитория %s будет переименована. Несохранённые изменения потребуют обновления. settings.wiki_branch_rename_failure = Не удалось нормализовать название ветки вики репозитория. settings.confirm_wiki_branch_rename = Переименовать ветку вики settings.wiki_rename_branch_main_notices_1 = Эта операция НЕОБРАТИМА. @@ -2613,7 +2614,7 @@ settings.wiki_branch_rename_success = Название ветки вики ре ambiguous_runes_description = `Этот файл содержит символы Юникода, которые легко спутать с похожими. Если так и должно быть, можете спокойно игнорировать это предупреждение. Отобразить символы можно кнопкой Экранирования.` editor.invalid_commit_mail = Неправильная почта для создания коммита. pulls.has_merged = Слияние не удалось: запрос уже был слит, изменение целевой ветки или повторное слияние невозможно. -settings.enter_repo_name = Для подтверждения введите название репозитория: +settings.enter_repo_name = Введите имя владельца и название репозитория как указано: signing.wont_sign.error = Не удалось проверить возможность подписать коммит. signing.wont_sign.nokey = Нет ключей для подписи этого коммита. settings.wiki_globally_editable = Разрешить редактирование Вики всем пользователям @@ -2634,6 +2635,10 @@ contributors.contribution_type.filter_label = Тип участия: pulls.commit_ref_at = `сослался(ась) на этот запрос слияния из комммита %[2]s` settings.thread_id = ИД обсуждения pulls.made_using_agit = AGit +activity.navbar.contributors = Соавторы +activity.navbar.code_frequency = Частота кода +activity.navbar.recent_commits = Недавние коммиты +settings.confirmation_string = Подтверждение [graphs] @@ -2722,14 +2727,14 @@ teams.read_access_helper=Участники могут просматриват teams.write_access=Запись teams.write_access_helper=Участники могут читать и выполнять push в командные репозитории. teams.admin_access=Доступ администратора -teams.admin_access_helper=Участники могут выполнять pull, push в командные репозитории и добавлять соавторов в команду. +teams.admin_access_helper=Участники могут выполнять pull, push в командные репозитории и добавлять соучастников в команду. teams.no_desc=Эта группа не имеет описания teams.settings=Настройки teams.owners_permission_desc=Владельцы имеют полный доступ ко всем репозиториям и имеют права администратора организации. -teams.members=Члены группы разработки +teams.members=Участники команды teams.update_settings=Обновить настройки teams.delete_team=Удалить команду -teams.add_team_member=Добавление члена группы разработки +teams.add_team_member=Добавить участника teams.invite_team_member=Пригласить в %s teams.invite_team_member.list=Приглашения в ожидании teams.delete_team_title=Удалить команду @@ -2737,8 +2742,8 @@ teams.delete_team_desc=Удаление команды отменяет дост teams.delete_team_success=Команда удалена. teams.read_permission_desc=Эта команда предоставляет доступ на Чтение: члены могут просматривать и клонировать репозитории команды. teams.write_permission_desc=Эта команда предоставляет доступ на Запись: члены могут получать и выполнять push команды в репозитории. -teams.admin_permission_desc=Эта команда даёт административный доступ: участники могут читать, отправлять изменения и добавлять соавторов к её репозиториям. -teams.create_repo_permission_desc=Кроме того, эта команда предоставляет право Создание репозитория: члены команды могут создавать новые репозитории в организации. +teams.admin_permission_desc=Эта команда даёт административный доступ: участники могут читать, отправлять изменения и добавлять соучастников к её репозиториям. +teams.create_repo_permission_desc=Кроме того, эта команда предоставляет право Создание репозитория: участники команды могут создавать новые репозитории в организации. teams.repositories=Репозитории группы разработки teams.search_repo_placeholder=Поиск репозитория… teams.remove_all_repos_title=Удалить все репозитории команды @@ -2755,7 +2760,7 @@ teams.all_repositories=Все репозитории teams.all_repositories_helper=Команда имеет доступ ко всем репозиториям. Выбрав его, добавит все существующие репозитории в команду. teams.all_repositories_read_permission_desc=Эта команда предоставляет прочтено доступ к всем репозиториям: участники могут просматривать и клонировать репозитории. teams.all_repositories_write_permission_desc=Эта команда предоставляет Написать доступ к всем репозиториям: участники могут читать и выполнять push в репозитории. -teams.all_repositories_admin_permission_desc=Эта команда предоставляет администратору доступ к всем репозиториям: участники могут читать, отправлять сообщения и добавлять соавторов в репозитории. +teams.all_repositories_admin_permission_desc=Эта команда предоставляет администратору доступ к всем репозиториям: участники могут читать, отправлять сообщения и добавлять соучастников в репозитории. teams.invite.title=Вас пригласили присоединиться к команде %s организации %s. teams.invite.by=Приглашен(а) %s teams.invite.description=Нажмите на кнопку ниже, чтобы присоединиться к команде. @@ -2895,7 +2900,7 @@ users.update_profile=Обновить профиль пользователя users.delete_account=Удалить эту учётную запись users.cannot_delete_self=Вы не можете удалить свою учётную запись users.still_own_repo=Этот пользователь всё ещё является владельцем одного или более репозиториев. Сначала удалите или передайте эти репозитории. -users.still_has_org=Этот пользователь всё ещё является членом одной или более организаций. Сначала удалите пользователя из всех организаций. +users.still_has_org=Этот пользователь состоит в одной или нескольких организациях. Сначала удалите пользователя из всех организаций. users.purge=Удалить пользователя users.purge_help=Принудительное удаление пользователя и любых репозиториев, организаций и пакетов, принадлежащих пользователю. Все комментарии и задачи этого пользователя тоже будут удалены. users.still_own_packages=Этот пользователь всё ещё владеет одним или несколькими пакетами, сначала удалите их. @@ -3148,7 +3153,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=No-reply адрес config.default_visibility_organization=Видимость по умолчанию для новых организаций config.default_enable_dependencies=Включение зависимостей для задач по умолчанию @@ -3636,6 +3641,9 @@ variables.creation.failed=Не удалось добавить переменн variables.creation.success=Переменная «%s» добавлена. variables.update.failed=Не удалось изменить переменную. variables.update.success=Переменная изменена. +variables.id_not_exist = Переменная с идентификатором %d не существует. +runs.no_workflows.quick_start = Не знаете, как начать использовать Действия Forgejo? Читайте руководство по быстрому старту. +runs.no_workflows.documentation = Чтобы узнать больше о Действиях Forgejo, читайте документацию. [projects] type-1.display_name=Индивидуальный проект @@ -3656,6 +3664,6 @@ submodule=Подмодуль [graphs] component_loading_failed = Не удалось загрузить %s component_failed_to_load = Случилась непредвиденная ошибка. -contributors.what = участие +contributors.what = соучастие component_loading = Загрузка %s... component_loading_info = Это займёт некоторое время… diff --git a/options/locale/locale_sl.ini b/options/locale/locale_sl.ini index e7df961707..a64954c2cb 100644 --- a/options/locale/locale_sl.ini +++ b/options/locale/locale_sl.ini @@ -55,7 +55,7 @@ copy_url = Kopiraj URL admin_panel = Administracija spletnega mesta copy_error = Kopiranje ni uspelo new_mirror = Novo ogledalo -re_type = Potrditev gesla +re_type = Potrdite geslo webauthn_unsupported_browser = Vaš brskalnik trenutno ne podpira WebAuthna. copy = Kopiraj enabled = Omogočeno @@ -85,7 +85,7 @@ new_repo = Nov repozitorij webauthn_error_unable_to_process = Strežnik ni mogel obdelati vaše zahteve. register = Registracija mirror = Zrcalo -access_token = Žeton za dostop +access_token = Token za dostop download_logs = Prenos dnevnikov webauthn_insert_key = Vstavite varnostni ključ password = Geslo @@ -106,7 +106,7 @@ show_full_screen = Prikaži celoten zaslon webauthn_error_insecure = WebAuthn podpira samo varne povezave. Za testiranje prek protokola HTTP lahko uporabite izvor "localhost" ali "127.0.0.1" username = Usmerjevalno ime tracked_time_summary = Povzetek spremljanega časa na podlagi filtrov seznama zadev -email = Elektronski naslov +email = E-poštni naslov captcha = CAPTCHA sources = Viri issues = Vprašanja @@ -118,6 +118,7 @@ signed_in_as = Prijavil se je kot remove = Odstrani remove_all = Odstrani vse remove_label_str = Odstranite element "%s" +confirm_delete_artifact = Ste prepričani, da želite izbrisati artefakt "%s"? [install] reinstall_confirm_check_3 = Potrjujete, da ste popolnoma prepričani, da se ta program Forgejo izvaja s pravilno lokacijo app.ini, in da ste prepričani, da ga morate znova namestiti. Potrjujete, da se zavedate zgoraj navedenih tveganj. @@ -173,7 +174,7 @@ enable_captcha_popup = Zahtevajte CAPTCHA za samoprijavo uporabnika. admin_setting_desc = Ustvarjanje skrbniškega računa ni obvezno. Prvi registrirani uporabnik bo samodejno postal skrbnik. allow_dots_in_usernames = Uporabnikom dovolite uporabo pik v uporabniških imenih. Ne vpliva na obstoječe račune. default_keep_email_private = Privzeto skrivanje e-poštnih naslovov -no_reply_address_helper = Ime domene za uporabnike s skritim e-poštnim naslovom. Na primer, uporabniško ime 'joe' bo prijavljeno v Git kot 'joe@noreply.example.org', če je skrita e-poštna domena nastavljena na 'noreply.example.org'. +no_reply_address_helper = Ime domene za uporabnike s skritim e-poštnim naslovom. Na primer, uporabniško ime "joe" bo prijavljeno v Git kot "joe@noreply.example.org", če je skrita e-poštna domena nastavljena na "noreply.example.org". password_algorithm = Algoritem šifriranja gesel no_reply_address = Skrita e-poštna domena default_allow_create_organization_popup = Novim uporabniškim računom privzeto dovolite ustvarjanje organizacij. @@ -204,13 +205,13 @@ admin_password = Geslo confirm_password = Potrditev gesla install_btn_confirm = Namestitev programa Forgejo admin_email = e-poštni naslov -test_git_failed = Ukaza 'git' ni bilo mogoče preizkusiti: %v -sqlite3_not_available = Ta različica programa Forgejo ne podpira SQLite3. Prosimo, prenesite uradno binarno različico s %s (ne različice 'gobuild'). +test_git_failed = Ukaza "git" ni bilo mogoče preizkusiti: %v +sqlite3_not_available = Ta različica programa Forgejo ne podpira SQLite3. Prosimo, prenesite uradno binarno različico s %s (ne različice "gobuild"). invalid_db_setting = Nastavitve podatkovne zbirke so neveljavne: %v invalid_db_table = Tabela zbirke podatkov "%s" je neveljavna: %v invalid_repo_path = Korenska pot skladišča je neveljavna: %v invalid_app_data_path = Podatkovna pot aplikacije je neveljavna: %v -run_user_not_match = Uporabniško ime 'zagnati kot' ni trenutno uporabniško ime: %s -> %s +run_user_not_match = Uporabniško ime "zagnati kot" ni trenutno uporabniško ime: %s -> %s internal_token_failed = Ni uspelo ustvariti notranjega žetona: %v secret_key_failed = Ni uspelo ustvariti tajnega ključa: %v save_config_failed = Ni uspelo shraniti konfiguracije: %v @@ -370,6 +371,13 @@ last_admin = Zadnjega upravitelja ne morete odstraniti. Obstajati mora vsaj en s authorization_failed_desc = Avtorizacija ni uspela, ker smo zaznali neveljavno zahtevo. Obrnite se na vzdrževalca aplikacije, ki ste jo poskušali avtorizirati. sspi_auth_failed = Avtentikacija SSPI ni uspela password_pwned_err = Ni bilo mogoče dokončati zahteve za HaveIBeenPwned +remember_me.compromised = Prijavni žeton ni več veljaven, kar lahko pomeni, da je račun ogrožen. Preverite, ali v vašem računu potekajo nenavadne dejavnosti. +disable_forgot_password_mail = Obnovitev računa je onemogočena, ker e-pošta ni nastavljena. Obrnite se na skrbnika spletnega mesta. +disable_forgot_password_mail_admin = Obnovitev računa je na voljo le, če je nastavljena e-pošta. Če želite omogočiti obnovitev računa, nastavite e-pošto. +email_domain_blacklisted = S svojim e-poštnim naslovom se ne morete registrirati. +authorize_application = Odobritev vloge +authorize_application_description = Če mu dovolite dostop, bo lahko dostopal do vseh informacij o vašem računu, vključno z zasebnimi skladišči in organizacijami, in pisal vanje. +remember_me = Ne pozabite na to napravo [home] show_both_archived_unarchived = Prikazovanje arhiviranih in nearhiviranih @@ -380,7 +388,7 @@ 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štni naslov +uname_holder = Uporabniško ime ali E-poštovni naslov password_holder = Geslo my_repos = Repozitoriji show_more_repos = Prikaži več skladišč… @@ -430,4 +438,23 @@ repo.collaborator.added.subject = %s vas je dodal v %s team_invite.subject = %[1]s vas je povabil, da se pridružite organizaciji %[2]s issue.action.new = @%[1]s ustvaril #%[2]d. team_invite.text_1 = %[1]s vas je povabil, da se pridružite ekipi %[2]s v organizaciji %[3]s. -team_invite.text_3 = Opomba: To vabilo je bilo namenjeno %[1]. Če tega vabila niste pričakovali, ga lahko ignorirate. \ No newline at end of file +team_invite.text_3 = Opomba: To vabilo je bilo namenjeno %[1]. Če tega vabila niste pričakovali, ga lahko ignorirate. +reply = ali neposredno odgovorite na to e-poštno sporočilo +activate_email = Preverite svoj e-poštni naslov +activate_email.title = %s, preverite svoj e-poštni naslov +activate_email.text = Kliknite naslednjo povezavo, da preverite svoj e-poštni naslov v %s: +register_notify.text_1 = to je vaše e-poštno sporočilo s potrditvijo registracije za %s! +issue_assigned.pull = @%[1]s vam je dodelil zahtevo za poteg %[2]s v skladišču %[3]s. +issue_assigned.issue = @%[1]s vam je dodelil izdajo %[2]s v skladišču %[3]s. +issue.action.force_push = %[1]s sila-potisnila%[2]s < /b > iz %[3]s v %[4]s. + +[modal] +confirm = Potrdite +no = Ne +cancel = Prekliči +modify = Posodobitev + +[form] +UserName = Uporabniško ime +Password = Geslo +Retype = Potrditev gesla \ No newline at end of file diff --git a/options/locale/locale_tr-TR.ini b/options/locale/locale_tr-TR.ini index 27accb4e03..0702262f9c 100644 --- a/options/locale/locale_tr-TR.ini +++ b/options/locale/locale_tr-TR.ini @@ -3567,6 +3567,9 @@ variables.creation.failed=Değişken eklenemedi. variables.creation.success=`"%s" değişkeni eklendi.` variables.update.failed=Değişken düzenlenemedi. variables.update.success=Değişken düzenlendi. +runs.no_workflows.documentation = Gitea İşlem'i hakkında daha fazla bilgi için, belgeye bakabilirsiniz. +variables.id_not_exist = %d kimlikli değişken mevcut değil. +runs.no_workflows.quick_start = Gitea İşlem'i nasıl başlatacağınızı bilmiyor musunuz? Hızlı başlangıç rehberine bakabilirsiniz. [projects] type-1.display_name=Kişisel Proje From 07b99560cc3aaa0b06cda1ed2d0179e88e61b7b4 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Wed, 28 Feb 2024 15:57:48 +0100 Subject: [PATCH 255/271] [SEMVER] X.Y.Z+A.B.C replaced with X.Y.Z+gitea-A.B.C Gitea versions are * A.B.C * A.B.C+rc-0 * A.B.C+dev-5-g4fb9056 If Forgejo versions are: * X.Y.Z+A.B.C * X.Y.Z-rc0+A.B.C * X.Y.Z-dev-1232-g4fb905687+A.B.C It is non trivial for a client trying to sort out if the server responding to `/api/v1/version` is Forgejo or Gitea. The Forgejo version changes to be: * X.Y.Z+gitea-A.B.C * X.Y.Z-rc0+gitea-A.B.C * X.Y.Z-dev-1232-g4fb905687+gitea-A.B.C and a client can now: * Split the version with + * If the second part (the metadata) exists and contains "gitea", the answer server is Forgejo * Otherwise it is Gitea --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 4f241dde0a..9d0a92bdfb 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,7 @@ endif STORED_VERSION_FILE := VERSION HUGO_VERSION ?= 0.111.3 -GITEA_COMPATIBILITY ?= 1.22.0 +GITEA_COMPATIBILITY ?= gitea-1.22.0 STORED_VERSION=$(shell cat $(STORED_VERSION_FILE) 2>/dev/null) ifneq ($(STORED_VERSION),) From 6e6ebe48570bb0e9cd73bb266a8101028bdc1309 Mon Sep 17 00:00:00 2001 From: hazycora Date: Wed, 28 Feb 2024 21:17:53 -0600 Subject: [PATCH 256/271] Make settings tab not active when on repository units tab --- templates/repo/header.tmpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/repo/header.tmpl b/templates/repo/header.tmpl index ed377e9d18..286eedff18 100644 --- a/templates/repo/header.tmpl +++ b/templates/repo/header.tmpl @@ -177,7 +177,7 @@ {{svg "octicon-diff-added"}} {{ctx.Locale.Tr "repo.settings.units.add_more"}} {{end}} - + {{svg "octicon-tools"}} {{ctx.Locale.Tr "repo.settings"}} {{end}} From 42ae3d3d81996536a8b2a7de61efb2a1bf791b4a Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 29 Feb 2024 12:03:40 +0100 Subject: [PATCH 257/271] [BUG] Fix header name in swagger response - This was incorrectly copied from the `swaggerCommitList` struct, which on the other hand does set `X-Total`, but the API handler that uses this response only sets `X-Total-Count`. --- routers/api/v1/swagger/repo.go | 2 +- templates/swagger/v1_json.tmpl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/routers/api/v1/swagger/repo.go b/routers/api/v1/swagger/repo.go index 263e335873..c7fa98a697 100644 --- a/routers/api/v1/swagger/repo.go +++ b/routers/api/v1/swagger/repo.go @@ -263,7 +263,7 @@ type swaggerChangedFileList struct { PerPage int `json:"X-PerPage"` // Total commit count - Total int `json:"X-Total"` + Total int `json:"X-Total-Count"` // Total number of pages PageCount int `json:"X-PageCount"` diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl index 1bceee0802..ef292f2d65 100644 --- a/templates/swagger/v1_json.tmpl +++ b/templates/swagger/v1_json.tmpl @@ -24170,7 +24170,7 @@ "format": "int64", "description": "Commits per page" }, - "X-Total": { + "X-Total-Count": { "type": "integer", "format": "int64", "description": "Total commit count" From 331fa449561ae8e1280c9ca939962d48dc51a427 Mon Sep 17 00:00:00 2001 From: Gusted Date: Wed, 28 Feb 2024 19:23:14 +0100 Subject: [PATCH 258/271] [BUG] Ensure `HasIssueContentHistory` takes into account `comment_id` - The content history table contains the content history of issues and comments. For issues they are saved with an comment id of zero. - If you want to check if the issue has an content history, it should take into account that SQL has `comment_id = 0`, as it otherwise could return incorrect results when for example the issue already has an comment that has an content history. - Fix the code of `HasIssueContentHistory` to take this into account, it relied on XORM to generate the SQL from the non-default values of the struct, this wouldn't generate the `comment_id = 0` SQL as `0` is the default value of an integer. - Remove an unncessary log (it's not the responsibility of `models` code to do logging). - Adds unit test. - Resolves #2513 --- models/issues/content_history.go | 10 +--------- models/issues/content_history_test.go | 13 +++++++++++++ 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/models/issues/content_history.go b/models/issues/content_history.go index 8b00adda99..cd3e217b21 100644 --- a/models/issues/content_history.go +++ b/models/issues/content_history.go @@ -172,15 +172,7 @@ func FetchIssueContentHistoryList(dbCtx context.Context, issueID, commentID int6 // HasIssueContentHistory check if a ContentHistory entry exists func HasIssueContentHistory(dbCtx context.Context, issueID, commentID int64) (bool, error) { - exists, err := db.GetEngine(dbCtx).Cols("id").Exist(&ContentHistory{ - IssueID: issueID, - CommentID: commentID, - }) - if err != nil { - log.Error("can not fetch issue content history. err=%v", err) - return false, err - } - return exists, err + return db.GetEngine(dbCtx).Where("issue_id = ? AND comment_id = ?", issueID, commentID).Exist(new(ContentHistory)) } // SoftDeleteIssueContentHistory soft delete diff --git a/models/issues/content_history_test.go b/models/issues/content_history_test.go index 0ea1d0f7b2..89d77a1df3 100644 --- a/models/issues/content_history_test.go +++ b/models/issues/content_history_test.go @@ -78,3 +78,16 @@ func TestContentHistory(t *testing.T) { assert.EqualValues(t, 7, list2[1].HistoryID) assert.EqualValues(t, 4, list2[2].HistoryID) } + +func TestHasIssueContentHistory(t *testing.T) { + assert.NoError(t, unittest.PrepareTestDatabase()) + + // Ensures that comment_id is into taken account even if it's zero. + _ = issues_model.SaveIssueContentHistory(db.DefaultContext, 1, 11, 100, timeutil.TimeStampNow(), "c-a", true) + _ = issues_model.SaveIssueContentHistory(db.DefaultContext, 1, 11, 100, timeutil.TimeStampNow().Add(5), "c-b", false) + + hasHistory1, _ := issues_model.HasIssueContentHistory(db.DefaultContext, 11, 0) + assert.False(t, hasHistory1) + hasHistory2, _ := issues_model.HasIssueContentHistory(db.DefaultContext, 11, 100) + assert.True(t, hasHistory2) +} From f7c26a44913670f1dfa07652673b1996e35ec4e1 Mon Sep 17 00:00:00 2001 From: "Panagiotis \"Ivory\" Vasilopoulos" Date: Thu, 29 Feb 2024 19:09:29 +0100 Subject: [PATCH 259/271] [I18N] Improve registration / password reset emails This change does not introduce any new strings, but improves upon some existing ones. --- options/locale/locale_en-US.ini | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index f8b4f8a2fb..0a6b1e0d82 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -436,7 +436,7 @@ last_admin = You cannot remove the last admin. There must be at least one admin. [mail] view_it_on = View it on %s reply = or reply to this email directly -link_not_working_do_paste = Not working? Try copying and pasting it to your browser. +link_not_working_do_paste = Does the link not work? Try copying and pasting it into your browser's URL bar. hi_user_x = Hi %s, activate_account = Please activate your account @@ -455,12 +455,12 @@ admin.new_user.text = Please click here to manage this user fro register_notify = Welcome to Forgejo register_notify.title = %[1]s, welcome to %[2]s register_notify.text_1 = this is your registration confirmation email for %s! -register_notify.text_2 = You can now login via username: %s. -register_notify.text_3 = If this account has been created for you, please set your password first. +register_notify.text_2 = You can sign into your account using your username: %s +register_notify.text_3 = If someone else made this account for you, you will need to set your password first. reset_password = Recover your account -reset_password.title = %s, you have requested to recover your account -reset_password.text = Please click the following link to recover your account within %s: +reset_password.title = %s, we have received a request to recover your account +reset_password.text = If this was you, please click the following link to recover your account within %s: register_success = Registration successful From 86b46085c79126cc60816ac490f48e6baae08a67 Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 29 Feb 2024 00:09:20 +0100 Subject: [PATCH 260/271] [BUG] Sort file list case insensitively - Make the sorting done on the entries list case insensitive. - Adds integration test. - Resolves #317 --- modules/base/natural_sort.go | 2 + modules/base/natural_sort_test.go | 6 +++ tests/integration/repo_test.go | 62 +++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+) diff --git a/modules/base/natural_sort.go b/modules/base/natural_sort.go index e920177f89..5b5febb906 100644 --- a/modules/base/natural_sort.go +++ b/modules/base/natural_sort.go @@ -5,11 +5,13 @@ package base import ( "math/big" + "strings" "unicode/utf8" ) // NaturalSortLess compares two strings so that they could be sorted in natural order func NaturalSortLess(s1, s2 string) bool { + s1, s2 = strings.ToLower(s1), strings.ToLower(s2) var i1, i2 int for { rune1, j1, end1 := getNextRune(s1, i1) diff --git a/modules/base/natural_sort_test.go b/modules/base/natural_sort_test.go index 91e864ad2a..7378d9a643 100644 --- a/modules/base/natural_sort_test.go +++ b/modules/base/natural_sort_test.go @@ -20,4 +20,10 @@ func TestNaturalSortLess(t *testing.T) { test("a-1-a", "a-1-b", true) test("2", "12", true) test("a", "ab", true) + + // Test for case insensitive. + test("A", "ab", true) + test("B", "ab", false) + test("a", "AB", true) + test("b", "AB", false) } diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go index b62297e0e9..03124ecaf8 100644 --- a/tests/integration/repo_test.go +++ b/tests/integration/repo_test.go @@ -6,6 +6,7 @@ package integration import ( "fmt" "net/http" + "net/url" "path" "strings" "testing" @@ -15,11 +16,13 @@ import ( repo_model "code.gitea.io/gitea/models/repo" unit_model "code.gitea.io/gitea/models/unit" "code.gitea.io/gitea/models/unittest" + user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/test" "code.gitea.io/gitea/modules/translation" repo_service "code.gitea.io/gitea/services/repository" + files_service "code.gitea.io/gitea/services/repository/files" "code.gitea.io/gitea/tests" "github.com/PuerkitoBio/goquery" @@ -899,3 +902,62 @@ func TestRepoHomeViewRedirect(t *testing.T) { assert.Equal(t, "Wiki", txt) }) } + +func TestRepoFilesList(t *testing.T) { + onGiteaRun(t, func(t *testing.T, u *url.URL) { + user2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) + + // create the repo + repo, _, f := CreateDeclarativeRepo(t, user2, "", + []unit_model.Type{unit_model.TypeCode}, nil, + []*files_service.ChangeRepoFile{ + { + Operation: "create", + TreePath: "zEta", + ContentReader: strings.NewReader("zeta"), + }, + { + Operation: "create", + TreePath: "licensa", + ContentReader: strings.NewReader("licensa"), + }, + { + Operation: "create", + TreePath: "licensz", + ContentReader: strings.NewReader("licensz"), + }, + { + Operation: "create", + TreePath: "delta", + ContentReader: strings.NewReader("delta"), + }, + { + Operation: "create", + TreePath: "Charlie/aa.txt", + ContentReader: strings.NewReader("charlie"), + }, + { + Operation: "create", + TreePath: "Beta", + ContentReader: strings.NewReader("beta"), + }, + { + Operation: "create", + TreePath: "alpha", + ContentReader: strings.NewReader("alpha"), + }, + }, + ) + defer f() + + req := NewRequest(t, "GET", "/"+repo.FullName()) + resp := MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + filesList := htmlDoc.Find("#repo-files-table tbody tr").Map(func(_ int, s *goquery.Selection) string { + return s.AttrOr("data-entryname", "") + }) + + assert.EqualValues(t, []string{"Charlie", "alpha", "Beta", "delta", "licensa", "LICENSE", "licensz", "README.md", "zEta"}, filesList) + }) +} From 9e662fe2cd93be107aa0c2fba4d623a1bdba86f1 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sat, 2 Mar 2024 17:39:25 +0800 Subject: [PATCH 261/271] [RELEASE] publish container images tagged with the major version Now that semantic versions are used, the major version must be used instead of major.minor to distinguish releases with breaking changes. Before: Forgejo v1.21.1-0, tags 1.21.1-0 and 1.21 Forgejo v1.21.2-0, tags 1.21.2-0 and 1.21 Forgejo v1.22.1-0, tags 1.22.1-0 and 1.22 After Forgejo v7.0.0 tags 7.0.0 and 7 Forgejo v7.0.1 tags 7.0.1 and 7 Forgejo v7.1.2 tags 7.1.2 and 7 Forgejo v8.0.1 tags 8.0.1 and 8 --- .forgejo/workflows/publish-release.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.forgejo/workflows/publish-release.yml b/.forgejo/workflows/publish-release.yml index 1afa941952..eaa14c3693 100644 --- a/.forgejo/workflows/publish-release.yml +++ b/.forgejo/workflows/publish-release.yml @@ -42,16 +42,19 @@ jobs: - uses: actions/checkout@v3 - name: copy & sign - uses: https://code.forgejo.org/forgejo/forgejo-build-publish/publish@v1 + uses: https://code.forgejo.org/forgejo/forgejo-build-publish/publish@v4 with: - forgejo: ${{ vars.FORGEJO }} + from-forgejo: ${{ vars.FORGEJO }} + to-forgejo: ${{ vars.FORGEJO }} from-owner: ${{ vars.FROM_OWNER }} to-owner: ${{ vars.TO_OWNER }} repo: ${{ vars.REPO }} - ref-name: ${{ github.ref_name }} release-notes: "See https://codeberg.org/forgejo/forgejo/src/branch/forgejo/RELEASE-NOTES.md#{ANCHOR}" - doer: ${{ vars.DOER }} - token: ${{ secrets.TOKEN }} + ref-name: ${{ github.ref_name }} + sha: ${{ github.sha }} + from-token: ${{ secrets.TOKEN }} + to-doer: ${{ vars.DOER }} + to-token: ${{ secrets.TOKEN }} gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} gpg-passphrase: ${{ secrets.GPG_PASSPHRASE }} verbose: ${{ vars.VERBOSE }} From 30e0b2f85115aeeb5943f2f37d487cf36178abbe Mon Sep 17 00:00:00 2001 From: Otto Richter Date: Sun, 18 Feb 2024 16:48:32 +0100 Subject: [PATCH 262/271] Label and focus styling for mobile menu button --- options/locale/locale_en-US.ini | 1 + templates/base/head_navbar.tmpl | 2 +- web_src/css/modules/navbar.css | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 0a6b1e0d82..31ecf6bc1a 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -26,6 +26,7 @@ enable_javascript = This website requires JavaScript. toc = Table of Contents licenses = Licenses return_to_gitea = Return to Forgejo +toggle_menu = Toggle Menu username = Username email = Email address diff --git a/templates/base/head_navbar.tmpl b/templates/base/head_navbar.tmpl index b4a93688e3..fb0301aeb1 100644 --- a/templates/base/head_navbar.tmpl +++ b/templates/base/head_navbar.tmpl @@ -20,7 +20,7 @@
      {{end}} - +
      diff --git a/web_src/css/modules/navbar.css b/web_src/css/modules/navbar.css index b6fd2ff20a..11020df359 100644 --- a/web_src/css/modules/navbar.css +++ b/web_src/css/modules/navbar.css @@ -41,8 +41,8 @@ justify-content: stretch; } -#navbar a.item:hover, -#navbar button.item:hover { +#navbar a.item:hover, #navbar a.item:focus, +#navbar button.item:hover, #navbar button.item:focus { background: var(--color-nav-hover-bg); } From 4d2c019b5abc920737ca857b433b799b679fec1d Mon Sep 17 00:00:00 2001 From: Otto Richter Date: Sun, 18 Feb 2024 17:28:35 +0100 Subject: [PATCH 263/271] Add focus styling to most button types While it might be favourable to have distinct focus and hover styling, having no focus styling at all makes keyboard navigation very difficult. Some people consider :focus to be equal to a keyboard-driven :hover, so I'm moving the focus pseudo-classes from being a no-op to adding the hover styling. --- web_src/css/base.css | 6 ++-- web_src/css/modules/button.css | 64 ++++++++++++++++++---------------- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/web_src/css/base.css b/web_src/css/base.css index 9cad9c5d23..bb45c8d325 100644 --- a/web_src/css/base.css +++ b/web_src/css/base.css @@ -1935,9 +1935,9 @@ table th[data-sortt-desc] .svg { } .ui.secondary.pointing.menu .active.item, -.ui.secondary.pointing.menu .active.item:hover, -.ui.secondary.pointing.menu .dropdown.item:hover, -.ui.secondary.pointing.menu a.item:hover { +.ui.secondary.pointing.menu .active.item:hover, .ui.secondary.pointing.menu .active.item:focus, +.ui.secondary.pointing.menu .dropdown.item:hover, .ui.secondary.pointing.menu .dropdown.item:focus, +.ui.secondary.pointing.menu a.item:hover, .ui.secondary.pointing.menu a.item:focus { color: var(--color-text-dark); } diff --git a/web_src/css/modules/button.css b/web_src/css/modules/button.css index 36cb499aeb..b772a4c14e 100644 --- a/web_src/css/modules/button.css +++ b/web_src/css/modules/button.css @@ -1,14 +1,14 @@ /* this contains override styles for buttons and related elements */ /* these styles changed the Fomantic UI's rules, Fomantic UI expects only "basic" buttons have borders */ -.ui.button, -.ui.button:focus { +.ui.button { background: var(--color-button); border: 1px solid var(--color-light-border); color: var(--color-text); } -.ui.button:hover { +.ui.button:hover, +.ui.button:focus { background: var(--color-hover); color: var(--color-text); } @@ -20,13 +20,15 @@ .ui.active.button, .ui.button:active, .ui.active.button:active, -.ui.active.button:hover { +.ui.active.button:hover, +.ui.active.button:focus { background: var(--color-active); color: var(--color-text); } .delete-button, -.delete-button:hover { +.delete-button:hover, +.delete-button:focus { color: var(--color-red); } @@ -87,15 +89,15 @@ It needs some tricks to tweak the left/right borders with active state */ .ui.labeled.button.disabled > .button, .ui.basic.buttons .button, -.ui.basic.button, -.ui.basic.buttons .button:focus, -.ui.basic.button:focus { +.ui.basic.button { color: var(--color-text-light); background: var(--color-button); } .ui.basic.buttons .button:hover, -.ui.basic.button:hover { +.ui.basic.button:hover, +.ui.basic.buttons .button:focus, +.ui.basic.button:focus { color: var(--color-text); background: var(--color-hover); } @@ -105,7 +107,9 @@ It needs some tricks to tweak the left/right borders with active state */ .ui.basic.buttons .active.button, .ui.basic.active.button, .ui.basic.buttons .active.button:hover, -.ui.basic.active.button:hover { +.ui.basic.active.button:hover, +.ui.basic.buttons .active.button:focus, +.ui.basic.active.button:focus { color: var(--color-text); background: var(--color-active); } @@ -124,15 +128,15 @@ It needs some tricks to tweak the left/right borders with active state */ .ui.primary.labels .label, .ui.ui.ui.primary.label, .ui.primary.button, -.ui.primary.buttons .button, -.ui.primary.button:focus, -.ui.primary.buttons .button:focus { +.ui.primary.buttons .button { background: var(--color-primary); color: var(--color-primary-contrast); } .ui.primary.button:hover, -.ui.primary.buttons .button:hover { +.ui.primary.buttons .button:hover, +.ui.primary.button:focus, +.ui.primary.buttons .button:focus { background: var(--color-primary-hover); color: var(--color-primary-contrast); } @@ -143,15 +147,15 @@ It needs some tricks to tweak the left/right borders with active state */ } .ui.basic.primary.buttons .button, -.ui.basic.primary.button, -.ui.basic.primary.buttons .button:focus, -.ui.basic.primary.button:focus { +.ui.basic.primary.button{ color: var(--color-primary); border-color: var(--color-primary); } .ui.basic.primary.buttons .button:hover, -.ui.basic.primary.button:hover { +.ui.basic.primary.button:hover, +.ui.basic.primary.buttons .button:focus, +.ui.basic.primary.button:focus { color: var(--color-primary-hover); border-color: var(--color-primary-hover); } @@ -184,15 +188,15 @@ It needs some tricks to tweak the left/right borders with active state */ } .ui.basic.secondary.buttons .button, -.ui.basic.secondary.button, -.ui.basic.secondary.button:focus, -.ui.basic.secondary.buttons .button:focus { +.ui.basic.secondary.button { color: var(--color-secondary-button); border-color: var(--color-secondary-button); } .ui.basic.secondary.buttons .button:hover, -.ui.basic.secondary.button:hover { +.ui.basic.secondary.button:hover, +.ui.basic.secondary.button:focus, +.ui.basic.secondary.buttons .button:focus { color: var(--color-secondary-hover); border-color: var(--color-secondary-hover); } @@ -208,14 +212,14 @@ It needs some tricks to tweak the left/right borders with active state */ .ui.red.labels .label, .ui.ui.ui.red.label, .ui.red.button, -.ui.red.buttons .button, -.ui.red.button:focus, -.ui.red.buttons .button:focus { +.ui.red.buttons .button { background: var(--color-red); } .ui.red.button:hover, -.ui.red.buttons .button:hover { +.ui.red.buttons .button:hover, +.ui.red.button:focus, +.ui.red.buttons .button:focus { background: var(--color-red-dark-1); } @@ -225,15 +229,15 @@ It needs some tricks to tweak the left/right borders with active state */ } .ui.basic.red.buttons .button, -.ui.basic.red.button, -.ui.basic.red.buttons .button:focus, -.ui.basic.red.button:focus { +.ui.basic.red.button { color: var(--color-red); border-color: var(--color-red); } .ui.basic.red.buttons .button:hover, -.ui.basic.red.button:hover { +.ui.basic.red.button:hover, +.ui.basic.red.buttons .button:focus, +.ui.basic.red.button:focus { color: var(--color-red-dark-1); border-color: var(--color-red-dark-1); } From 88f68850b59b6ae801df8af5d677e88e4dd16133 Mon Sep 17 00:00:00 2001 From: Otto Richter Date: Sun, 18 Feb 2024 17:54:53 +0100 Subject: [PATCH 264/271] Accessibility: Watch & Star on small screens The elements were hidden on small screens to preserve space and the icons still conveyed the meaning for users with intact eye vision. However, the names were no longer exposed to screen readers, and their users usually cannot obtain the meaning from the icons. Adding aria-labels to the affected templates results in certain complexity due to the DOM, so instead I decided to use some accessible CSS tricks to move the content off the screen instead of hiding it. It should remain accessible for most screen readers. --- web_src/css/repo/header.css | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/web_src/css/repo/header.css b/web_src/css/repo/header.css index d5c7d212e8..3400284e4b 100644 --- a/web_src/css/repo/header.css +++ b/web_src/css/repo/header.css @@ -89,11 +89,17 @@ .repo-header .flex-item { flex-grow: 1; } - .repo-buttons .ui.labeled.button .text { - display: none; - } + .repo-buttons .ui.labeled.button .text, .repo-header .flex-item-trailing .label { - display: none; + /* the elements are hidden from users with intact eye vision, + * because SVG icons convey the meaning. + * However, they should remain accessible to screen readers */ + position: absolute; + left: -1000vw; + top: auto; + width: 1px; + height: 1px; + overflow: hidden; } .repo-header .flex-item-trailing .repo-icon { display: initial; From c63b52c1267b87b5888f77c7c98bff83b41839db Mon Sep 17 00:00:00 2001 From: Gusted Date: Thu, 29 Feb 2024 20:51:02 +0100 Subject: [PATCH 265/271] [FEAT] Show follow symlink button - When a user goes opens a symlink file in Forgejo, the file would be rendered with the path of the symlink as content. - Add a button that is shown when the user opens a *valid* symlink file, which means that the symlink must have an valid path to an existent file and after 999 follows isn't a symlink anymore. - Return the relative path from the `FollowLink` functions, because Git really doesn't want to tell where an file is located based on the blob ID. - Adds integration tests. --- modules/base/tool.go | 2 +- modules/git/tree_entry.go | 32 +++++++++++---------- options/locale/locale_en-US.ini | 1 + routers/web/repo/view.go | 13 +++++++-- templates/repo/view_file.tmpl | 3 ++ tests/integration/repo_test.go | 51 +++++++++++++++++++++++++++++++++ 6 files changed, 84 insertions(+), 18 deletions(-) diff --git a/modules/base/tool.go b/modules/base/tool.go index 168a2220b2..231507546d 100644 --- a/modules/base/tool.go +++ b/modules/base/tool.go @@ -174,7 +174,7 @@ func Int64sToStrings(ints []int64) []string { func EntryIcon(entry *git.TreeEntry) string { switch { case entry.IsLink(): - te, err := entry.FollowLink() + te, _, err := entry.FollowLink() if err != nil { log.Debug(err.Error()) return "file-symlink-file" diff --git a/modules/git/tree_entry.go b/modules/git/tree_entry.go index 9513121487..2c47c8858c 100644 --- a/modules/git/tree_entry.go +++ b/modules/git/tree_entry.go @@ -23,15 +23,15 @@ func (te *TreeEntry) Type() string { } // FollowLink returns the entry pointed to by a symlink -func (te *TreeEntry) FollowLink() (*TreeEntry, error) { +func (te *TreeEntry) FollowLink() (*TreeEntry, string, error) { if !te.IsLink() { - return nil, ErrBadLink{te.Name(), "not a symlink"} + return nil, "", ErrBadLink{te.Name(), "not a symlink"} } // read the link r, err := te.Blob().DataAsync() if err != nil { - return nil, err + return nil, "", err } closed := false defer func() { @@ -42,7 +42,7 @@ func (te *TreeEntry) FollowLink() (*TreeEntry, error) { buf := make([]byte, te.Size()) _, err = io.ReadFull(r, buf) if err != nil { - return nil, err + return nil, "", err } _ = r.Close() closed = true @@ -56,33 +56,35 @@ func (te *TreeEntry) FollowLink() (*TreeEntry, error) { } if t == nil { - return nil, ErrBadLink{te.Name(), "points outside of repo"} + return nil, "", ErrBadLink{te.Name(), "points outside of repo"} } target, err := t.GetTreeEntryByPath(lnk) if err != nil { if IsErrNotExist(err) { - return nil, ErrBadLink{te.Name(), "broken link"} + return nil, "", ErrBadLink{te.Name(), "broken link"} } - return nil, err + return nil, "", err } - return target, nil + return target, lnk, nil } // FollowLinks returns the entry ultimately pointed to by a symlink -func (te *TreeEntry) FollowLinks() (*TreeEntry, error) { +func (te *TreeEntry) FollowLinks() (*TreeEntry, string, error) { if !te.IsLink() { - return nil, ErrBadLink{te.Name(), "not a symlink"} + return nil, "", ErrBadLink{te.Name(), "not a symlink"} } entry := te + entryLink := "" for i := 0; i < 999; i++ { if entry.IsLink() { - next, err := entry.FollowLink() + next, link, err := entry.FollowLink() + entryLink = link if err != nil { - return nil, err + return nil, "", err } if next.ID == entry.ID { - return nil, ErrBadLink{ + return nil, "", ErrBadLink{ entry.Name(), "recursive link", } @@ -93,12 +95,12 @@ func (te *TreeEntry) FollowLinks() (*TreeEntry, error) { } } if entry.IsLink() { - return nil, ErrBadLink{ + return nil, "", ErrBadLink{ te.Name(), "too many levels of symbolic links", } } - return entry, nil + return entry, entryLink, nil } // returns the Tree pointed to by this TreeEntry, or nil if this is not a tree diff --git a/options/locale/locale_en-US.ini b/options/locale/locale_en-US.ini index 0a6b1e0d82..424b063796 100644 --- a/options/locale/locale_en-US.ini +++ b/options/locale/locale_en-US.ini @@ -1205,6 +1205,7 @@ tag = Tag released_this = released this file.title = %s at %s file_raw = Raw +file_follow = Follow Symlink file_history = History file_view_source = View Source file_view_rendered = View Rendered diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 86977062cb..32c09bb5f2 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -114,7 +114,7 @@ func findReadmeFileInEntries(ctx *context.Context, entries []*git.TreeEntry, try log.Debug("Potential readme file: %s", entry.Name()) if readmeFiles[i] == nil || base.NaturalSortLess(readmeFiles[i].Name(), entry.Blob().Name()) { if entry.IsLink() { - target, err := entry.FollowLinks() + target, _, err := entry.FollowLinks() if err != nil && !git.IsErrBadLink(err) { return "", nil, err } else if target != nil && (target.IsExecutable() || target.IsRegular()) { @@ -267,7 +267,7 @@ func getFileReader(ctx gocontext.Context, repoID int64, blob *git.Blob) ([]byte, func renderReadmeFile(ctx *context.Context, subfolder string, readmeFile *git.TreeEntry) { target := readmeFile if readmeFile != nil && readmeFile.IsLink() { - target, _ = readmeFile.FollowLinks() + target, _, _ = readmeFile.FollowLinks() } if target == nil { // if findReadmeFile() failed and/or gave us a broken symlink (which it shouldn't) @@ -391,6 +391,15 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["FileName"] = blob.Name() ctx.Data["RawFileLink"] = ctx.Repo.RepoLink + "/raw/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(ctx.Repo.TreePath) + if entry.IsLink() { + _, link, err := entry.FollowLinks() + // Errors should be allowed, because this shouldn't + // block rendering invalid symlink files. + if err == nil { + ctx.Data["SymlinkURL"] = ctx.Repo.RepoLink + "/src/" + ctx.Repo.BranchNameSubURL() + "/" + util.PathEscapeSegments(link) + } + } + commit, err := ctx.Repo.Commit.GetCommitByPath(ctx.Repo.TreePath) if err != nil { ctx.ServerError("GetCommitByPath", err) diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index b8eb2393fe..91b10f744a 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -43,6 +43,9 @@ {{end}} {{if not .ReadmeInList}}
      + {{if .SymlinkURL}} + {{ctx.Locale.Tr "repo.file_follow"}} + {{end}} {{ctx.Locale.Tr "repo.file_raw"}} {{if not .IsViewCommit}} {{ctx.Locale.Tr "repo.file_permalink"}} diff --git a/tests/integration/repo_test.go b/tests/integration/repo_test.go index 03124ecaf8..cb79a2fa9b 100644 --- a/tests/integration/repo_test.go +++ b/tests/integration/repo_test.go @@ -961,3 +961,54 @@ func TestRepoFilesList(t *testing.T) { assert.EqualValues(t, []string{"Charlie", "alpha", "Beta", "delta", "licensa", "LICENSE", "licensz", "README.md", "zEta"}, filesList) }) } + +func TestRepoFollowSymlink(t *testing.T) { + defer tests.PrepareTestEnv(t)() + session := loginUser(t, "user2") + + assertCase := func(t *testing.T, url, expectedSymlinkURL string, shouldExist bool) { + t.Helper() + + req := NewRequest(t, "GET", url) + resp := session.MakeRequest(t, req, http.StatusOK) + + htmlDoc := NewHTMLParser(t, resp.Body) + symlinkURL, ok := htmlDoc.Find(".file-actions .button[data-kind='follow-symlink']").Attr("href") + if shouldExist { + assert.True(t, ok) + assert.EqualValues(t, expectedSymlinkURL, symlinkURL) + } else { + assert.False(t, ok) + } + } + + t.Run("Normal", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + assertCase(t, "/user2/readme-test/src/branch/symlink/README.md?display=source", "/user2/readme-test/src/branch/symlink/some/other/path/awefulcake.txt", true) + }) + + t.Run("Normal", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + assertCase(t, "/user2/readme-test/src/branch/symlink/some/README.txt", "/user2/readme-test/src/branch/symlink/some/other/path/awefulcake.txt", true) + }) + + t.Run("Normal", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + assertCase(t, "/user2/readme-test/src/branch/symlink/up/back/down/down/README.md", "/user2/readme-test/src/branch/symlink/down/side/../left/right/../reelmein", true) + }) + + t.Run("Broken symlink", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + assertCase(t, "/user2/readme-test/src/branch/fallbacks-broken-symlinks/docs/README", "", false) + }) + + t.Run("Loop symlink", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + assertCase(t, "/user2/readme-test/src/branch/symlink-loop/README.md", "", false) + }) + + t.Run("Not a symlink", func(t *testing.T) { + defer tests.PrintCurrentTest(t)() + assertCase(t, "/user2/readme-test/src/branch/master/README.md", "", false) + }) +} From ad547edf3b291aca5dce1773bf1b6bb3601774e0 Mon Sep 17 00:00:00 2001 From: Earl Warren Date: Sun, 3 Mar 2024 11:11:26 +0800 Subject: [PATCH 266/271] [TESTS] enable AddFixtures in unit tests Use setting.AppWorkPath instead of filepath.Dir(setting.AppPath). It is the common denominator between: * models/unittest/testdb.go:MainTest * tests/test_utils.go:InitTest which makes it usable in unit tests as well as integration tests. --- tests/test_utils.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_utils.go b/tests/test_utils.go index 8e456783cf..85d7462d73 100644 --- a/tests/test_utils.go +++ b/tests/test_utils.go @@ -271,8 +271,8 @@ func Printf(format string, args ...any) { func AddFixtures(dirs ...string) func() { return unittest.OverrideFixtures( unittest.FixturesOptions{ - Dir: filepath.Join(filepath.Dir(setting.AppPath), "models/fixtures/"), - Base: filepath.Dir(setting.AppPath), + Dir: filepath.Join(setting.AppWorkPath, "models/fixtures/"), + Base: setting.AppWorkPath, Dirs: dirs, }, ) From 41b4884085347a24b0f9e3993b2e75a37c29e3ef Mon Sep 17 00:00:00 2001 From: Codeberg Translate Date: Mon, 4 Mar 2024 01:58:49 +0000 Subject: [PATCH 267/271] [I18N] Translations update from Weblate (#2521) Translations update from [Weblate](https://translate.codeberg.org) for [Forgejo/forgejo](https://translate.codeberg.org/projects/forgejo/forgejo/). Current translation status: ![Weblate translation status](https://translate.codeberg.org/widget/forgejo/forgejo/horizontal-auto.svg) Co-authored-by: 0ko <0ko@users.noreply.translate.codeberg.org> Co-authored-by: TheAwiteb Co-authored-by: mondstern Co-authored-by: flactwin Co-authored-by: Gusted Co-authored-by: Salif Mehmed Co-authored-by: Fjuro Co-authored-by: Wuzzy Co-authored-by: fnetX Co-authored-by: zenobit Co-authored-by: Dirk Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/2521 Co-authored-by: Codeberg Translate Co-committed-by: Codeberg Translate --- options/locale/locale_ar.ini | 17 +- options/locale/locale_be.ini | 19 ++ options/locale/locale_bg.ini | 320 +++++++++++++++++++++++++++--- options/locale/locale_cs-CZ.ini | 337 +++++++++++++++++++++++--------- options/locale/locale_de-DE.ini | 286 ++++++++++++++------------- options/locale/locale_nl-NL.ini | 13 +- options/locale/locale_ru-RU.ini | 102 +++++----- options/locale/locale_sl.ini | 129 +++++++++++- 8 files changed, 897 insertions(+), 326 deletions(-) create mode 100644 options/locale/locale_be.ini diff --git a/options/locale/locale_ar.ini b/options/locale/locale_ar.ini index 7f0f9791c7..c60ed5c42c 100644 --- a/options/locale/locale_ar.ini +++ b/options/locale/locale_ar.ini @@ -64,7 +64,7 @@ copy_url = انسخ الرابط admin_panel = إدارة الموقع copy_error = فشل النسخ new_mirror = مرآة جديدة -re_type = أكّد كلمة المرور الجديدة +re_type = تأكيد كلمة المرور webauthn_unsupported_browser = متصفحك لا يدعم ويب آوثن حالياً. copy = انسخ enabled = مُفَعَّل @@ -210,8 +210,8 @@ admin_email = عنوان البريد الإلكتروني install_btn_confirm = تثبت فورجيو secret_key_failed = لم يتم توليد مفتاح سري: %v save_config_failed = فشل في حفظ الإعداد: %s -sqlite3_not_available = هذا الأصدار من فورجيو لا يدعم SQLite3. من فضلك قم بتحميل الاصدار الملفي الرسمي من %s (ليس اصدار 'gobuild'). -test_git_failed = لم يتمكن من أختبار أمر جِت: %v +sqlite3_not_available = هذا الإصدار من فورجيو لا يدعم SQLite3. من فضلك قم بتنزيل الإصدار الرسمي من %s (ليس إصدار 'gobuild'). +test_git_failed = يتعذر اختبار أمر جِت: %v confirm_password = أكّد كلمة المرور invalid_admin_setting = إعداد حساب المدير غير صالح: %v invalid_log_root_path = مسار السجل غير صالح: %v @@ -1375,6 +1375,7 @@ network_error = خطأ في الشبكة invalid_csrf = طلب سيئ: رمز CSRF غير صالح occurred = حدث خطأ missing_csrf = طلب سيئ: لا يوجد رمز CSRF +server_internal = خطأ داخلي في الخادم [startpage] install = سهلة التثبيت @@ -1492,6 +1493,7 @@ openid_signin_desc = أدخل مسار الـOpenID الخاص بك. مثلاً: openid_register_desc = مسار الـOpenID المختار مجهول. اربطه مع حساب جديد هنا. remember_me = تذكر هذا الجهاز remember_me.compromised = رمز الاحتفاظ بتسجيل الدخول لم يعد صالحا، مما قد يعني اختراق الحساب. نرجو مراجعة حسابك لرؤية أي نشاط غير مألوف. +authorization_failed_desc = فشل التفويض لأننا اكتشفنا طلبًا غير صالح. يرجى الاتصال بمشرف التطبيق الذي حاولت ترخيصه. [packages] rpm.repository.multiple_groups = هذه الحزمة متوفرة في مجموعات متعددة. @@ -1656,7 +1658,7 @@ config.default_enable_timetracking = فعّل تتبع الوقت مبدئيا config.default_allow_only_contributors_to_track_time = اسمح للمشتركين في المستودع موحدهم بتتبع الوقت [form] -username_error_no_dots = ` يُمكن أن يحتوي فقط على أرقام "0-9 "، أبجدية "A-Z" ،"a-z"، شرطة "-"، وخط أسفل "_" ولا يمكن أن تبدأ أو تنتهي بغير الأبجدية الرقمية، كما يحظر تتالي رموز غير أبجدية رقمية.` +username_error_no_dots = ` يُمكنه أن يحتوي على حروف إنجليزية وأرقام وشرطة ("-") وشرطة سفلية ("_") فقط. ويمكنه ان يبدأ وينتهي بحرف او برقم.` Password = كلمة المرور admin_cannot_delete_self = لا يمكنك أن تحذف نفسك عندما تكون مدير من فضلك ازيل امتيازاتك الإدارية اولا. enterred_invalid_repo_name = اسم المستودع الذي أدخلته خطأ. @@ -1684,8 +1686,8 @@ username_password_incorrect = اسم المستخدم أو كلمة المرور org_still_own_repo = "لدى هذه المنظمة مستودع واحد أو أكثر؛ احذفهم أو انقل ملكيتهم أولا." enterred_invalid_org_name = اسم المنظمة التي أدخلته خطأ. lang_select_error = اختر لغة من القائمة. -alpha_dash_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ('-') والشرطة السفلية ('_').` -alpha_dash_dot_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ('-') والشرطة السفلية ('_') والنقطة ('.').` +alpha_dash_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ("-") والشرطة السفلية ("_").` +alpha_dash_dot_error = ` لا يجب أن يحتوي إلا على الحروف الإنجليزية والأرقام والشرطة ("-") والشرطة السفلية ("_") والنقطة (".").` repo_name_been_taken = اسم المستودع مستعمل بالفعل. Email = البريد الإلكتروني auth_failed = فشل الاستيثاق: %v @@ -1733,6 +1735,9 @@ git_ref_name_error = `يجب أن يكون اسمًا مرجعيًا جيدًا include_error = ` يجب أن يحتوي على سلسلة فرعية "%s".` size_error = `يجب أن يكون بالحجم %s.' glob_pattern_error = `النمط الشامل غير صالح: %s.` +CommitChoice = إختيار الإداع +regex_pattern_error = ` نمط التعبير النمطي غير صالح: %s.` +username_error = ` يُمكنه أن يحتوي على حروف إنجليزية وأرقام وشرطة ("-") وشرطة سفلية ("_") و نقطة (".") فقط. ويمكنه ان يبدأ وينتهي بحرف او برقم.` [home] filter = تصفيات أخرى diff --git a/options/locale/locale_be.ini b/options/locale/locale_be.ini new file mode 100644 index 0000000000..f9d8e738c3 --- /dev/null +++ b/options/locale/locale_be.ini @@ -0,0 +1,19 @@ + + + +[common] +dashboard = Панэль кіравання +explore = Агляд +help = Дапамога +logo = Лагатып +sign_in = Увайсці +sign_in_or = або +sign_out = Выйсці +sign_up = Зарэгістравацца +link_account = Звязаць Уліковы запіс +register = Рэгістрацыя +version = Версія +powered_by = Працуе на ℅s +page = Старонка +home = Галоўная Старонка +sign_in_with_provider = Увайсці з %s \ No newline at end of file diff --git a/options/locale/locale_bg.ini b/options/locale/locale_bg.ini index de2c40c174..3eca60f551 100644 --- a/options/locale/locale_bg.ini +++ b/options/locale/locale_bg.ini @@ -128,6 +128,7 @@ profile_desc = Контролирайте как вашият профил се permission_write = Четене и Писане twofa_disable = Изключване на двуфакторното удостоверяване twofa_enroll = Включване на двуфакторно удостоверяване +ssh_key_name_used = Вече съществува SSH ключ със същото име във вашия акаунт. [packages] container.labels.value = Стойност @@ -147,6 +148,9 @@ keywords = Ключови думи details.author = Автор about = Относно този пакет settings.delete.success = Пакетът бе изтрит. +settings.delete = Изтриване на пакета +container.details.platform = Платформа +settings.delete.error = Неуспешно изтриване на пакет. [tool] hours = %d часа @@ -243,7 +247,7 @@ email = Адрес на ел. поща issues = Задачи retry = Повторен опит remove = Премахване -admin_panel = Администриране на сайта +admin_panel = Управление на сайта account_settings = Настройки на акаунта powered_by = Осъществено от %s pull_requests = Заявки за сливане @@ -451,15 +455,15 @@ activity.period.quarterly = 3 месеца activity.period.semiyearly = 6 месеца activity.title.user_1 = %d потребител activity.title.user_n = %d потребители -activity.title.prs_n = %d Заявки за сливане +activity.title.prs_n = %d заявки за сливане activity.merged_prs_count_1 = Слята заявка за сливане activity.merged_prs_count_n = Слети заявки за сливане -activity.active_issues_count_1 = %d Активна задача -activity.active_issues_count_n = %d Активни задачи +activity.active_issues_count_1 = %d активна задача +activity.active_issues_count_n = %d активни задачи activity.closed_issues_count_1 = Затворена задача activity.closed_issues_count_n = Затворени задачи -activity.title.issues_1 = %d Задача -activity.title.issues_n = %d Задачи +activity.title.issues_1 = %d задача +activity.title.issues_n = %d задачи wiki.pages = Страници activity.git_stats_author_1 = %d автор activity.git_stats_and_deletions = и @@ -467,14 +471,14 @@ project_board = Проекти wiki.save_page = Запазване на страницата activity.git_stats_author_n = %d автори wiki.delete_page_button = Изтриване на страницата -activity.title.prs_1 = %d Заявка за сливане -activity.active_prs_count_n = %d Активни заявки за сливане +activity.title.prs_1 = %d заявка за сливане +activity.active_prs_count_n = %d активни заявки за сливане activity.period.filter_label = Период: activity.period.daily = 1 ден activity.period.halfweekly = 3 дни activity.period.weekly = 1 седмица activity.period.yearly = 1 година -activity.active_prs_count_1 = %d Активна заявка за сливане +activity.active_prs_count_1 = %d активна заявка за сливане wiki.page_title = Заглавие на страницата wiki.page_content = Съдържание на страницата wiki.filter_page = Филтриране на страница @@ -613,7 +617,7 @@ issues.filter_milestone_all = Всички етапи issues.filter_milestone_open = Отворени етапи issues.filter_milestone_none = Без етапи issues.filter_project = Проект -issues.num_participants = %d Участващи +issues.num_participants = %d участващи issues.filter_assignee = Изпълнител issues.filter_milestone_closed = Затворени етапи issues.filter_assginee_no_select = Всички изпълнители @@ -626,8 +630,8 @@ activity.opened_prs_label = Предложена activity.title.issues_closed_from = %s затворена от %s activity.closed_issue_label = Затворена activity.new_issue_label = Отворена -activity.title.releases_1 = %d Издание -activity.title.releases_n = %d Издания +activity.title.releases_1 = %d издание +activity.title.releases_n = %d издания milestones.completeness = %d%% Завършен activity.title.prs_opened_by = %s предложена от %s issues.action_milestone_no_select = Без етап @@ -686,18 +690,18 @@ more_operations = Още операции download_archive = Изтегляне на хранилището branch = Клон tree = Дърво -branches = Клонове -tags = Тагове -tag = Таг -filter_branch_and_tag = Филтриране на клон или таг +branches = Клони +tags = Маркери +tag = Маркер +filter_branch_and_tag = Филтриране на клон или маркер symbolic_link = Символна връзка executable_file = Изпълним файл blame = Авторство editor.patch = Прилагане на кръпка editor.new_patch = Нова кръпка signing.wont_sign.not_signed_in = Не сте влезли. -settings.tags = Тагове -release.tags = Тагове +settings.tags = Маркери +release.tags = Маркери star_guest_user = Влезте, за отбелязване на това хранилище със звезда. download_bundle = Изтегляне на BUNDLE desc.private = Частно @@ -746,17 +750,196 @@ issues.label_modify = Редактиране на етикета issues.due_date_added = добави крайния срок %s %s issues.due_date_remove = премахна крайния срок %s %s release.new_release = Ново издание -release.tag_helper_existing = Съществуващ таг. -release.tag_name = Име на тага -issues.no_ref = Няма указан Клон/Таг +release.tag_helper_existing = Съществуващ маркер. +release.tag_name = Име на маркера +issues.no_ref = Няма указан клон/маркер issues.lock.reason = Причина за заключването pulls.create = Създаване на заявка за сливане issues.label.filter_sort.reverse_by_size = Най-голям размер issues.unlock = Отключване на обсъждането issues.due_date_form_add = Добавяне на краен срок -release.save_draft = Запазване като чернова -release.add_tag = Създаване само на таг +release.save_draft = Запазване на чернова +release.add_tag = Създаване само на маркер release.publish = Публикуване на издание +file_view_source = Преглед на изходния код +diff.parent = родител +issues.unlock_comment = отключи това обсъждане %s +release.edit_subheader = Изданията ви позволяват да управлявате версиите на проекта. +branch.already_exists = Вече съществува клон на име "%s". +contributors.contribution_type.deletions = Изтривания +contributors.contribution_type.additions = Добавяния +diff.browse_source = Разглеждане на изходния код +file_view_rendered = Преглед на визуализация +issues.lock_with_reason = заключи като %s и ограничи обсъждането до сътрудници %s +milestones.new_subheader = Етапите ви помагат да управлявате задачите и да проследявате напредъка им. +release.edit = редактиране +activity.published_release_label = Публикувано +activity.navbar.contributors = Допринесли +pulls.recently_pushed_new_branches = Изтласкахте в клона %[1]s %[2]s +branch.branch_name_conflict = Името на клон "%s" е в конфликт с вече съществуващия клон "%s". +all_branches = Всички клонове +file_raw = Директно +file_history = История +file_permalink = Постоянна връзка +projects.edit_subheader = Проектите ви позволяват да управлявате задачите и да проследявате напредъка. +release.compare = Сравняване +released_this = публикува това +file_too_large = Файлът е твърде голям, за да бъде показан. +commits = Подавания +commit = Подаване +editor.commit_changes = Подаване на промените +editor.add_tmpl = Добавяне на "<име на файла>" +editor.add = Добавяне на %s +editor.delete = Изтриване на %s +editor.update = Обновяване на %s +editor.commit_message_desc = Добавете опционално разширено описание… +commit_graph.monochrome = Моно +commit.contained_in = Това подаване се съдържа в: +editor.new_branch_name_desc = Име на новия клон… +editor.propose_file_change = Предлагане на промяна на файла +editor.create_new_branch = Създаване на нов клон за това подаване и започване на заявка за сливане. +editor.create_new_branch_np = Създаване на нов клон за това подаване. +editor.filename_is_invalid = Името на файла е невалидно: "%s". +editor.commit_directly_to_this_branch = Подаване директно към клона %s. +editor.branch_already_exists = Клонът "%s" вече съществува в това хранилище. +editor.file_already_exists = Файл с име "%s" вече съществува в това хранилище. +editor.commit_empty_file_header = Подаване на празен файл +editor.commit_empty_file_text = Файлът, който сте на път да подадете, е празен. Продължаване? +editor.fail_to_update_file_summary = Съобщение за грешка: +editor.fail_to_update_file = Неуспешно обновяване/създаване на файл "%s". +editor.add_subdir = Добавяне на директория… +commits.commits = Подавания +commits.find = Търсене +commits.search_all = Всички клони +commits.search = Потърсете подавания… +commit.operations = Операции +issues.deleted_milestone = `(изтрит)` +issues.deleted_project = `(изтрит)` +milestones.edit_subheader = Етапите ви позволяват да управлявате задачите и да проследявате напредъка. +activity.navbar.recent_commits = Скорошни подавания +activity.git_stats_deletion_n = %d изтривания +activity.git_stats_addition_n = %d добавяния +release.draft = Чернова +release.detail = Подробности за изданието +releases.desc = Проследявайте версиите на проекта и изтеглянията. +release.ahead.target = в %s след това издание +release.prerelease = Предварително издание +release.target = Цел +release.new_subheader = Изданията ви позволяват да управлявате версиите на проекта. +release.tag_helper = Изберете съществуващ маркер или създайте нов маркер. +release.tag_helper_new = Нов маркер. Този маркер ще бъде създаден от целта. +release.message = Опишете това издание +release.prerelease_desc = Отбелязване като предварително издание +release.delete_release = Изтриване на изданието +release.delete_tag = Изтриване на маркера +release.edit_release = Обновяване на изданието +diff.committed_by = подадено от +release.downloads = Изтегляния +issues.sign_in_require_desc = Влезте за да се присъедините към това обсъждане. +activity.git_stats_push_to_all_branches = към всички клони. +release.deletion_tag_success = Маркерът е изтрит. +release.cancel = Отказ +release.deletion = Изтриване на изданието +release.download_count = Изтегляния: %s +release.tag_name_invalid = Името на маркера не е валидно. +diff.stats_desc = %d променени файла с %d добавяния и %d изтривания +release.tag_name_already_exist = Вече съществува издание с това име на маркер. +branch.branch_already_exists = Клонът "%s" вече съществува в това хранилище. +diff.download_patch = Изтегляне на файл-кръпка +diff.show_diff_stats = Показване на статистика +diff.commit = подаване +diff.download_diff = Изтегляне на файл-разлики +diff.whitespace_show_everything = Показване на всички промени +diff.show_split_view = Разделен изглед +diff.show_unified_view = Обединен изглед +issues.review.self.approval = Не можете да одобрите собствената си заявка за сливане. +fork_repo = Разклоняване на хранилището +pulls.merged = Слети +issues.push_commits_n = добави %d подавания %s +pulls.num_conflicting_files_n = %d конфликтни файла +issues.push_commit_1 = добави %d подаване %s +fork_visibility_helper = Видимостта на разклонено хранилище не може да бъде променена. +language_other = Други +stars_remove_warning = Това ще премахне всички звезди от това хранилище. +tree_path_not_found_tag = Пътят %[1]s не съществува в маркер %[2]s +tree_path_not_found_commit = Пътят %[1]s не съществува в подаване %[2]s +tree_path_not_found_branch = Пътят %[1]s не съществува в клон %[2]s +transfer.accept = Приемане на прехвърлянето +transfer.reject = Отхвърляне на прехвърлянето +archive.issue.nocomment = Това хранилище е архивирано. Не можете да коментирате в задачите. +forked_from = разклонено от +issues.delete_branch_at = `изтри клон %s %s` +pulls.has_viewed_file = Прегледано +pulls.viewed_files_label = %[1]d / %[2]d прегледани файла +pulls.approve_count_n = %d одобрения +activity.git_stats_commit_1 = %d подаване +activity.git_stats_deletion_1 = %d изтриване +diff.review.approve = Одобряване +diff.review.comment = Коментиране +issues.stop_tracking = Спиране на таймера +issues.stop_tracking_history = `спря работа %s` +issues.cancel_tracking = Отхвърляне +issues.add_time = Ръчно добавяне на време +issues.start_tracking_history = `започна работа %s` +issues.start_tracking_short = Пускане на таймера +issues.review.approve = одобри тези промени %s +pulls.tab_conversation = Обсъждане +pulls.close = Затваряне на заявката за сливане +issues.add_time_short = Добавяне на време +issues.add_time_hours = Часове +issues.add_time_minutes = Минути +issues.add_time_cancel = Отказ +pulls.tab_commits = Подавания +pulls.tab_files = Променени файлове +pulls.approve_count_1 = %d одобрение +pulls.can_auto_merge_desc = Тази заявка за сливане може да бъде слята автоматично. +pulls.num_conflicting_files_1 = %d конфликтен файл +activity.git_stats_commit_n = %d подавания +settings.event_issues = Задачи +branch.delete_head = Изтриване +branch.delete = Изтриване на клона "%s" +branch.delete_html = Изтриване на клона +tag.create_success = Маркерът "%s" е създаден. +branch.new_branch_from = Създаване на нов клон от "%s" +branch.new_branch = Създаване на нов клон +branch.confirm_rename_branch = Преименуване на клона +branch.create_from = от "%s" +settings.add_team_duplicate = Екипът вече разполага с това хранилище +settings.slack_domain = Домейн +editor.directory_is_a_file = Името на директорията "%s" вече се използва като име на файл в това хранилище. +editor.filename_is_a_directory = Името на файла "%s" вече се използва като име на директория в това хранилище. +editor.file_editing_no_longer_exists = Файлът, който се редактира, "%s", вече не съществува в това хранилище. +editor.file_deleting_no_longer_exists = Файлът, който се изтрива, "%s", вече не съществува в това хранилище. +editor.unable_to_upload_files = Неуспешно качване на файлове в "%s" с грешка: %v +settings.web_hook_name_slack = Slack +settings.web_hook_name_discord = Discord +settings.web_hook_name_telegram = Telegram +settings.web_hook_name_matrix = Matrix +settings.web_hook_name_gogs = Gogs +settings.web_hook_name_feishu_or_larksuite = Feishu / Lark Suite +settings.web_hook_name_feishu = Feishu +settings.web_hook_name_larksuite = Lark Suite +settings.web_hook_name_wechatwork = WeCom (Wechat Work) +settings.web_hook_name_packagist = Packagist +diff.file_byte_size = Размер +branch.create_success = Клонът "%s" е създаден. +branch.deletion_success = Клонът "%s" е изтрит. +branch.deletion_failed = Неуспешно изтриване на клон "%s". +branch.rename_branch_to = Преименуване от "%s" на: +settings.web_hook_name_msteams = Microsoft Teams +settings.web_hook_name_dingtalk = DingTalk +issues.error_removing_due_date = Неуспешно премахване на крайния срок. +branch.renamed = Клонът %s е преименуван на %s. +settings.teams = Екипи +settings.add_team = Добавяне на екип +settings.web_hook_name_gitea = Gitea +settings.web_hook_name_forgejo = Forgejo +release.tag_already_exist = Вече съществува маркер с това име. +branch.name = Име на клона +settings.rename_branch = Преименуване на клона +branch.restore_failed = Неуспешно възстановяване на клон "%s". +branch.download = Изтегляне на клона "%s" +branch.rename = Преименуване на клона "%s" [modal] confirm = Потвърждаване @@ -834,10 +1017,23 @@ follow_blocked_user = Не можете да следвате тази орга settings.delete_prompt = Организацията ще бъде премахната завинаги. Това НЕ МОЖЕ да бъде отменено! settings.labels_desc = Добавете етикети, които могат да се използват за задачи за всички хранилища в тази организация. teams.none_access = Без достъп -teams.members.none = Няма участници в този екип. +teams.members.none = Няма членове в този екип. repo_updated = Обновено teams.delete_team_success = Екипът е изтрит. teams.search_repo_placeholder = Потърсете хранилище… +teams.delete_team_title = Изтриване на екипа +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.delete_team = Изтриване на екипа +members.owner = Притежател +members.member_role = Роля на участника: +members.member = Участник +members.private_helper = да е видим [install] admin_password = Парола @@ -896,9 +1092,10 @@ register_notify = Добре дошли във Forgejo issue.action.new = @%[1]s създаде #%[2]d. issue.action.review = @%[1]s коментира в тази заявка за сливане. issue.action.reopen = @%[1]s отвори наново #%[2]d. +issue.action.approve = @%[1]s одобри тази заявка за сливане. [user] -joined_on = Присъединен на %s +joined_on = Присъединени на %s user_bio = Биография repositories = Хранилища activity = Публична дейност @@ -977,6 +1174,42 @@ users.deletion_success = Потребителският акаунт бе изт last_page = Последна config.test_email_placeholder = Ел. поща (напр. test@example.com) users.cannot_delete_self = Не можете да изтриете себе си +repos.owner = Притежател +auths.domain = Домейн +auths.host = Хост +auths.port = Порт +auths.type = Тип +config.ssh_config = SSH Конфигурация +monitor.stats = Статистика +monitor.queue = Опашка: %s +config = Конфигурация +config.mailer_user = Потребител +config.enable_captcha = Включване на CAPTCHA +repos.size = Размер +auths.enabled = Включено +config.git_config = Git Конфигурация +config.mailer_protocol = Протокол +users.bot = Бот +config.db_path = Път +monitor.queues = Опашки +config.server_config = Сървърна конфигурация +packages.size = Размер +settings = Админ. настройки +users = Потребителски акаунти +emails.duplicate_active = Този адрес на ел. поща вече е активен за друг потребител. +config.app_ver = Версия на Forgejo +config.custom_conf = Път на конфигурационния файл +config.git_version = Версия на Git +config.lfs_config = LFS Конфигурация +config.db_ssl_mode = SSL +users.admin = Админ +auths.name = Име +repos.issues = Задачи +packages.owner = Притежател +packages.creator = Създател +packages.type = Тип +orgs.teams = Екипи +orgs.members = Участници [error] not_found = Целта не може да бъде намерена. @@ -1006,6 +1239,10 @@ team_not_exist = Екипът не съществува. TeamName = Име на екипа email_error = ` не е валиден адрес на ел. поща.` email_invalid = Адресът на ел. поща е невалиден. +SSHTitle = Име на SSH ключ +repo_name_been_taken = Името на хранилището вече е използвано. +team_name_been_taken = Името на екипа вече е заето. +org_name_been_taken = Името на организацията вече е заето. [action] close_issue = `затвори задача %[3]s#%[2]s` @@ -1024,9 +1261,13 @@ comment_pull = `коментира в заявка за сливане %[3]s#%[2]s` auto_merge_pull_request = `сля автоматично заявка за сливане %[3]s#%[2]s` watched_repo = започна да наблюдава %[2]s -delete_tag = изтри таг %[2]s от %[3]s +delete_tag = изтри маркер %[2]s от %[3]s delete_branch = изтри клон %[2]s от %[3]s create_branch = създаде клон %[3]s на %[4]s +publish_release = `публикува издание "%[4]s" на %[3]s` +push_tag = изтласка маркер %[3]s към %[4]s +approve_pull_request = `одобри %[3]s#%[2]s` +reject_pull_request = `предложи промени за %[3]s#%[2]s` [auth] login_openid = OpenID @@ -1080,6 +1321,7 @@ read = Прочетени watching = Наблюдавани no_unread = Няма непрочетени известия. mark_all_as_read = Отбелязване на всички като прочетени +pin = Закачване на известието [explore] code_search_results = Резултати от търсенето на "%s" @@ -1099,6 +1341,18 @@ runners.version = Версия variables = Променливи runners.labels = Етикети actions = Действия +variables.none = Все още няма променливи. +variables.creation.failed = Неуспешно добавяне на променлива. +variables.update.failed = Неуспешно редактиране на променлива. +variables.creation.success = Променливата "%s" е добавена. +variables.deletion.success = Променливата е премахната. +variables.edit = Редактиране на променливата +variables.deletion = Премахване на променливата +variables.update.success = Променливата е редактирана. +variables.creation = Добавяне на променлива +variables.deletion.failed = Неуспешно премахване на променлива. +runners.task_list.repository = Хранилище +runners.description = Описание [heatmap] less = По-малко @@ -1116,4 +1370,16 @@ submodule = Подмодул [dropzone] -default_message = Пуснете файлове тук или щракнете, за качване. \ No newline at end of file +default_message = Пуснете файлове тук или щракнете, за качване. +remove_file = Премахване на файла +file_too_big = Размерът на файла ({{filesize}} MB) надвишава максималния размер от ({{maxFilesize}} MB). +invalid_input_type = Не можете да качвате файлове от този тип. + +[graphs] +component_loading_failed = Неуспешно зареждане на %s +contributors.what = приноси +recent_commits.what = скорошни подавания +component_loading = Зареждане на %s... + +[projects] +type-1.display_name = Индивидуален проект \ No newline at end of file diff --git a/options/locale/locale_cs-CZ.ini b/options/locale/locale_cs-CZ.ini index fdb91f987f..be798000f5 100644 --- a/options/locale/locale_cs-CZ.ini +++ b/options/locale/locale_cs-CZ.ini @@ -18,7 +18,7 @@ template=Šablona language=Jazyk notifications=Oznámení active_stopwatch=Aktivní sledování času -tracked_time_summary=Shrnutí sledovaného času na základě filtrů v seznamu úkolů +tracked_time_summary=Shrnutí sledovaného času na základě filtrů v seznamu problémů create_new=Vytvořit… user_profile_and_more=Profily a nastavení… signed_in_as=Přihlášen jako @@ -31,7 +31,7 @@ username=Uživatelské jméno email=E-mailová adresa password=Heslo access_token=Přístupový token -re_type=Potvrdit heslo +re_type=Potvrzení hesla captcha=CAPTCHA twofa=Dvoufaktorové ověřování twofa_scratch=Dvoufaktorový pomocný kód @@ -142,6 +142,8 @@ confirm_delete_selected=Potvrdit odstranění všech vybraných položek? name=Název value=Hodnota sign_in_with_provider = Přihlásit se přes %s +confirm_delete_artifact = Opravdu chcete odstranit artefakt „%s“? +toggle_menu = Přepnout nabídku [aria] navbar=Navigační lišta @@ -182,13 +184,14 @@ missing_csrf=Špatný požadavek: Neexistuje CSRF token invalid_csrf=Špatný požadavek: Neplatný CSRF token not_found=Cíl nebyl nalezen. network_error=Chyba sítě +server_internal = Interní chyba serveru [startpage] app_desc=Snadno přístupný vlastní Git install=Jednoduchá na instalaci install_desc=Jednoduše spusťte jako binární program pro vaši platformu, nasaďte jej pomocí Docker, nebo jej stáhněte jako balíček. platform=Multiplatformní -platform_desc=Forgejo běží všude, kde Go může kompilovat: Windows, macOS, Linux, ARM, atd. Vyberte si ten, který milujete! +platform_desc=Forgejo běží na všech platformách, na které může kompilovat jazyk Go: Windows, macOS, Linux, ARM, atd. Výběr je opravdu velký! lightweight=Lehká lightweight_desc=Forgejo má minimální požadavky a může běžet na Raspberry Pi. Šetřete energii vašeho stroje! license=Open Source @@ -278,13 +281,13 @@ admin_password=Heslo confirm_password=Potvrdit heslo admin_email=E-mailová adresa install_btn_confirm=Nainstalovat Forgejo -test_git_failed=Chyba při testu příkazu 'git': %v -sqlite3_not_available=Tato verze Forgejo nepodporuje SQLite3. Stáhněte si oficiální binární verzi od %s (nikoli verzi „gobuild“). +test_git_failed=Chyba při testu příkazu „git“: %v +sqlite3_not_available=Tato verze Forgejo nepodporuje SQLite3. Stáhněte si oficiální binární verzi z %s (nikoli verzi „gobuild“). invalid_db_setting=Nastavení databáze je neplatné: %v invalid_db_table=Databázová tabulka „%s“ je neplatná: %v invalid_repo_path=Kořenový adresář repozitářů není správný: %v invalid_app_data_path=Cesta k datům aplikace je neplatná: %v -run_user_not_match=`"Run as" uživatelské jméno není aktuální uživatelské jméno: %s -> %s` +run_user_not_match=Uživatelské jméno v poli „Spustit jako“ není aktuální uživatelské jméno: %s -> %s internal_token_failed=Nepodařilo se vytvořit interní token: %v secret_key_failed=Nepodařilo se vytvořit tajný klíč: %v save_config_failed=Uložení konfigurace se nezdařilo: %v @@ -297,7 +300,7 @@ default_allow_create_organization_popup=Povolit novým uživatelským účtům v default_enable_timetracking=Povolit sledování času ve výchozím nastavení default_enable_timetracking_popup=Povolí sledování času pro nové repozitáře. no_reply_address=Skrytá e-mailová doména -no_reply_address_helper=Název domény pro uživatele se skrytou e-mailovou adresou. Příklad: Pokud je název skryté e-mailové domény nastaven na „noreply.example.org“, uživatelské jméno „joe“ bude zaznamenáno v Gitu jako „joe@noreply.example.org“. +no_reply_address_helper=Název domény pro uživatele se skrytou e-mailovou adresou. Příklad: pokud je název skryté e-mailové domény nastaven na „noreply.example.org“, uživatelské jméno „joe“ bude zaznamenáno v Gitu jako „joe@noreply.example.org“. password_algorithm=Hash algoritmus hesla invalid_password_algorithm=Neplatný algoritmus hash hesla password_algorithm_helper=Nastavte algoritmus hashování hesla. Algoritmy mají odlišné požadavky a sílu. Algoritmus argon2 je poměrně bezpečný, ale používá spoustu paměti a může být nevhodný pro malé systémy. @@ -305,6 +308,9 @@ enable_update_checker=Povolit kontrolu aktualizací enable_update_checker_helper=Kontroluje vydání nových verzí pravidelně připojením ke gitea.io. env_config_keys=Konfigurace prostředí env_config_keys_prompt=Následující proměnné prostředí budou také použity pro váš konfigurační soubor: +enable_update_checker_helper_forgejo = Pravidelně kontroluje nové verze Forgejo kontrolou DNS TXT záznamu na adrese release.forgejo.org. +allow_dots_in_usernames = Povolit uživatelům používat tečky ve svých uživatelských jménech. Neovlivní stávající účty. +smtp_from_invalid = Adresa v poli „Poslat e-mail jako“ je neplatná [home] uname_holder=Uživatelské jméno nebo e-mailová adresa @@ -425,11 +431,15 @@ authorization_failed_desc=Autorizace selhala, protože jsme detekovali neplatný sspi_auth_failed=SSPI autentizace selhala password_pwned=Heslo, které jste zvolili, je na seznamu odcizených hesel, která byla dříve odhalena při narušení veřejných dat. Zkuste to prosím znovu s jiným heslem. password_pwned_err=Nelze dokončit požadavek na HaveIBeenPwned +change_unconfirmed_email = Pokud jste při registraci zadali nesprávnou e-mailovou adresu, můžete ji změnit níže. Potvrzovací e-mail bude místo toho odeslán na novou adresu. +change_unconfirmed_email_error = Nepodařilo se změnit e-mailovou adresu: %v +change_unconfirmed_email_summary = Změna e-mailové adresy, na kterou bude odeslán aktivační e-mail. +last_admin = Nemůžete odebrat posledního administrátora. Vždy musí existovat alespoň jeden administrátor. [mail] view_it_on=Zobrazit na %s reply=nebo přímo odpovědět na tento e-mail -link_not_working_do_paste=Nefunguje? Zkuste jej zkopírovat a vložit do svého prohlížeče. +link_not_working_do_paste=Odkaz nefunguje? Zkuste jej zkopírovat a vložit do adresního řádku svého prohlížeče. hi_user_x=Ahoj %s, activate_account=Prosíme, aktivujte si váš účet @@ -444,12 +454,12 @@ activate_email.text=Pro aktivaci vašeho účtu do %s klikněte na násle register_notify=Vítejte v Forgejo register_notify.title=%[1]s vítejte v %[2]s register_notify.text_1=toto je váš potvrzovací e-mail pro %s! -register_notify.text_2=Nyní se můžete přihlásit přes uživatelské jméno: %s. -register_notify.text_3=Pokud pro vás byl vytvořen tento účet, nejprve nastavte své heslo. +register_notify.text_2=Do svého účtu se můžete přihlásit svým uživatelským jménem: %s +register_notify.text_3=Pokud vám tento účet vytvořil někdo jiný, musíte si nejprve nastavit své heslo. reset_password=Obnovit váš účet -reset_password.title=%s, požádal jste o obnovení vašeho účtu -reset_password.text=Klikněte prosím na následující odkaz pro obnovení vašeho účtu v rámci %s: +reset_password.title=Uživateli %s, obdrželi jsme žádost o obnovu vašeho účtu +reset_password.text=Pokud jste to byli vy, klikněte na následující odkaz pro obnovení vašeho účtu do %s: register_success=Registrace byla úspěšná @@ -491,6 +501,9 @@ team_invite.subject=%[1]s vás pozval/a, abyste se připojili k organizaci %[2]s team_invite.text_1=%[1]s vás pozval/a do týmu %[2]s v organizaci %[3]s. team_invite.text_2=Pro připojení k týmu klikněte na následující odkaz: team_invite.text_3=Poznámka: Tato pozvánka byla určena pro %[1]s. Pokud jste neočekávali tuto pozvánku, můžete tento e-mail ignorovat. +admin.new_user.user_info = Informace o uživateli +admin.new_user.text = Klikněte sem pro správu tohoto uživatele z administrátorského panelu. +admin.new_user.subject = Právě se zaregistroval nový uživatel %s [modal] yes=Ano @@ -523,8 +536,8 @@ SSPISeparatorReplacement=Oddělovač SSPIDefaultLanguage=Výchozí jazyk require_error=` nemůže být prázdný.` -alpha_dash_error=` by měl obsahovat pouze alfanumerické znaky, pomlčku („-“) a podtržítka („_“). ` -alpha_dash_dot_error=` by měl obsahovat pouze alfanumerické znaky, pomlčku („-“), podtržítka („_“) nebo tečku („.“). ` +alpha_dash_error=` by měl obsahovat pouze alfanumerické znaky, pomlčky („-“) a podtržítka („_“). ` +alpha_dash_dot_error=` by měl obsahovat pouze alfanumerické znaky, pomlčky („-“), podtržítka („_“) nebo tečky („.“). ` git_ref_name_error=` musí být správný název odkazu Git.` size_error=` musí být minimálně velikosti %s.` min_size_error=` musí obsahovat nejméně %s znaků.` @@ -534,7 +547,7 @@ url_error=`„%s“ není platná adresa URL.` include_error=` musí obsahovat substring „%s“.` glob_pattern_error=`zástupný vzor je neplatný: %s.` regex_pattern_error=` regex vzor je neplatný: %s.` -username_error=` může obsahovat pouze alfanumerické znaky („0-9“, „a-z“, „A-Z“), pomlčku („-“), podtržítka („_“) a tečka („.“). Nemůže začínat nebo končit nealfanumerickými znaky a po sobě jdoucí nealfanumerické znaky jsou také zakázány.` +username_error=` může obsahovat pouze alfanumerické znaky („0-9“, „a-z“, „A-Z“), pomlčky („-“), podtržítka („_“) a tečky („.“). Nemůže začínat nebo končit nealfanumerickými znaky. Jsou také zakázány po sobě jdoucí nealfanumerické znaky.` invalid_group_team_map_error=` mapování je neplatné: %s` unknown_error=Neznámá chyba: captcha_incorrect=CAPTCHA kód není správný. @@ -570,7 +583,7 @@ enterred_invalid_owner_name=Nové jméno vlastníka není správné. enterred_invalid_password=Zadané heslo není správné. user_not_exist=Tento uživatel neexistuje. team_not_exist=Tento tým neexistuje. -last_org_owner=Nemůžete odstranit posledního uživatele z týmu „vlastníci“. Musí existovat alespoň jeden vlastník pro organizaci. +last_org_owner=Nemůžete odebrat posledního uživatele z týmu „vlastníci“. Organizace musí obsahovat alespoň jednoho vlastníka. cannot_add_org_to_team=Organizace nemůže být přidána jako člen týmu. duplicate_invite_to_team=Uživatel byl již pozván jako člen týmu. organization_leave_success=Úspěšně jste opustili organizaci %s. @@ -579,16 +592,18 @@ invalid_ssh_key=Nelze ověřit váš SSH klíč: %s invalid_gpg_key=Nelze ověřit váš GPG klíč: %s invalid_ssh_principal=Neplatný SSH Principal certifikát: %s must_use_public_key=Zadaný klíč je soukromý klíč. Nenahrávejte svůj soukromý klíč nikde. Místo toho použijte váš veřejný klíč. -unable_verify_ssh_key=Nelze ověřit váš SSH klíč. +unable_verify_ssh_key=Nepodařilo se ověřit klíč SSH, zkontrolujte, zda neobsahuje chyby. auth_failed=Ověření selhalo: %v -still_own_repo=Váš účet vlastní jeden nebo více repozitářů. Nejprve je smažte nebo převeďte. +still_own_repo=Váš účet vlastní jeden nebo více repozitářů. Nejprve je odstraňte nebo přesuňte. still_has_org=Váš účet je členem jedné nebo více organizací. Nejdříve je musíte opustit. still_own_packages=Váš účet vlastní jeden nebo více balíčků. Nejprve je musíte odstranit. -org_still_own_repo=Organizace stále vlastní jeden nebo více repozitářů. Nejdříve je smažte nebo převeďte. -org_still_own_packages=Organizace stále vlastní jeden nebo více balíčků. Nejdříve je smažte. +org_still_own_repo=Organizace stále vlastní jeden nebo více repozitářů. Nejdříve je odstraňte nebo přesuňte. +org_still_own_packages=Organizace stále vlastní jeden nebo více balíčků. Nejdříve je odstraňte. target_branch_not_exist=Cílová větev neexistuje. +admin_cannot_delete_self = Nemůžete odstranit sami sebe, když jste administrátorem. Nejprve prosím odeberte svá práva administrátora. +username_error_no_dots = ` může obsahovat pouze alfanumerické znaky („0-9“, „a-z“, „A-Z“), pomlčky („-“) a podtržítka („_“). Nemůže začínat nebo končit nealfanumerickými znaky. Jsou také zakázány po sobě jdoucí nealfanumerické znaky.` [user] @@ -615,6 +630,14 @@ settings=Uživatelská nastavení form.name_reserved=Uživatelské jméno „%s“ je rezervováno. form.name_pattern_not_allowed=Vzor „%s“ není povolen v uživatelském jméně. form.name_chars_not_allowed=Uživatelské jméno „%s“ obsahuje neplatné znaky. +block_user = Zablokovat uživatele +block_user.detail = Pokud zablokujete tohoto uživatele, budou provedeny i další akce. Například: +block_user.detail_1 = Tento uživatel vás nebude moci sledovat. +block_user.detail_2 = Tento uživatel nebude moci interagovat s vašimi repozitáři, vytvářet problémy a komentáře. +block_user.detail_3 = Tento uživatel vás nebude moci přidat jako spolupracovníka a naopak. +follow_blocked_user = Tohoto uživatele nemůžete sledovat, protože jste si jej zablokovali nebo si on zablokoval vás. +block = Zablokovat +unblock = Odblokovat [settings] profile=Profil @@ -658,7 +681,7 @@ language=Jazyk ui=Motiv vzhledu hidden_comment_types=Skryté typy komentářů hidden_comment_types_description=Zde zkontrolované typy komentářů nebudou zobrazeny na stránkách problémů. Zaškrtnutí „Štítek“ například odstraní všechny komentáře „ přidal/odstranil zu %s %s geändert` issues.remove_ref_at=`hat die Referenz %s entfernt %s` issues.add_ref_at=`hat die Referenz %s hinzugefügt %s` -issues.delete_branch_at=`löschte die Branch %s %s` +issues.delete_branch_at=`löschte den Branch %s %s` issues.filter_label=Label issues.filter_label_exclude=`Alt + Klick/Enter verwenden, um Labels auszuschließen` issues.filter_label_no_select=Alle Labels @@ -1497,7 +1499,7 @@ issues.draft_title=Entwurf issues.num_comments_1=%d Kommentar issues.num_comments=%d Kommentare issues.commented_at=`hat %s kommentiert` -issues.delete_comment_confirm=Bist du sicher dass du diesen Kommentar löschen möchtest? +issues.delete_comment_confirm=Bist du sicher, dass du diesen Kommentar löschen möchtest? issues.context.copy_link=Link kopieren issues.context.quote_reply=Antwort zitieren issues.context.reference_issue=In neuem Issue referenzieren @@ -1617,7 +1619,7 @@ issues.add_time_sum_to_small=Es wurde keine Zeit eingegeben. issues.time_spent_total=Zeitaufwand insgesamt issues.time_spent_from_all_authors=`Aufgewendete Zeit: %s` issues.due_date=Fällig am -issues.invalid_due_date_format=Das Fälligkeitsdatum muss das Format ‚JJJJ-MM-TT‘ haben. +issues.invalid_due_date_format=Das Fälligkeitsdatum muss das Format „JJJJ-MM-TT“ haben. issues.error_modifying_due_date=Fehler beim Ändern des Fälligkeitsdatums. issues.error_removing_due_date=Fehler beim Entfernen des Fälligkeitsdatums. issues.push_commit_1=hat %d Commit %s hinzugefügt @@ -1744,7 +1746,7 @@ pulls.tab_conversation=Diskussion pulls.tab_commits=Commits pulls.tab_files=Geänderte Dateien pulls.reopen_to_merge=Bitte diesen Pull-Request wieder öffnen, um zu mergen. -pulls.cant_reopen_deleted_branch=Dieser Pull-Request kann nicht wieder geöffnet werden, da die Branch bereits gelöscht wurde. +pulls.cant_reopen_deleted_branch=Dieser Pull-Request kann nicht wieder geöffnet werden, da der Branch bereits gelöscht wurde. pulls.merged=Zusammengeführt pulls.merged_success=Pull-Request erfolgreich gemerged und geschlossen pulls.closed=Pull-Request geschlossen @@ -1754,8 +1756,8 @@ pulls.is_closed=Der Pull-Request wurde geschlossen. pulls.title_wip_desc=`Beginne den Titel mit %s, um zu verhindern, dass der Pull-Request versehentlich gemergt wird.` pulls.cannot_merge_work_in_progress=Dieser Pull Request ist als „Work in Progress“ (in Bearbeitung) markiert. pulls.still_in_progress=Noch in Bearbeitung? -pulls.add_prefix=%s Präfix hinzufügen -pulls.remove_prefix=%s Präfix entfernen +pulls.add_prefix=Präfix „%s“ hinzufügen +pulls.remove_prefix=Präfix „%s” entfernen pulls.data_broken=Dieser Pull-Requests ist kaputt, da Fork-Informationen gelöscht wurden. pulls.files_conflicted=Dieser Pull-Request hat Änderungen, die im Widerspruch zum Ziel-Branch stehen. pulls.is_checking=Die Merge-Konfliktprüfung läuft noch. Bitte aktualisiere die Seite in wenigen Augenblicken. @@ -1781,7 +1783,7 @@ 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.wrong_commit_id=die Commit ID muss eine Commit ID auf dem Zielbranch sein +pulls.wrong_commit_id=die Commit-ID muss eine Commit-ID auf dem Zielbranch sein pulls.no_merge_desc=Dieser Pull-Request kann nicht gemergt werden, da alle Repository-Merge-Optionen deaktiviert sind. pulls.no_merge_helper=Aktiviere Mergeoptionen in den Repositoryeinstellungen oder merge den Pull-Request manuell. @@ -1819,8 +1821,8 @@ pulls.status_checks_details=Details pulls.update_branch=Branch durch Mergen aktualisieren pulls.update_branch_rebase=Branch durch Rebase aktualisieren pulls.update_branch_success=Branch-Aktualisierung erfolgreich -pulls.update_not_allowed=Du hast keine Berechtigung, die Branch zu Updaten -pulls.outdated_with_base_branch=Dieser Branch enthält nicht die neusten Commits der Basis-Branch +pulls.update_not_allowed=Du hast keine Berechtigung, den Branch zu updaten +pulls.outdated_with_base_branch=Dieser Branch enthält nicht die neusten Commits des Basis-Branches pulls.close=Pull-Request schließen pulls.closed_at=`hat diesen Pull-Request %[2]s geschlossen` pulls.reopened_at=`hat diesen Pull-Request %[2]s wieder geöffnet` @@ -1964,7 +1966,7 @@ activity.title.releases_1=%d Release activity.title.releases_n=%d Releases activity.title.releases_published_by=%s von %s veröffentlicht activity.published_release_label=Veröffentlicht -activity.no_git_activity=In diesem Zeitraum sind keine Commit-Aktivität vorhanden. +activity.no_git_activity=In diesem Zeitraum hat es keine Commit-Aktivität gegeben. activity.git_stats_exclude_merges=Merges ausgenommen, activity.git_stats_author_1=%d Autor activity.git_stats_author_n=%d Autoren @@ -2288,7 +2290,7 @@ settings.title=Titel settings.deploy_key_content=Inhalt settings.key_been_used=Ein Deploy-Key mit identischem Inhalt wird bereits verwendet. settings.key_name_used=Ein Deploy-Key mit diesem Namen existiert bereits. -settings.add_key_success=Der Deploy-Key "%s" wurde erfolgreich hinzugefügt. +settings.add_key_success=Der Deploy-Key „%s“ wurde erfolgreich hinzugefügt. settings.deploy_key_deletion=Deploy-Key löschen settings.deploy_key_deletion_desc=Nach dem Löschen wird dieser Deploy-Key keinen Zugriff mehr auf dieses Repository haben. Fortfahren? settings.deploy_key_deletion_success=Der Deploy-Key wurde entfernt. @@ -2305,18 +2307,18 @@ settings.protect_this_branch_desc=Verhindert das Löschen und schränkt Git auf settings.protect_disable_push=Push deaktivieren settings.protect_disable_push_desc=Kein Push auf diesen Branch erlauben. settings.protect_enable_push=Push aktivieren -settings.protect_enable_push_desc=Jeder, der Schreibzugriff hat, darf in diesen Branch Pushen (aber kein Force-Push). +settings.protect_enable_push_desc=Jeder, der Schreibzugriff hat, darf in diesen Branch pushen (aber kein Force-Push). settings.protect_enable_merge=Merge aktivieren settings.protect_enable_merge_desc=Jeder mit Schreibzugriff darf die Pull-Requests in diesen Branch mergen. settings.protect_whitelist_committers=Schütze gewhitelistete Commiter settings.protect_whitelist_committers_desc=Jeder, der auf der Whitelist steht, darf in diesen Branch pushen (aber kein Force-Push). settings.protect_whitelist_deploy_keys=Deploy-Schlüssel mit Schreibzugriff zum Pushen whitelisten. settings.protect_whitelist_users=Nutzer, die pushen dürfen: -settings.protect_whitelist_search_users=Benutzer suchen… +settings.protect_whitelist_search_users=Benutzer suchen … settings.protect_whitelist_teams=Teams, die pushen dürfen: -settings.protect_whitelist_search_teams=Teams suchen… +settings.protect_whitelist_search_teams=Teams suchen … settings.protect_merge_whitelist_committers=Merge-Whitelist aktivieren -settings.protect_merge_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist Pull-Requests in diesen Branch zu mergen. +settings.protect_merge_whitelist_committers_desc=Erlaube Nutzern oder Teams auf der Whitelist, Pull-Requests in diesen Branch zu mergen. settings.protect_merge_whitelist_users=Nutzer, die mergen dürfen: settings.protect_merge_whitelist_teams=Teams, die mergen dürfen: settings.protect_check_status_contexts=Statusprüfungen aktivieren @@ -2340,14 +2342,14 @@ settings.require_signed_commits_desc=Pushes auf diesen Branch ablehnen, wenn Com settings.protect_branch_name_pattern=Muster für geschützte Branchnamen settings.protect_patterns=Muster settings.protect_protected_file_patterns=Geschützte Dateimuster (durch Semikolon „;“ getrennt): -settings.protect_protected_file_patterns_desc=Geschützte Dateien dürfen nicht direkt geändert werden, auch wenn der Benutzer Rechte hat, Dateien in diesem Branch hinzuzufügen, zu bearbeiten oder zu löschen. Mehrere Muster können mit Semikolon (';') getrennt werden. Siehe github.com/gobwas/glob Dokumentation zur Mustersyntax. Beispiele: .drone.yml, /docs/**/*.txt. +settings.protect_protected_file_patterns_desc=Geschützte Dateien dürfen nicht direkt geändert werden, auch wenn der Benutzer Rechte hat, Dateien in diesem Branch hinzuzufügen, zu bearbeiten oder zu löschen. Mehrere Muster können mit Semikolon („;“) getrennt werden. Siehe github.com/gobwas/glob Dokumentation zur Mustersyntax. Beispiele: .drone.yml, /docs/**/*.txt. settings.protect_unprotected_file_patterns=Ungeschützte Dateimuster (durch Semikolon „;“ getrennt): -settings.protect_unprotected_file_patterns_desc=Ungeschützte Dateien, die direkt geändert werden dürfen, wenn der Benutzer Schreibzugriff hat, können die Push-Beschränkung umgehen. Mehrere Muster können mit Semikolon (';') getrennt werden. Siehe github.com/gobwas/glob Dokumentation zur Mustersyntax. Beispiele: .drone.yml, /docs/**/*.txt. +settings.protect_unprotected_file_patterns_desc=Ungeschützte Dateien, die direkt geändert werden dürfen, wenn der Benutzer Schreibzugriff hat, können die Push-Beschränkung umgehen. Mehrere Muster können mit Semikolon („;“) getrennt werden. Siehe github.com/gobwas/glob Dokumentation zur Mustersyntax. Beispiele: .drone.yml, /docs/**/*.txt. settings.add_protected_branch=Schutz aktivieren settings.delete_protected_branch=Schutz deaktivieren -settings.update_protect_branch_success=Branchschutzregel "%s" wurde geändert. -settings.remove_protected_branch_success=Branchschutzregel "%s" wurde deaktiviert. -settings.remove_protected_branch_failed=Entfernen der Branchschutzregel "%s" fehlgeschlagen. +settings.update_protect_branch_success=Branchschutzregel „%s“ wurde aktualisiert. +settings.remove_protected_branch_success=Branchschutzregel „%s“ wurde entfernt. +settings.remove_protected_branch_failed=Entfernen der Branchschutzregel „%s“ fehlgeschlagen. settings.protected_branch_deletion=Branch-Schutz deaktivieren 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=Merge bei abgelehnten Reviews blockieren @@ -2358,16 +2360,16 @@ settings.block_outdated_branch=Merge blockieren, wenn der Pull-Request veraltet settings.block_outdated_branch_desc=Mergen 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: settings.merge_style_desc=Merge-Styles -settings.default_merge_style_desc=Standard Mergeverhalten für Pull Requests: -settings.choose_branch=Branch wählen… +settings.default_merge_style_desc=Standard-Mergeverhalten +settings.choose_branch=Branch wählen … settings.no_protected_branch=Es gibt keine geschützten Branches. settings.edit_protected_branch=Bearbeiten settings.protected_branch_required_rule_name=Regelname erforderlich -settings.protected_branch_duplicate_rule_name=Regelname existiert bereits +settings.protected_branch_duplicate_rule_name=Es existiert bereits eine Regel für dieses Branch-Set settings.protected_branch_required_approvals_min=Die Anzahl der erforderlichen Zustimmungen darf nicht negativ sein. settings.tags=Tags settings.tags.protection=Tag-Schutz -settings.tags.protection.pattern=Tag Muster +settings.tags.protection.pattern=Tag-Muster settings.tags.protection.allowed=Erlaubt settings.tags.protection.allowed.users=Erlaubte Benutzer settings.tags.protection.allowed.teams=Erlaubte Teams @@ -2386,11 +2388,11 @@ settings.archive.header=Dieses Repo archivieren settings.archive.text=Durch das Archivieren wird ein Repo vollständig schreibgeschützt. Es wird vom Dashboard versteckt. Niemand (nicht einmal du!) wird in der Lage sein, neue Commits zu erstellen oder Issues oder Pull-Requests zu öffnen. settings.archive.success=Das Repo wurde erfolgreich archiviert. settings.archive.error=Beim Versuch, das Repository zu archivieren, ist ein Fehler aufgetreten. Weitere Details finden sich im Log. -settings.archive.error_ismirror=Du kannst keinen Repo-Mirror archivieren. +settings.archive.error_ismirror=Du kannst kein gespiegeltes Repo archivieren. settings.archive.branchsettings_unavailable=Branch-Einstellungen sind nicht verfügbar wenn das Repo archiviert ist. settings.archive.tagsettings_unavailable=Tag Einstellungen sind nicht verfügbar, wenn das Repo archiviert wurde. settings.unarchive.button=Archivieren rückgängig machen -settings.unarchive.header=Archivieren dieses Repositories rückgängig machen +settings.unarchive.header=Archivieren dieses Repositorys rückgängig machen settings.unarchive.text=Durch das Aufheben der Archivierung kann das Repo wieder Commits und Pushes sowie neue Issues und Pull-Requests empfangen. settings.unarchive.success=Die Archivierung des Repos wurde erfolgreich wieder rückgängig gemacht. settings.unarchive.error=Beim Versuch, die Archivierung des Repos aufzuheben, ist ein Fehler aufgetreten. Weitere Details finden sich im Log. @@ -2402,7 +2404,7 @@ 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_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_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 settings.lfs_locks=Sperren settings.lfs_invalid_locking_path=Ungültiger Pfad: %s @@ -2413,8 +2415,8 @@ settings.lfs_lock_path=Zu sperrender Dateipfad... settings.lfs_locks_no_locks=Keine Sperren settings.lfs_lock_file_no_exist=Gesperrte Datei existiert nicht im Standard-Branch settings.lfs_force_unlock=Freigabe erzwingen -settings.lfs_pointers.found=%d Blob-Zeiger gefunden - %d assoziiert, %d nicht assoziiert (%d fehlend im Speicher) -settings.lfs_pointers.sha=Blob SHA +settings.lfs_pointers.found=%d Blob-Zeiger gefunden – %d assoziiert, %d nicht assoziiert (%d fehlend im Speicher) +settings.lfs_pointers.sha=Blob-SHA settings.lfs_pointers.oid=OID settings.lfs_pointers.inRepo=Im Repo settings.lfs_pointers.exists=Existiert im Speicher @@ -2461,7 +2463,7 @@ diff.load=Diff laden diff.generated=generiert diff.vendored=vendored diff.comment.add_line_comment=Einzelnen Kommentar hinzufügen -diff.comment.placeholder=Kommentieren... +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 @@ -2533,28 +2535,28 @@ release.releases_for=Releases für %s release.tags_for=Tags für %s branch.name=Branchname -branch.already_exists=Ein Branch mit dem Namen "%s" existiert bereits. +branch.already_exists=Ein Branch mit dem Namen „%s“ existiert bereits. branch.delete_head=Löschen -branch.delete=Branch "%s" löschen +branch.delete=Branch „%s“ löschen branch.delete_html=Branch löschen branch.delete_desc=Das Löschen eines Branches ist permanent. Obwohl der Branch für eine kurze Zeit weiter existieren könnte, kann diese Aktion in den meisten Fällen NICHT rückgängig gemacht werden. Fortfahren? -branch.deletion_success=Branch "%s" wurde gelöscht. -branch.deletion_failed=Branch "%s" konnte nicht gelöscht werden. -branch.delete_branch_has_new_commits=Der Branch "%s" kann nicht gelöscht werden, da seit dem letzten Merge neue Commits hinzugefügt wurden. +branch.deletion_success=Branch „%s“ wurde gelöscht. +branch.deletion_failed=Branch „%s“ konnte nicht gelöscht werden. +branch.delete_branch_has_new_commits=Der Branch „%s“ kann nicht gelöscht werden, da seit dem letzten Merge neue Commits hinzugefügt wurden. branch.create_branch=Erstelle Branch %s -branch.create_from=`von "%s"` -branch.create_success=Branch "%s" wurde erstellt. -branch.branch_already_exists=Branch "%s" existiert bereits in diesem Repository. -branch.branch_name_conflict=Der Branch-Name "%s" steht in Konflikt mit dem bestehenden Branch "%s". -branch.tag_collision=Branch "%s" kann nicht erstellt werden, da in diesem Repository bereits ein Tag mit dem selben Namen existiert. +branch.create_from=`von „%s“` +branch.create_success=Branch „%s“ wurde erstellt. +branch.branch_already_exists=Branch „%s“ existiert bereits in diesem Repository. +branch.branch_name_conflict=Der Branch-Name „%s“ steht in Konflikt mit dem bestehenden Branch „%s“. +branch.tag_collision=Branch „%s“ kann nicht erstellt werden, da in diesem Repository bereits ein Tag mit dem selben Namen existiert. branch.deleted_by=Von %s gelöscht -branch.restore_success=Branch "%s" wurde wiederhergestellt. -branch.restore_failed=Wiederherstellung vom Branch "%s" gescheitert. -branch.protected_deletion_failed=Branch "%s" ist geschützt und kann nicht gelöscht werden. -branch.default_deletion_failed=Branch "%s" kann nicht gelöscht werden, da dieser Branch der Standard-Branch ist. -branch.restore=Branch "%s" wiederherstellen -branch.download=Branch "%s" herunterladen -branch.rename=Branch "%s" umbenennen +branch.restore_success=Branch „%s“ wurde wiederhergestellt. +branch.restore_failed=Wiederherstellung vom Branch „%s“ gescheitert. +branch.protected_deletion_failed=Branch „%s“ ist geschützt und kann nicht gelöscht werden. +branch.default_deletion_failed=Branch „%s“ kann nicht gelöscht werden, da dieser Branch der Standard-Branch ist. +branch.restore=Branch „%s“ wiederherstellen +branch.download=Branch „%s“ herunterladen +branch.rename=Branch „%s“ umbenennen branch.search=Branch suchen branch.included_desc=Dieser Branch ist im Standard-Branch enthalten branch.included=Enthalten @@ -2565,20 +2567,20 @@ branch.rename_branch_to=„%s“ umbenennen in: branch.confirm_rename_branch=Branch umbenennen branch.create_branch_operation=Branch erstellen branch.new_branch=Neue Branch erstellen -branch.new_branch_from=Neuen Branch von "%s" erstellen +branch.new_branch_from=Neuen Branch von „%s“ erstellen branch.renamed=Branch %s wurde in %s umbenannt. tag.create_tag=Tag %s erstellen tag.create_tag_operation=Tag erstellen tag.confirm_create_tag=Tag erstellen -tag.create_tag_from=Neuen Tag von "%s" erstellen +tag.create_tag_from=Neuen Tag von „%s“ erstellen -tag.create_success=Tag "%s" wurde erstellt. +tag.create_success=Tag „%s“ wurde erstellt. topic.manage_topics=Themen verwalten topic.done=Fertig topic.count_prompt=Du kannst nicht mehr als 25 Themen auswählen -topic.format_prompt=Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) und Punkte ('.') enthalten und bis zu 35 Zeichen lang sein. Nur Kleinbuchstaben sind zulässig. +topic.format_prompt=Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) und Punkte („.“) enthalten und bis zu 35 Zeichen lang sein. Nur Kleinbuchstaben sind zulässig. find_file.go_to_file=Datei suchen find_file.no_matching=Keine passende Datei gefunden @@ -2599,9 +2601,9 @@ pulls.reopen_failed.base_branch = Der Pull-Request kann nicht wieder geöffnet w settings.mirror_settings.pushed_repository = Gepushtes Repository settings.add_collaborator_blocked_them = Der Mitarbeiter konnte nicht hinzugefügt werden, weil er den Besitzer des Repositorys blockiert hat. settings.wiki_rename_branch_main = Den Wiki-Branch-Namen normalisieren -settings.enter_repo_name = Gib den Repository-Namen zur Bestätigung ein: +settings.enter_repo_name = Gib den Besitzer- und den Repository-Namen genau wie angezeigt ein: settings.wiki_branch_rename_success = Der Branch-Name des Repository-Wikis wurde erfolgreich normalisiert. -settings.archive.mirrors_unavailable = Mirrors sind nicht verfügbar, wenn das Repo archiviert ist. +settings.archive.mirrors_unavailable = Spiegel sind nicht verfügbar, wenn das Repo archiviert ist. pulls.blocked_by_user = Du kannst keinen Pull-Request in diesem Repository erstellen, weil du vom Repository-Besitzer blockiert wurdest. settings.add_collaborator_blocked_our = Der Mitarbeiter konnte nicht hinzugefügt werden, weil der Repository-Besitzer ihn blockiert hat. issues.blocked_by_user = Du kannst kein Issue in diesem Repository erstellen, weil du vom Repository-Besitzer blockiert wurdest. @@ -2647,6 +2649,10 @@ contributors.contribution_type.filter_label = Art des Beitrags: vendored = Vendored activity.navbar.pulse = Puls pulls.made_using_agit = AGit +settings.confirmation_string = Bestätigungsstring +pulls.agit_explanation = Mittels AGit-Workflow erstellt. AGit erlaubt Mitwirkenden, Änderungen mittels „git push“ vorzuschlagen, ohne einen Fork oder neuen Branch zu erstellen. +activity.navbar.recent_commits = Neueste Commits +activity.navbar.code_frequency = Code-Frequenz [graphs] @@ -2660,7 +2666,7 @@ members=Mitglieder teams=Teams code=Quelltext lower_members=Mitglieder -lower_repositories=Repositories +lower_repositories=Repositorys create_new_team=Neues Team create_team=Team erstellen org_desc=Beschreibung @@ -2673,8 +2679,8 @@ team_permission_desc=Berechtigungen team_unit_desc=Zugriff auf Repositorybereiche erlauben team_unit_disabled=(Deaktiviert) -form.name_reserved=Der Organisationsname "%s" ist reserviert. -form.name_pattern_not_allowed=Das Muster "%s" ist in Organisationsnamen nicht erlaubt. +form.name_reserved=Der Organisationsname „%s“ ist reserviert. +form.name_pattern_not_allowed=Das Muster „%s“ ist in Organisationsnamen nicht erlaubt. form.create_org_not_allowed=Du bist nicht berechtigt, eine Organisation zu erstellen. settings=Einstellungen @@ -2703,9 +2709,9 @@ settings.delete_prompt=Die Organisation wird dauerhaft gelöscht. Dies K settings.confirm_delete_account=Löschen bestätigen settings.delete_org_title=Organisation löschen settings.delete_org_desc=Diese Organisation wird dauerhaft gelöscht. Fortfahren? -settings.hooks_desc=Webhooks hinzufügen, die für alle Repositories dieser Organisation ausgelöst werden. +settings.hooks_desc=Webhooks hinzufügen, die für alle Repositorys dieser Organisation ausgelöst werden. -settings.labels_desc=Labels hinzufügen, die für alle Repositories dieser Organisation genutzt werden können. +settings.labels_desc=Labels hinzufügen, die für alle Repositorys dieser Organisation genutzt werden können. members.membership_visibility=Sichtbarkeit der Mitgliedschaft: members.public=Sichtbar @@ -2725,8 +2731,8 @@ members.invite_now=Jetzt einladen teams.join=Beitreten teams.leave=Verlassen teams.leave.detail=%s verlassen? -teams.can_create_org_repo=Repositories erstellen -teams.can_create_org_repo_helper=Mitglieder können neue Repositories in der Organisation erstellen. Der Ersteller erhält Administrator-Zugriff auf das neue Repository. +teams.can_create_org_repo=Repositorys erstellen +teams.can_create_org_repo_helper=Mitglieder können neue Repositorys in der Organisation erstellen. Der Ersteller erhält Administrator-Zugriff auf das neue Repository. teams.none_access=Kein Zugriff teams.none_access_helper=Teammitglieder haben keinen Zugriff auf diese Einheit. teams.general_access=Allgemeiner Zugriff @@ -2739,7 +2745,7 @@ teams.admin_access=Administratorzugang teams.admin_access_helper=Mitglieder können auf Team-Repositories pushen, von ihnen pullen und Mitwirkende hinzufügen. teams.no_desc=Dieses Team hat keine Beschreibung teams.settings=Einstellungen -teams.owners_permission_desc=Besitzer haben vollen Zugriff auf alle Repositories und Admin-Rechte für diese Organisation. +teams.owners_permission_desc=Besitzer haben vollen Zugriff auf alle Repositorys und Admin-Rechte für diese Organisation. teams.members=Teammitglieder teams.update_settings=Einstellungen aktualisieren teams.delete_team=Team löschen @@ -2749,27 +2755,27 @@ teams.invite_team_member.list=Ausstehende Einladungen teams.delete_team_title=Team löschen teams.delete_team_desc=Das Löschen eines Teams widerruft den Repository-Zugriff für seine Mitglieder. Fortfahren? teams.delete_team_success=Das Team wurde gelöscht. -teams.read_permission_desc=Dieses Team hat Lesezugriff: Mitglieder können Team-Repositories einsehen und klonen. -teams.write_permission_desc=Dieses Team hat Schreibzugriff: Mitglieder können Team-Repositories einsehen und darauf pushen. -teams.admin_permission_desc=Dieses Team hat Adminzugriff: Mitglieder dieses Teams können Team-Repositories ansehen, auf sie pushen und Mitarbeiter hinzufügen. -teams.create_repo_permission_desc=Zusätzlich erteilt dieses Team die Berechtigung Repository erstellen: Mitglieder können neue Repositories in der Organisation erstellen. -teams.repositories=Team-Repositories -teams.search_repo_placeholder=Repository durchsuchen… -teams.remove_all_repos_title=Alle Team-Repositories entfernen -teams.remove_all_repos_desc=Dies entfernt alle Repositories von dem Team. -teams.add_all_repos_title=Alle Repositories hinzufügen -teams.add_all_repos_desc=Dadurch werden alle Repositories der Organisation dem Team hinzugefügt. +teams.read_permission_desc=Dieses Team hat Lesezugriff: Mitglieder können Team-Repositorys einsehen und klonen. +teams.write_permission_desc=Dieses Team hat Schreibzugriff: Mitglieder können Team-Repositorys einsehen und darauf pushen. +teams.admin_permission_desc=Dieses Team hat Adminzugriff: Mitglieder dieses Teams können Team-Repositorys ansehen, auf sie pushen und Mitarbeiter hinzufügen. +teams.create_repo_permission_desc=Zusätzlich erteilt dieses Team die Berechtigung Repository erstellen: Mitglieder können neue Repositorys in der Organisation erstellen. +teams.repositories=Team-Repositorys +teams.search_repo_placeholder=Repository durchsuchen … +teams.remove_all_repos_title=Alle Team-Repositorys entfernen +teams.remove_all_repos_desc=Dies entfernt alle Repositorys von dem Team. +teams.add_all_repos_title=Alle Repositorys hinzufügen +teams.add_all_repos_desc=Dadurch werden alle Repositorys der Organisation dem Team hinzugefügt. teams.add_nonexistent_repo=Das Repository, das du hinzufügen möchtest, existiert nicht. Bitte erstelle es zuerst. teams.add_duplicate_users=Dieser Benutzer ist bereits ein Teammitglied. teams.repos.none=Dieses Team hat Zugang zu keinem Repository. teams.members.none=Keine Mitglieder in diesem Team. -teams.specific_repositories=Bestimmte Repositories -teams.specific_repositories_helper=Mitglieder haben nur Zugriff auf Repositories, die explizit dem Team hinzugefügt wurden. Wenn Du diese Option wählst, werden Repositories, die bereits mit Alle Repositories hinzugefügt wurden, nicht automatisch entfernt. -teams.all_repositories=Alle Repositories -teams.all_repositories_helper=Team hat Zugriff auf alle Repositories. Wenn dies ausgewählt wird, werden alle vorhandenen Repositories zum Team hinzugefügt. -teams.all_repositories_read_permission_desc=Dieses Team gewährt Lese-Zugriff auf Repositories: Mitglieder können Repositories ansehen und klonen. -teams.all_repositories_write_permission_desc=Dieses Team gewährt Schreib-Zugriff auf alle Repositories: Mitglieder können Repositories lesen und auf sie pushen. -teams.all_repositories_admin_permission_desc=Dieses Team gewährt Administrator-Zugriff auf alle Repositories: Mitglieder können Repositories lesen, auf sie pushen und Mitwirkende zu Repositorys hinzufügen. +teams.specific_repositories=Bestimmte Repositorys +teams.specific_repositories_helper=Mitglieder haben nur Zugriff auf Repositorys, die explizit dem Team hinzugefügt wurden. Wenn Du diese Option wählst, werden Repositorys, die bereits mit Alle Repositorys hinzugefügt wurden, nicht automatisch entfernt. +teams.all_repositories=Alle Repositorys +teams.all_repositories_helper=Team hat Zugriff auf alle Repositorys. Wenn dies ausgewählt wird, werden alle vorhandenen Repositorys zum Team hinzugefügt. +teams.all_repositories_read_permission_desc=Dieses Team gewährt Lese-Zugriff auf Repositorys: Mitglieder können Repositorys ansehen und klonen. +teams.all_repositories_write_permission_desc=Dieses Team gewährt Schreib-Zugriff auf alle Repositorys: Mitglieder können Repositorys lesen und auf sie pushen. +teams.all_repositories_admin_permission_desc=Dieses Team gewährt Administrator-Zugriff auf alle Repositorys: Mitglieder können Repositorys lesen, auf sie pushen und Mitwirkende zu Repositorys hinzufügen. teams.invite.title=Du wurdest eingeladen, dem Team %s in der Organisation %s beizutreten. teams.invite.by=Von %s eingeladen teams.invite.description=Bitte klicke auf die folgende Schaltfläche, um dem Team beizutreten. @@ -2781,7 +2787,7 @@ identity_access=Identität & Zugriff users=Benutzerkonten organizations=Organisationen assets=Code-Assets -repositories=Repositories +repositories=Repositorys hooks=Webhooks integrations=Integrationen authentication=Authentifizierungsquellen @@ -2819,20 +2825,20 @@ dashboard.delete_inactive_accounts.started=Löschen aller nicht aktivierten Acco dashboard.delete_repo_archives=Lösche alle Repository-Archive (ZIP, TAR.GZ, …) dashboard.delete_repo_archives.started=Löschen aller Repository-Archive gestartet. dashboard.delete_missing_repos=Alle Repository-Datensätze mit verloren gegangenen Git-Dateien löschen -dashboard.delete_missing_repos.started=Alle Repositories löschen, die den Git-File-Task nicht gestartet haben. +dashboard.delete_missing_repos.started=Alle Repositorys löschen, die den Git-File-Task nicht gestartet haben. dashboard.delete_generated_repository_avatars=Generierte Repository-Avatare löschen dashboard.sync_repo_branches=Fehlende Branches aus den Git-Daten in die Datenbank synchronisieren -dashboard.update_mirrors=Mirrors aktualisieren -dashboard.repo_health_check=Healthchecks für alle Repositories ausführen +dashboard.update_mirrors=Spiegel aktualisieren +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.git_gc_repos=Garbage-Collection für alle Repositories 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 Identitäten. -dashboard.resync_all_hooks=Die „pre-receive“-, „update“- und „post-receive“-Hooks für alle Repositories erneut synchronisieren. -dashboard.reinit_missing_repos=Alle Git-Repositories neu einlesen, für die Einträge existieren +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. +dashboard.resync_all_hooks=Die „pre-receive“-, „update“- und „post-receive“-Hooks für alle Repositorys erneut synchronisieren. +dashboard.reinit_missing_repos=Alle Git-Repositorys neu einlesen, für die Einträge existieren dashboard.sync_external_users=Externe Benutzerdaten synchronisieren dashboard.cleanup_hook_task_table=Hook-Task-Tabelle bereinigen dashboard.cleanup_packages=Veraltete Pakete löschen @@ -2894,7 +2900,7 @@ users.created=Registriert am users.last_login=Letzte Anmeldung users.never_login=Hat sich noch nie eingeloggt users.send_register_notify=Benutzer-Registrierungsbenachrichtigung senden -users.new_success=Der Account "%s" wurde erstellt. +users.new_success=Der Account „%s“ wurde erstellt. users.edit=Bearbeiten users.auth_source=Authentifizierungsquelle users.local=Lokal @@ -2902,15 +2908,15 @@ users.auth_login_name=Anmeldename zur Authentifizierung users.password_helper=Passwort leer lassen, um es nicht zu verändern. users.update_profile_success=Das Benutzerkonto wurde aktualisiert. users.edit_account=Benutzerkonto bearbeiten -users.max_repo_creation=Maximale Anzahl an Repositories +users.max_repo_creation=Maximale Anzahl an Repositorys users.max_repo_creation_desc=(Gib -1 ein, um das globale Standardlimit zu verwenden.) users.is_activated=Account ist aktiviert users.prohibit_login=Anmelden deaktivieren users.is_admin=Ist Administrator users.is_restricted=Ist eingeschränkt users.allow_git_hook=Darf „Git Hooks“ erstellen -users.allow_git_hook_tooltip=Git-Hooks werden mit denselben Benutzer-Rechten ausgeführt, mit denen Forgejo läuft, und haben die gleiche Ebene von Host-Zugriff. Dadurch können Benutzer mit diesen speziellen Git-Hook-Rechten auf alle Forgejo-Repositories sowie auf die von Forgejo verwendete Datenbank zugreifen und diese ändern. Auch das Erhalten von Administratorrechten für Forgejo ist möglich. -users.allow_import_local=Darf lokale Repositories importieren +users.allow_git_hook_tooltip=Git-Hooks werden mit denselben Benutzer-Rechten ausgeführt, mit denen Forgejo läuft, und haben die gleiche Ebene von Host-Zugriff. Dadurch können Benutzer mit diesen speziellen Git-Hook-Rechten auf alle Forgejo-Repositorys sowie auf die von Forgejo verwendete Datenbank zugreifen und diese ändern. Auch das Erhalten von Administratorrechten für Forgejo ist möglich. +users.allow_import_local=Darf lokale Repositorys importieren users.allow_create_organization=Darf Organisationen erstellen users.update_profile=Benutzerkonto aktualisieren users.delete_account=Benutzerkonto löschen @@ -2955,8 +2961,8 @@ orgs.members=Mitglieder orgs.new_orga=Neue Organisation repos.repo_manage_panel=Repositoryverwaltung -repos.unadopted=Nicht übernommene Repositories -repos.unadopted.no_more=Keine weiteren nicht übernommenen Repositories gefunden +repos.unadopted=Nicht übernommene Repositorys +repos.unadopted.no_more=Keine weiteren nicht übernommenen Repositorys gefunden repos.owner=Besitzer repos.name=Name repos.private=Privat @@ -2981,12 +2987,12 @@ packages.size=Größe packages.published=Veröffentlicht defaulthooks=Standard-Webhooks -defaulthooks.desc=Webhooks senden automatisch eine HTTP-POST-Anfrage an einen Server, wenn bestimmte Forgejo-Events ausgelöst werden. Hier definierte Webhooks sind die Standardwerte, die in alle neuen Repositories kopiert werden. Mehr Infos findest du in der Webhooks-Anleitung (auf Englisch). +defaulthooks.desc=Webhooks senden automatisch eine HTTP-POST-Anfrage an einen Server, wenn bestimmte Forgejo-Events ausgelöst werden. Hier definierte Webhooks sind die Standardwerte, die in alle neuen Repositorys kopiert werden. Mehr Infos findest du in der Webhooks-Anleitung (auf Englisch). defaulthooks.add_webhook=Standard-Webhook hinzufügen defaulthooks.update_webhook=Standard-Webhook aktualisieren systemhooks=System-Webhooks -systemhooks.desc=Webhooks senden automatisch HTTP-POST-Anfragen an einen Server, wenn bestimmte Forgejo-Events ausgelöst werden. Hier definierte Webhooks werden auf alle Repositories des Systems übertragen, beachte daher mögliche Performance-Einbrüche. Mehr Infos findest du in der Webhooks-Anleitung (auf Englisch). +systemhooks.desc=Webhooks senden automatisch HTTP-POST-Anfragen an einen Server, wenn bestimmte Forgejo-Events ausgelöst werden. Hier definierte Webhooks werden auf alle Repositorys des Systems übertragen, beachte daher mögliche Performance-Einbrüche. Mehr Infos findest du in der Webhooks-Anleitung (auf Englisch). systemhooks.add_webhook=System-Webhook hinzufügen systemhooks.update_webhook=System-Webhook aktualisieren @@ -3021,10 +3027,10 @@ auths.search_page_size=Seitengröße auths.filter=Benutzerfilter auths.admin_filter=Admin-Filter auths.restricted_filter=Eingeschränkte Filter -auths.restricted_filter_helper=Leer lassen, um keine Benutzer als eingeschränkt festzulegen. Verwende einen Stern ('*'), um alle Benutzer, die nicht dem Admin-Filter entsprechen, als eingeschränkt zu setzen. +auths.restricted_filter_helper=Leer lassen, um keine Benutzer als eingeschränkt festzulegen. Verwende einen Asterisk („*“), um alle Benutzer, die nicht dem Admin-Filter entsprechen, als eingeschränkt zu setzen. auths.verify_group_membership=Gruppenmitgliedschaft in LDAP verifizieren (zum Überspringen leer lassen) auths.group_search_base=Gruppensuche Basisdomainname -auths.group_attribute_list_users=Gruppenattribut, welches die die Benutzerliste enthält +auths.group_attribute_list_users=Gruppenattribut, welches die Benutzerliste enthält auths.user_attribute_in_group=Benutzerattribut in der Gruppenliste auths.map_group_to_team=Ordne LDAP-Gruppen Organisationsteams zu (zum Überspringen leer lassen) auths.map_group_to_team_removal=Benutzer aus synchronisierten Teams entfernen, wenn der Benutzer nicht zur entsprechenden LDAP-Gruppe gehört @@ -3038,7 +3044,7 @@ auths.allowed_domains_helper=Leer lassen, um alle Domains zuzulassen. Trenne meh auths.skip_tls_verify=TLS-Verifikation überspringen auths.force_smtps=SMTPS erzwingen auths.force_smtps_helper=SMTPS wird immer auf Port 465 verwendet. Setze dies, um SMTPS auf anderen Ports zu erzwingen. (Sonst wird STARTTLS auf anderen Ports verwendet, wenn es vom Host unterstützt wird.) -auths.helo_hostname=HELO Hostname +auths.helo_hostname=HELO-Hostname auths.helo_hostname_helper=Mit HELO gesendeter Hostname. Leer lassen, um den aktuellen Hostnamen zu senden. auths.disable_helo=HELO deaktivieren auths.pam_service_name=PAM-Dienstname @@ -3062,9 +3068,9 @@ auths.oauth2_required_claim_name_helper=Setze diesen Namen, damit Nutzer aus die 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_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) +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) auths.oauth2_map_group_to_team_removal=Benutzer aus synchronisierten Teams entfernen, wenn der Benutzer nicht zur entsprechenden Gruppe gehört. auths.enable_auto_register=Automatische Registrierung aktivieren auths.sspi_auto_create_users=Benutzer automatisch anlegen @@ -3072,19 +3078,19 @@ auths.sspi_auto_create_users_helper=Erlaube der SSPI Authentifikationsmethode, a auths.sspi_auto_activate_users=Benutzer automatisch aktivieren auths.sspi_auto_activate_users_helper=Erlaube der SSPI Authentifikationsmethode, automatisch neue Benutzerkonten zu aktivieren auths.sspi_strip_domain_names=Domain vom Nutzernamen entfernen -auths.sspi_strip_domain_names_helper=Falls aktiviert werden Domainnamen bei Loginnamen entfernt (z.B. "DOMAIN\nutzer" und "nutzer@example.ort" werden beide nur "nutzer"). +auths.sspi_strip_domain_names_helper=Falls aktiviert, werden Domainnamen von Loginnamen entfernt (z.B. „DOMAIN\nutzer“ und „nutzer@example.org“ werden beide nur „nutzer“). auths.sspi_separator_replacement=Trennzeichen als Ersatz für \, / und @ -auths.sspi_separator_replacement_helper=Das zu verwendende Trennzeichen um Logon-Namen (zB. \ in "DOMAIN\user") und die Hauptnamen von Benutzern (z. B. das @ in "user@example.org") zu separieren. +auths.sspi_separator_replacement_helper=Das zu verwendende Trennzeichen, um Logon-Namen (z.B. das „\“ in „DOMAIN\nutzer“) und die Hauptnamen von Benutzern (z.B. das „@“ in „nutzer@example.org“) zu trennen. auths.sspi_default_language=Standardsprache für Benutzer -auths.sspi_default_language_helper=Standardsprache für Benutzer, die automatisch mit der SSPI Authentifizierungsmethode erstellt wurden. Leer lassen, wenn du es bevorzugst, dass eine Sprache automatisch erkannt wird. +auths.sspi_default_language_helper=Standardsprache für Benutzer, die automatisch mit der SSPI-Authentifizierungsmethode erstellt wurden. Leer lassen, wenn du es bevorzugst, dass eine Sprache automatisch erkannt wird. auths.tips=Tipps auths.tips.oauth2.general=OAuth2-Authentifizierung auths.tips.oauth2.general.tip=Beim Registrieren einer OAuth2-Anwendung sollte die Callback-URL folgendermaßen lauten: auths.tip.oauth2_provider=OAuth2-Anbieter -auths.tip.bitbucket=Registriere einen neuen OAuth-Consumer unter https://bitbucket.org/account/user//oauth-consumers/new und füge die Berechtigung „Account“ – „Read“ hinzu. +auths.tip.bitbucket=Registriere einen neuen OAuth-Consumer unter https://bitbucket.org/account/user//oauth-consumers/new und füge die Berechtigung „Account“ – „Read“ hinzu auths.tip.nextcloud=Registriere über das "Settings -> Security -> OAuth 2.0 client"-Menü einen neuen "OAuth consumer" auf der Nextcloud-Instanz -auths.tip.dropbox=Erstelle eine neue App auf https://www.dropbox.com/developers/apps. -auths.tip.facebook=Erstelle eine neue Anwendung auf https://developers.facebook.com/apps und füge das Produkt „Facebook Login“ hinzu. +auths.tip.dropbox=Erstelle eine neue App auf https://www.dropbox.com/developers/apps +auths.tip.facebook=Erstelle eine neue Anwendung auf https://developers.facebook.com/apps und füge das Produkt „Facebook Login“ hinzu auths.tip.github=Erstelle unter https://github.com/settings/applications/new eine neue OAuth-Anwendung. auths.tip.gitlab=Erstelle unter https://gitlab.com/profile/applications eine neue Anwendung. auths.tip.google_plus=Du erhältst die OAuth2-Client-Zugangsdaten in der Google-API-Konsole unter https://console.developers.google.com/ @@ -3092,11 +3098,11 @@ auths.tip.openid_connect=Benutze die OpenID-Connect-Discovery-URL (/.wel auths.tip.twitter=Gehe auf https://dev.twitter.com/apps, erstelle eine Anwendung und stelle sicher, dass die Option „Allow this application to be used to Sign in with Twitter“ aktiviert ist auths.tip.discord=Erstelle unter https://discordapp.com/developers/applications/me eine neue Anwendung. auths.tip.gitea=Registriere eine neue OAuth2-Anwendung. Eine Anleitung findest du unter https://docs.gitea.com/development/oauth2-provider/ -auths.tip.yandex=`Erstelle eine neue Anwendung auf https://oauth.yandex.com/client/new. Wähle folgende Berechtigungen aus dem "Yandex.Passport API" Bereich: "Zugriff auf E-Mail-Adresse", "Zugriff auf Benutzeravatar" und "Zugriff auf Benutzername, Vor- und Nachname, Geschlecht"` -auths.tip.mastodon=Gebe eine benutzerdefinierte URL für die Mastodon-Instanz ein, mit der du dich authentifizieren möchtest (oder benutze die standardmäßige) +auths.tip.yandex=`Erstelle eine neue Anwendung auf https://oauth.yandex.com/client/new. Wähle folgende Berechtigungen aus dem Abschnitt „Yandex.Passport API“: „Zugriff auf E-Mail-Adresse“, „Zugriff auf Benutzeravatar“ und „Zugriff auf Benutzername, Vor- und Nachname, Geschlecht“` +auths.tip.mastodon=Gib eine benutzerdefinierte URL für die Mastodon-Instanz ein, mit der du dich authentifizieren möchtest (oder benutze die standardmäßige) auths.edit=Authentifikationsquelle bearbeiten auths.activated=Diese Authentifikationsquelle ist aktiviert -auths.new_success=Die Authentifizierung "%s" wurde hinzugefügt. +auths.new_success=Die Authentifizierung „%s“ wurde hinzugefügt. auths.update_success=Diese Authentifizierungsquelle wurde aktualisiert. auths.update=Authentifizierungsquelle aktualisieren auths.delete=Authentifikationsquelle löschen @@ -3104,7 +3110,7 @@ auths.delete_auth_title=Authentifizierungsquelle löschen auths.delete_auth_desc=Das Löschen einer Authentifizierungsquelle verhindert, dass Benutzer sich darüber anmelden können. Fortfahren? auths.still_in_used=Diese Authentifizierungsquelle wird noch verwendet. Bearbeite oder lösche zuerst alle Benutzer, die diese Authentifizierungsquelle benutzen. auths.deletion_success=Die Authentifizierungsquelle „%s“ wurde gelöscht. -auths.login_source_exist=Die Authentifizierungsquelle "%s" existiert bereits. +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.invalid_openIdConnectAutoDiscoveryURL=Ungültige Auto-Discovery-URL (dies muss eine gültige URL sein, die mit http:// oder https:// beginnt) @@ -3192,13 +3198,13 @@ config.mailer_user=Benutzer config.mailer_use_sendmail=Sendmail benutzen config.mailer_sendmail_path=Sendmail-Pfad config.mailer_sendmail_args=Zusätzliche Argumente für Sendmail -config.mailer_sendmail_timeout=Sendmail Timeout +config.mailer_sendmail_timeout=Sendmail-Timeout config.mailer_use_dummy=Dummy config.test_email_placeholder=E-Mail (z.B. test@example.com) config.send_test_mail=Test-E-Mail senden config.send_test_mail_submit=Senden -config.test_mail_failed=Das Senden der Test-E-Mail an '%s' ist fehlgeschlagen: %v -config.test_mail_sent=Eine Test-E-Mail wurde an "%s" gesendet. +config.test_mail_failed=Das Senden der Test-E-Mail an „%s“ ist fehlgeschlagen: %v +config.test_mail_sent=Eine Test-E-Mail wurde an „%s“ gesendet. config.oauth_config=OAuth-Konfiguration config.oauth_enabled=Aktiviert @@ -3230,7 +3236,7 @@ config.git_max_diff_line_characters=Max. Diff-Zeichen (in einer Zeile) config.git_max_diff_files=Max. Diff-Dateien (Angezeigte) config.git_gc_args=GC-Argumente config.git_migrate_timeout=Zeitlimit für Migration -config.git_mirror_timeout=Zeitlimit für Mirror-Aktualisierung +config.git_mirror_timeout=Zeitlimit für Spiegel-Aktualisierung config.git_clone_timeout=Zeitlimit für Clone config.git_pull_timeout=Zeitlimit für Pull config.git_gc_timeout=Zeitlimit für GC @@ -3326,7 +3332,7 @@ comment_issue=`hat das Issue %[3]s#%[2]s kommentiert` comment_pull=`Pull-Request %[3]s#%[2]s wurde kommentiert` merge_pull_request=`Pull-Request %[3]s#%[2]s wurde zusammengeführt` auto_merge_pull_request=`Pull-Request %[3]s#%[2]s wurde automatisch zusammengeführt` -transfer_repo=hat Repository %s transferiert an %s +transfer_repo=hat Repository %s übertragen zu %s push_tag=Tag %[3]s nach %[4]s wurde gepusht delete_tag=hat Tag %[2]s in %[3]s gelöscht delete_branch=hat Branch %[2]s in %[3]s gelöscht @@ -3335,15 +3341,15 @@ compare_commits=Vergleiche %d Commits compare_commits_general=Commits vergleichen mirror_sync_push=Commits zu %[3]s bei %[4]s wurden von einem Spiegel synchronisiert mirror_sync_create=neue Referenz %[3]s bei %[4]s wurde von einem Spiegel synchronisiert -mirror_sync_delete=hat die Referenz des Mirrors %[2]s in %[3]s synchronisiert und gelöscht -approve_pull_request=`hat %[3]s#%[2]s approved` +mirror_sync_delete=hat die Referenz des Spiegels %[2]s in %[3]s synchronisiert und gelöscht +approve_pull_request=`hat %[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` +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_reason=Grund: create_branch=legte den Branch %[3]s in %[4]s an -starred_repo=markiert %[2]s -watched_repo=beobachtet %[2]s +starred_repo=favorisierte %[2]s +watched_repo=beobachtet ab jetzt %[2]s [tool] now=jetzt @@ -3405,7 +3411,7 @@ error.unit_not_allowed=Du hast keine Berechtigung, um auf diesen Repository-Bere title=Pakete desc=Repository-Pakete verwalten. empty=Noch keine Pakete vorhanden. -empty.documentation=Weitere Informationen zur Paket-Registry findest Du in der Dokumentation. +empty.documentation=Weitere Informationen zur Paket-Registry findest du in der Dokumentation. empty.repo=Hast du ein Paket hochgeladen, das hier nicht angezeigt wird? Gehe zu den Paketeinstellungen und verlinke es mit diesem Repo. registry.documentation=Für weitere Informationen zur %s-Registry, schaue in der Dokumentation nach. filter.type=Typ @@ -3437,20 +3443,20 @@ alpine.registry.info=Wähle $branch und $repository aus der Liste unten. alpine.install=Nutze folgenden Befehl, um das Paket zu installieren: alpine.repository=Repository-Informationen alpine.repository.branches=Branches -alpine.repository.repositories=Repositories +alpine.repository.repositories=Repositorys alpine.repository.architectures=Architekturen cargo.registry=Richte diese Registry in der Cargo-Konfigurationsdatei ein (z.B. ~/.cargo/config.toml): cargo.install=Um das Paket mit Cargo zu installieren, führe den folgenden Befehl aus: -chef.registry=Richte diese Registry in deiner ~/.chef/config.rb Datei ein: +chef.registry=Richte diese Registry in deiner ~/.chef/config.rb-Datei ein: chef.install=Nutze folgenden Befehl, um das Paket zu installieren: -composer.registry=Setze diese Paketverwaltung in deiner ~/.composer/config.json Datei auf: +composer.registry=Setze diese Paketverwaltung in deiner ~/.composer/config.json-Datei auf: composer.install=Nutze folgenden Befehl, um das Paket mit Composer zu installieren: composer.dependencies=Abhängigkeiten composer.dependencies.development=Entwicklungsabhängigkeiten conan.details.repository=Repository conan.registry=Diese Registry über die Kommandozeile einrichten: conan.install=Um das Paket mit Conan zu installieren, führe den folgenden Befehl aus: -conda.registry=Richte diese Registry als Conda-Repository in deiner .condarc Datei ein: +conda.registry=Richte diese Registry als Conda-Repository in deiner .condarc-Datei ein: conda.install=Um das Paket mit Conda zu installieren, führe den folgenden Befehl aus: container.details.type=Container-Image Typ container.details.platform=Plattform @@ -3461,7 +3467,7 @@ container.layers=Container-Image Ebenen container.labels=Labels container.labels.key=Schlüssel container.labels.value=Wert -cran.registry=Richte diese Registry in deiner Rprofile.site Datei ein: +cran.registry=Richte diese Registry in deiner Rprofile.site-Datei ein: cran.install=Nutze folgenden Befehl, um das Paket zu installieren: debian.registry=Diese Registry über die Kommandozeile einrichten: debian.registry.info=Wähle $distribution und $component aus der Liste unten. @@ -3562,7 +3568,7 @@ none=Noch keine Secrets vorhanden. creation=Secret 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.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? @@ -3625,7 +3631,7 @@ runs.all_workflows=Alle Workflows runs.commit=Commit runs.scheduled=Geplant runs.pushed_by=gepusht von -runs.invalid_workflow_helper=Die Workflow-Konfigurationsdatei ist ungültig. Bitte überprüfe Deine Konfigurationsdatei: %s +runs.invalid_workflow_helper=Die Workflow-Konfigurationsdatei ist ungültig. Bitte überprüfe deine Konfigurationsdatei: %s runs.actor=Initiator runs.status=Status runs.actors_no_select=Alle Initiatoren @@ -3634,9 +3640,9 @@ runs.no_results=Keine passenden Ergebnisse gefunden. runs.no_runs=Der Workflow hat noch keine Ausführungen. workflow.disable=Workflow deaktivieren -workflow.disable_success=Workflow '%s' erfolgreich deaktiviert. +workflow.disable_success=Workflow „%s“ erfolgreich deaktiviert. workflow.enable=Workflow aktivieren -workflow.enable_success=Workflow '%s' erfolgreich aktiviert. +workflow.enable_success=Workflow „%s“ erfolgreich aktiviert. workflow.disabled=Workflow ist deaktiviert. need_approval_desc=Um Workflows für den Pull-Request eines Forks auszuführen, ist eine Genehmigung erforderlich. @@ -3661,6 +3667,7 @@ runs.no_workflows = Es gibt noch keine Workflows. runs.no_workflows.documentation = Für weitere Informationen über Forgejo Actions, siehe die Dokumentation. runs.empty_commit_message = (leere Commit-Nachricht) variables.id_not_exist = Variable mit ID %d existiert nicht. +runs.workflow = Workflow [projects] type-1.display_name=Individuelles Projekt @@ -3684,3 +3691,4 @@ component_loading_info = Dies könnte einen Moment dauern … component_failed_to_load = Ein unerwarteter Fehler ist aufgetreten. component_loading = Lade %s … contributors.what = Beiträge +recent_commits.what = neueste Commits diff --git a/options/locale/locale_nl-NL.ini b/options/locale/locale_nl-NL.ini index 919edc0150..4d494a6d8b 100644 --- a/options/locale/locale_nl-NL.ini +++ b/options/locale/locale_nl-NL.ini @@ -142,6 +142,7 @@ pin = Vastpinnen unpin = Ontpinnen remove_label_str = Verwijder punt "%s" confirm_delete_artifact = Weet u zeker dat u het artefact "%s" wilt verwijderen? +toggle_menu = Menu schakelen [aria] navbar = Navigatiebalk @@ -436,7 +437,7 @@ remember_me.compromised = De login-sleutel is niet meer geldig, dit kan wijzen o [mail] view_it_on=Bekijk het op %s -link_not_working_do_paste=Werkt dit niet? Probeer het te kopiëren en te plakken naar uw browser. +link_not_working_do_paste=Werkt de link niet? Kopieer en plak de link dan in de URL-balk van je browser. hi_user_x=Hoi %s, activate_account=Activeer uw account @@ -450,12 +451,12 @@ activate_email.text=Klik op de volgende link om je e-mailadres te bevestigen in register_notify=Welkom bij Forgejo register_notify.title=%[1]s, welkom bij %[2]s register_notify.text_1=dit is uw registratie bevestigingsemail voor %s! -register_notify.text_2=U kunt nu inloggen via de gebruikersnaam: %s. -register_notify.text_3=Als dit account voor u is aangemaakt, kunt u eerst uw wachtwoord instellen. +register_notify.text_2=U kunt zich aanmelden bij uw account met uw gebruikersnaam: %s +register_notify.text_3=Als iemand anders dit account voor u heeft gemaakt, moet u eerst uw wachtwoord instellen. reset_password=Account herstellen -reset_password.title=%s, u heeft verzocht om uw account te herstellen -reset_password.text=Klik op de volgende link om je account te herstellen binnen %s: +reset_password.title=%s, we hebben een verzoek ontvangen om uw account te herstellen +reset_password.text=Als u dit was, klik dan op de volgende link om uw account te herstellen binnen %s: register_success=Registratie succesvol @@ -2649,6 +2650,7 @@ pulls.agit_explanation = Gemaakt met behulp van de AGit workflow. AGit laat bijd settings.confirmation_string = Confirmatie string activity.navbar.code_frequency = Code Frequentie activity.navbar.recent_commits = Recente commits +file_follow = Volg Symlink @@ -3660,6 +3662,7 @@ runs.no_workflows.quick_start = Weet je niet hoe je moet beginnen met Forgejo Ac variables.description = Variabelen worden doorgegeven aan bepaalde acties en kunnen anders niet worden gelezen. runners.delete_runner_success = Runner succesvol verwijderd runs.no_matching_online_runner_helper = Geen overeenkomende online runner met label: %s +runs.workflow = Workflow diff --git a/options/locale/locale_ru-RU.ini b/options/locale/locale_ru-RU.ini index 31b7b5d605..df8ba3ccb8 100644 --- a/options/locale/locale_ru-RU.ini +++ b/options/locale/locale_ru-RU.ini @@ -5,7 +5,7 @@ explore=Обзор help=Помощь logo=Логотип sign_in=Вход -sign_in_with_provider=Войти с помощью %s +sign_in_with_provider=Войти через %s sign_in_or=или sign_out=Выход sign_up=Регистрация @@ -166,7 +166,7 @@ buttons.list.unordered.tooltip=Добавить маркированный сп buttons.list.ordered.tooltip=Добавить нумерованный список buttons.list.task.tooltip=Добавить список заданий buttons.mention.tooltip=Упомянуть пользователя или команду -buttons.ref.tooltip=Сослаться на задачу или запрос на слияние +buttons.ref.tooltip=Сослаться на задачу или запрос слияния buttons.switch_to_legacy.tooltip=Использовать старый редактор buttons.enable_monospace_font=Включить моноширинный шрифт buttons.disable_monospace_font=Выключить моноширинный шрифт @@ -256,7 +256,7 @@ register_confirm=Требовать подтверждение по эл. поч mail_notify=Разрешить почтовые уведомления server_service_title=Сервер и настройки внешних служб offline_mode=Включить локальный режим -offline_mode_popup=Отключить сторонние сети доставки контента и отдавать все ресурсы из их локальных копий. +offline_mode_popup=Отключить сторонние сети доставки контента и передавать все ресурсы из их локальных копий. disable_gravatar=Отключить Gravatar disable_gravatar_popup=Отключить Gravatar и сторонние источники аватаров. Если пользователь не загрузит аватар локально, то по умолчанию будет использоваться стандартный аватар. federated_avatar_lookup=Включить федерированные аватары @@ -436,7 +436,7 @@ change_unconfirmed_email = Если при регистрации был вве [mail] view_it_on=Посмотреть на %s reply=или ответьте на это письмо -link_not_working_do_paste=Не сработало? Попробуйте скопировать ссылку и вставить адресную строку. +link_not_working_do_paste=Ссылка не работает? Попробуйте скопировать и вставить адресную строку. hi_user_x=Привет %s, activate_account=Активация учётной записи @@ -451,16 +451,16 @@ activate_email.text=Для подтверждения эл. почты пере register_notify=Добро пожаловать в Forgejo register_notify.title=%[1]s, добро пожаловать в %[2]s register_notify.text_1=это письмо с вашим подтверждением регистрации в %s! -register_notify.text_2=Теперь вы можете войти, используя логин: %s. -register_notify.text_3=Если эта учётная запись была создана для вас, пожалуйста, сначала установите пароль. +register_notify.text_2=Теперь вы можете войти в учётную запись, используя логин: %s. +register_notify.text_3=Если эта учётная запись создана кем-то для вас, сперва будет необходимо задать пароль. reset_password=Восстановление учётной записи -reset_password.title=%s, вы запросили восстановление вашей учётной записи -reset_password.text=Для восстановления учётной записи перейдите по следующей ссылке в течение %s: +reset_password.title=%s, был получен запрос на восстановление вашей учётной записи +reset_password.text=Если этот запрос ваш, для восстановления учётной записи используйте следующую ссылку в течение %s: register_success=Регистрация прошла успешно -issue_assigned.pull=@%[1]s назначил(а) вам запрос на слияние %[2]s в репозитории %[3]s. +issue_assigned.pull=@%[1]s вы назначены на запрос слияния %[2]s в репозитории %[3]s. issue_assigned.issue=@%[1]s назначил(а) вам задачу %[2]s в репозитории %[3]s. issue.x_mentioned_you=@%s упомянул(а) вас: @@ -470,11 +470,11 @@ issue.action.push_n=@%[1]s отправил(а) %[3]d изменений issue.action.close=@%[1]s закрыл(а) #%[2]d. issue.action.reopen=@%[1]s переоткрыл(а) #%[2]d. issue.action.merge=@%[1]s слил(а) #%[2]d в %[3]s. -issue.action.approve=@%[1]s одобрил(а) этот запрос на слияние. -issue.action.reject=@%[1]s запросил(а) изменения в этом запросе на слияние. -issue.action.review=@%[1]s прокомментировал(а) этот запрос на слияние. -issue.action.review_dismissed=@%[1]s отклонил(а) последний отзыв с %[2]s для этого запроса на слияние. -issue.action.ready_for_review=@%[1]s отметил(а) этот запрос на слияние как готовый к рассмотрению. +issue.action.approve=@%[1]s одобрение этого слияния. +issue.action.reject=@%[1]s запрос изменений в этом запросе на слияние. +issue.action.review=@%[1]s комментарий для этого запроса на слияние. +issue.action.review_dismissed=@%[1]s отклонён последний отзыв от %[2]s для этого запроса на слияние. +issue.action.ready_for_review=@%[1]s запрос на слияние отмечен как готовый к рецензии. issue.action.new=@%[1]s создал(а) #%[2]d. issue.in_tree_path=В %s: @@ -491,7 +491,7 @@ repo.transfer.subject_to_you=%s хочет передать вам «%s» repo.transfer.to_you=вам repo.transfer.body=Чтобы принять или отклонить передачу, перейдите по ссылке %s или просто проигнорируйте этот запрос. -repo.collaborator.added.subject=%s добавил(а) вас в %s +repo.collaborator.added.subject=%s вы добавлены в %s repo.collaborator.added.text=Вы были добавлены в качестве соучастника репозитория: team_invite.subject=%[1]s приглашает вас присоединиться к организации %[2]s @@ -605,7 +605,7 @@ username_error_no_dots = ` может состоять только из лат [user] change_avatar=Изменить свой аватар… -joined_on=Зарегистрирован(а) с %s +joined_on=Регистрация %s repositories=Репозитории activity=Публичная активность followers=Подписчики @@ -835,7 +835,7 @@ manage_access_token=Управление токенами generate_new_token=Создать новый токен tokens_desc=Эти токены предоставляют доступ к вашей учётной записи с помощью Forgejo API. token_name=Имя токена -generate_token=Генерировать токен +generate_token=Создать токен generate_token_success=Новый токен создан. Скопируйте и сохраните его сейчас, так как он не будет показан снова. generate_token_name_duplicate=%s уже использовалось в качестве имени приложения. Пожалуйста, используйте другое имя. delete_token=Удалить @@ -1133,9 +1133,9 @@ fork_from_self=Вы не можете форкнуть ваш собственн fork_guest_user=Войдите, чтобы форкнуть репозиторий. watch_guest_user=Войдите, чтобы отслеживать этот репозиторий. star_guest_user=Войдите, чтобы добавить в избранное этот репозиторий. -unwatch=Прекратить отслеживание +unwatch=Не отслеживать watch=Отслеживать -unstar=Убрать из избранного +unstar=Убр. из избранного star=В избранное fork=Форкнуть download_archive=Скачать репозиторий @@ -1169,7 +1169,7 @@ org_labels_desc=Метки уровня организации, которые org_labels_desc_manage=управлять milestones=Этапы -commits=коммитов +commits=коммиты commit=коммит release=Выпуск releases=Выпуски @@ -1282,7 +1282,7 @@ editor.cherry_pick=Перенести изменения %s в: editor.revert=Откатить %s к: commits.desc=Просмотр истории изменений исходного кода. -commits.commits=Коммитов +commits.commits=коммиты commits.no_commits=Нет общих коммитов. «%s» и «%s» имеют совершенно разные истории. commits.nothing_to_compare=Эти ветки одинаковы. commits.search=Поиск коммитов… @@ -1401,13 +1401,13 @@ issues.label_templates.info=Меток пока нет. Создайте нов issues.label_templates.helper=Выберите метку issues.label_templates.use=Использовать набор меток issues.label_templates.fail_to_load_file=Не удалось загрузить файл шаблона меток «%s»: %v -issues.add_label=добавил(а) метку %s %s -issues.add_labels=добавил(а) метки %s %s +issues.add_label=добавлена метка %s %s +issues.add_labels=добавлены метки %s %s issues.remove_label=удалил(а) метку %s %s issues.remove_labels=удалил(а) метки %s %s -issues.add_remove_labels=добавил(а) метки %s и удалил(а) %s %s -issues.add_milestone_at=`добавил(а) к этапу %s %s` -issues.add_project_at=`добавил(а) в %s проект %s` +issues.add_remove_labels=добавлены метки %s и убраны метки %s %s +issues.add_milestone_at=`добавлено в этап %s %s` +issues.add_project_at=`добавлено в проект %s %s` issues.change_milestone_at=`изменил(а) целевой этап с %s на %s %s` issues.change_project_at=`изменил(а) проект с %s на %s %s` issues.remove_milestone_at=`удалил(а) это из этапа %s %s` @@ -1421,7 +1421,7 @@ issues.remove_self_assignment=`убрал(а) их назначение %s` issues.change_title_at=`изменил(а) заголовок с %s на %s %s` issues.change_ref_at=`изменил(а) ссылку с %s на %s %s` issues.remove_ref_at=`убрал(а) ссылку %s %s` -issues.add_ref_at=`добавил(а) ссылку %s %s` +issues.add_ref_at=`добавлена ссылка %s %s` issues.delete_branch_at=`удалил(а) ветку %s %s` issues.filter_label=Метка issues.filter_label_exclude=`Используйте alt + click/enter, чтобы исключить метки` @@ -1500,11 +1500,11 @@ issues.reopen_comment_issue=Прокомментировать и открыть issues.create_comment=Комментировать issues.closed_at=`закрыл(а) эту задачу %[2]s` issues.reopened_at=`переоткрыл(а) эту проблему %[2]s` -issues.commit_ref_at=`сослался на эту задачу в коммите %[2]s` -issues.ref_issue_from=`сослался на эту задачу %[4]s %[2]s` -issues.ref_pull_from=`сослался(ась) на этот запрос слияния %[4]s %[2]s` -issues.ref_closing_from=`сослался(ась) на запрос слияния %[4]s, который закроет эту задачу %[2]s` -issues.ref_reopening_from=`сослался(ась) на запрос слияния %[4]s, который повторно откроет эту задачу %[2]s` +issues.commit_ref_at=`упоминание этой задачи в коммите %[2]s` +issues.ref_issue_from=`упоминание этой задачи %[4]s %[2]s` +issues.ref_pull_from=`упоминание этого запроса слияния %[4]s %[2]s` +issues.ref_closing_from=`упоминание запроса слияния %[4]s, закрывающего эту задачу %[2]s` +issues.ref_reopening_from=`упоминание запроса слияния %[4]s, повторно открывающего эту задачу %[2]s` issues.ref_closed_from=`закрыл этот запрос %[4]s %[2]s` issues.ref_reopened_from=`переоткрыл эту задачу %[4]s %[2]s` issues.ref_from=`из %[1]s` @@ -1595,7 +1595,7 @@ issues.add_time=Вручную добавить время issues.del_time=Удалить этот журнал времени issues.add_time_short=Добавить время issues.add_time_cancel=Отмена -issues.add_time_history=`добавил(а) к затраченному времени %s` +issues.add_time_history=`добавлено затраченное время %s` issues.del_time_history=`удалил(а) потраченное время %s` issues.add_time_hours=Часы issues.add_time_minutes=Минуты @@ -1606,8 +1606,8 @@ issues.due_date=Срок выполнения issues.invalid_due_date_format=Дата окончания должна быть в формате «гггг-мм-дд». issues.error_modifying_due_date=Не удалось изменить срок выполнения. issues.error_removing_due_date=Не удалось убрать срок выполнения. -issues.push_commit_1=добавил(а) %d коммит %s -issues.push_commits_n=добавил(а) %d коммитов %s +issues.push_commit_1=добавлен %d коммит %s +issues.push_commits_n=добавлены %d коммита(ов) %s issues.force_push_codes=`форсировал(а) отправку изменений %[1]s %[4]s вместо %[2]s %[6]s` issues.force_push_compare=Сравнить issues.due_date_form=гггг-мм-дд @@ -1615,7 +1615,7 @@ issues.due_date_form_add=Добавить срок выполнения issues.due_date_form_edit=Редактировать issues.due_date_form_remove=Удалить issues.due_date_not_set=Срок выполнения не установлен. -issues.due_date_added=добавил(а) срок выполнения %s %s +issues.due_date_added=добавлен срок выполнения %s %s issues.due_date_modified=изменил(а) срок выполнения с %[2]s на %[1]s %[3]s issues.due_date_remove=убрал(а) срок выполнения %s %s issues.due_date_overdue=Просроченные @@ -1630,7 +1630,7 @@ issues.dependency.add=Добавить зависимость… issues.dependency.cancel=Отменить issues.dependency.remove=Удалить issues.dependency.remove_info=Удалить эту зависимость -issues.dependency.added_dependency=`добавил(а) новую зависимость %s` +issues.dependency.added_dependency=`добавлена новая зависимость %s` issues.dependency.removed_dependency=`убрал(а) зависимость %s` issues.dependency.pr_closing_blockedby=Закрытие этого запроса на слияние блокируется следующими задачами issues.dependency.issue_closing_blockedby=Закрытие этой задачи блокируется следующими задачами @@ -1915,8 +1915,8 @@ activity.opened_prs_count_1=Новый запрос на слияние activity.opened_prs_count_n=Новых запросов на слияние activity.title.user_1=%d пользователем activity.title.user_n=%d пользователями -activity.title.prs_1=%d запрос на слияние -activity.title.prs_n=%d запросов на слияние +activity.title.prs_1=%d запрос слияния +activity.title.prs_n=%d запросы слияний activity.title.prs_merged_by=%s приняты %s activity.title.prs_opened_by=%s предложены %s activity.merged_prs_label=Принято @@ -2150,7 +2150,7 @@ settings.webhook.response=Ответ settings.webhook.headers=Заголовки settings.webhook.payload=Содержимое settings.webhook.body=Тело ответа -settings.webhook.replay.description=Повторить этот веб-хук. +settings.webhook.replay.description=Повторить отправку. settings.webhook.delivery.success=Событие было добавлено в очередь доставки. Может пройти несколько секунд, прежде чем оно отобразится в истории. settings.githooks_desc=Git-хуки предоставляются самим Git. Вы можете изменять файлы хуков из списка ниже, чтобы настроить собственные операции. settings.githook_edit_desc=Если хук не активен, будет подставлен пример содержимого. Пустое значение в этом поле приведёт к отключению хука. @@ -2372,7 +2372,7 @@ settings.lfs_findcommits=Найти коммиты settings.lfs_lfs_file_no_commits=Для этого LFS файла не найдено коммитов settings.lfs_noattribute=Этот путь не имеет блокируемого атрибута в ветке по умолчанию settings.lfs_delete=Удалить файл LFS с OID %s -settings.lfs_delete_warning=Удаление файла LFS может привести к ошибкам 'объект не существует' при проверке. Вы уверены? +settings.lfs_delete_warning=Удаление файла LFS может привести к ошибкам «объект не существует» при проверке. Вы уверены? settings.lfs_findpointerfiles=Найти файлы указателя settings.lfs_locks=Заблокировать settings.lfs_invalid_locking_path=Недопустимый путь: %s @@ -2621,24 +2621,28 @@ settings.wiki_globally_editable = Разрешить редактировани settings.webhook.test_delivery_desc_disabled = Активируйте этот веб-хук для проверки тестовым событием. commits.browse_further = Смотреть далее vendored = Vendored -settings.units.add_more = Добавить больше... +settings.units.add_more = Доб. больше... pulls.fast_forward_only_merge_pull_request = Только fast-forward settings.units.overview = Обзор settings.units.units = Разделы репозитория pulls.reopen_failed.head_branch = Этот запрос на слияние не может быть открыт заново, потому что головная ветка больше не существует. pulls.reopen_failed.base_branch = Этот запрос на слияние не может быть открыт заново, потому что базовая ветка больше не существует. settings.ignore_stale_approvals = Игнорировать устаревшие одобрения -contributors.contribution_type.commits = Коммиты +contributors.contribution_type.commits = коммиты contributors.contribution_type.additions = Добавления contributors.contribution_type.deletions = Удаления contributors.contribution_type.filter_label = Тип участия: -pulls.commit_ref_at = `сослался(ась) на этот запрос слияния из комммита %[2]s` +pulls.commit_ref_at = `упоминание этого запроса слияния в коммите %[2]s` settings.thread_id = ИД обсуждения pulls.made_using_agit = AGit activity.navbar.contributors = Соавторы -activity.navbar.code_frequency = Частота кода +activity.navbar.code_frequency = Частота изменений activity.navbar.recent_commits = Недавние коммиты settings.confirmation_string = Подтверждение +settings.archive.text = Архивация репозитория сделает всё его содержимое доступным только для чтения. Он будет скрыт с домашнего экрана. Никто (включая вас!) не сможет добавлять коммиты, открывать задачи и запросы слияний. +release.deletion_desc = Удаление выпуска удаляет его только в Forgejo. Это действие не затронет тег в git, содержимое репозитория и его историю. Продолжить? +pulls.agit_explanation = Создано через рабочий поток AGit. С ним можно предлагать изменения, используя команду «git push», без необходимости в создании форка или новой ветки. +settings.webhook.replay.description_disabled = Активируйте веб-хук для повторения отправки. [graphs] @@ -3296,6 +3300,7 @@ dashboard.sync_branch.started = Начата синхронизация вето dashboard.sync_repo_tags = Синхронизировать теги из git в базу данных self_check.database_collation_mismatch = Ожидается, что БД использует сопоставление: %s self_check = Самопроверка +dashboard.rebuild_issue_indexer = Пересобрать индексатор задач [action] @@ -3328,7 +3333,7 @@ publish_release=`выпустил(а) "%[4]s" в %[3]s#%[2]s` review_dismissed_reason=Причина: create_branch=создал(а) ветку %[3]s в %[4]s -starred_repo=добавил(а) %[2]s в избранное +starred_repo=добавлено %[2]s в избранное watched_repo=начала(а) наблюдение за %[2]s [tool] @@ -3615,7 +3620,7 @@ runs.actor=Актор runs.status=Статус runs.actors_no_select=Все акторы runs.no_results=Ничего не найдено. -runs.no_workflows=Пока нет рабочих процессов. +runs.no_workflows=Пока нет рабочих потоков. runs.no_runs=Рабочий поток ещё не запускался. runs.empty_commit_message=(пустое сообщение коммита) @@ -3644,6 +3649,7 @@ variables.update.success=Переменная изменена. variables.id_not_exist = Переменная с идентификатором %d не существует. runs.no_workflows.quick_start = Не знаете, как начать использовать Действия Forgejo? Читайте руководство по быстрому старту. runs.no_workflows.documentation = Чтобы узнать больше о Действиях Forgejo, читайте документацию. +runs.workflow = Рабочий поток [projects] type-1.display_name=Индивидуальный проект @@ -3667,3 +3673,5 @@ component_failed_to_load = Случилась непредвиденная ош contributors.what = соучастие component_loading = Загрузка %s... component_loading_info = Это займёт некоторое время… +code_frequency.what = частота изменений +recent_commits.what = недавние коммиты diff --git a/options/locale/locale_sl.ini b/options/locale/locale_sl.ini index a64954c2cb..d6047eae3c 100644 --- a/options/locale/locale_sl.ini +++ b/options/locale/locale_sl.ini @@ -119,6 +119,8 @@ remove = Odstrani remove_all = Odstrani vse remove_label_str = Odstranite element "%s" confirm_delete_artifact = Ste prepričani, da želite izbrisati artefakt "%s"? +concept_code_repository = Repozitorij +error404 = Stran, ki jo poskušate doseči, ne obstaja ali niste pooblaščeni za njen ogled. [install] reinstall_confirm_check_3 = Potrjujete, da ste popolnoma prepričani, da se ta program Forgejo izvaja s pravilno lokacijo app.ini, in da ste prepričani, da ga morate znova namestiti. Potrjujete, da se zavedate zgoraj navedenih tveganj. @@ -230,11 +232,23 @@ env_config_keys_prompt = V konfiguracijski datoteki bodo uporabljene tudi nasled [admin] users.allow_git_hook_tooltip = Kljuke Git se izvajajo kot uporabnik operacijskega sistema, v katerem je nameščen program Forgejo, in imajo enako raven dostopa do gostitelja. Uporabniki s tem posebnim privilegijem Git Hook lahko dostopajo do vseh skladišč Forgejo in spreminjajo vse zbirke Forgejo ter podatkovno bazo, ki jo uporablja Forgejo. Posledično lahko pridobijo tudi skrbniške privilegije Forgejo. +auths.force_smtps_helper = SMTPS se vedno uporablja na vratih 465. Če želite, da se SMTPS uporablja tudi na drugih vratih, to nastavite. (V nasprotnem primeru se bo STARTTLS uporabljal na drugih vratih, če ga gostitelj podpira.) +self_check.database_fix_mysql = Uporabniki MySQL/MariaDB lahko za odpravo težav s kollacijo uporabite ukaz "gitea doctor convert", lahko pa težavo odpravite tudi z ukazom "ALTER ... COLLATE ..." SQL ročno. +users.purge_help = Prisilno izbrišite uporabnika in vsa skladišča, organizacije in pakete, ki so v njegovi lasti. Izbrisani bodo tudi vsi komentarji in vprašanja, ki jih je objavil ta uporabnik. +auths.sspi_default_language_helper = Privzet jezik za uporabnike, samodejno ustvarjene z metodo avtentikacije SSPI. Pustite prazno, če želite, da se jezik zazna samodejno. +auths.restricted_filter_helper = Pustite prazno, če ne želite nastaviti nobenega uporabnika kot omejenega. Uporabite zvezdico ("*"), če želite vse uporabnike, ki se ne ujemajo z administratorskim filtrom, nastaviti kot omejene. +auths.tip.twitter = Pojdite na https://dev.twitter.com/apps, ustvarite aplikacijo in preverite, ali je omogočena možnost "Allow this application to be used to Sign in with Twitter" [repo] migrate.github_token_desc = Tu lahko vstavite enega ali več žetonov, ločenih z vejico, da bo selitev hitrejša zaradi omejitve hitrosti GitHub API. OPOZORILO: Zloraba te funkcije lahko krši pravila ponudnika storitev in povzroči blokado računa. ambiguous_runes_description = `Ta datoteka vsebuje znake Unicode, ki bi jih lahko zamenjali z drugimi znaki. Če menite, da je to namerno, lahko to opozorilo mirno prezrete. Za njihovo razkritje uporabite gumb Escape.` invisible_runes_description = `Ta datoteka vsebuje nevidne znake Unicode, ki jih ljudje ne razlikujejo, vendar jih lahko računalnik obdela drugače. Če menite, da je to namerno, lahko to opozorilo mirno prezrete. Za njihovo razkritje uporabite gumb Escape.` +branch.delete_desc = Brisanje veje je trajno. Čeprav lahko izbrisana veja še nekaj časa obstaja, preden je dejansko odstranjena, je v večini primerov NI mogoče preklicati. Nadaljujte? +issues.delete.text = Ali res želite izbrisati to vprašanje? (S tem bo trajno odstranjena vsa vsebina. Če jo nameravate ohraniti v arhivu, jo raje zaprite.) +settings.githook_edit_desc = Če kavelj ni aktiven, se prikaže vzorčna vsebina. Če pustite vsebino prazno, bo ta kavelj onemogočen. +editor.file_changed_while_editing = Vsebina datoteke se je od začetka urejanja spremenila. Klikni tukaj, da si jih ogledaš, ali Sprejemi spremembe znova, da jih prepišeš. +settings.webhook.delivery.success = Dogodek je bil dodan v čakalno vrsto za dostavo. Lahko traja nekaj sekund, preden se prikaže v zgodovini dostave. +editor.filename_help = Dodajte imenik tako, da vnesete njegovo ime, ki mu sledi poševnica ("/"). Imenik odstranite tako, da na začetku vnosnega polja vtipkate backspace. [editor] buttons.list.ordered.tooltip = Dodajte oštevilčen seznam @@ -259,6 +273,20 @@ footer = Stopka [settings] oauth2_application_locked = Forgejo ob zagonu predhodno registrira nekatere aplikacije OAuth2, če je to omogočeno v konfiguraciji. Da bi preprečili nepričakovano obnašanje, jih ni mogoče niti urejati niti odstraniti. Za več informacij glejte dokumentacijo OAuth2. +profile = Profil +account = Račun +appearance = Videz +password = Geslo +authorized_oauth2_applications_description = Tem aplikacijam tretjih oseb ste odobrili dostop do svojega osebnega računa Forgejo. Prosimo, da prekličete dostop do aplikacij, ki jih ne uporabljate več. +social_desc = S temi družabnimi računi se lahko prijavite v svoj račun. Prepričajte se, da jih vse prepoznate. +access_token_desc = Izbrana dovoljenja žetona omejujejo avtorizacijo samo na ustrezne poti API. Za več informacij preberite dokumentacijo. +oauth2_client_secret_hint = Skrivnost se ne bo več prikazala, ko zapustite ali osvežite to stran. Prepričajte se, da ste jo shranili. +twofa_desc = Za zaščito računa pred krajo gesla lahko uporabite pametni telefon ali drugo napravo za prejemanje časovno omejenih enkratnih gesel ("TOTP"). +twofa_recovery_tip = Če napravo izgubite, boste lahko z obnovitvenim ključem za enkratno uporabo ponovno pridobili dostop do računa. +twofa_scratch_token_regenerated = Vaš obnovitveni ključ za enkratno uporabo je zdaj %s. Shranite ga na varnem mestu, saj ga ne boste več videli. +regenerate_scratch_token_desc = Če ste izgubili obnovitveni ključ ali ste ga že uporabili za prijavo, ga lahko ponastavite tukaj. +twofa_enrolled = Vaš račun je bil uspešno vpisan. Ključ za obnovitev za enkratno uporabo (%s) shranite na varno, saj ga ne boste več videli. +can_not_add_email_activations_pending = Aktivacija je v teku, poskusite znova čez nekaj minut, če želite dodati nov e-poštni naslov. [heatmap] less = Manj @@ -408,7 +436,7 @@ issues.in_your_repos = V vašem repozitorijev release.title = Naslov: %s release.downloads = Prenosi: activate_account.text_2 = Za aktivacijo računa v %s kliknite naslednjo povezavo: -link_not_working_do_paste = Ne deluje? Poskusite ga kopirati in prilepiti v brskalnik. +link_not_working_do_paste = Ali povezava ne deluje? Poskusite jo kopirati in prilepiti v vrstico URL brskalnika. issue.action.reopen = @%[1]s ponovno odprl #%[2]d. repo.transfer.body = Če ga želite sprejeti ali zavrniti, obiščite %s ali ga preprosto prezrite. team_invite.text_2 = Če se želite pridružiti ekipi, kliknite naslednjo povezavo: @@ -422,14 +450,14 @@ admin.new_user.user_info = Informacije o uporabniku admin.new_user.text = Prosimo, da klikni tukaj za upravljanje tega uporabnika iz upraviteljske plošče. register_notify = Dobrodošli v Forgejo register_notify.title = %[1]s, dobrodošli v %[2]s -register_notify.text_2 = Zdaj se lahko prijavite z uporabniškim imenom: %s. -register_notify.text_3 = Če je bil ta račun ustvarjen za vas, prosimo, da najprej nastavite svoje geslo. +register_notify.text_2 = V svoj račun se lahko prijavite z uporabniškim imenom: %s +register_notify.text_3 = Če je ta račun namesto vas ustvaril nekdo drug, boste morali najprej nastaviti svoje geslo. reset_password = Obnovite svoj račun -reset_password.title = %s, zahtevali ste obnovitev računa +reset_password.title = %s, prejeli smo zahtevo za izterjavo vašega računa register_success = Registracija je bila uspešna issue.x_mentioned_you = @%s vas je omenil: issue.action.close = @%[1]s zaprl #%[2]d. -reset_password.text = Za obnovitev računa v %s kliknite naslednjo povezavo: +reset_password.text = Če se je to zgodilo vam, kliknite naslednjo povezavo in obnovite račun v %s: release.note = Opomba: release.download.zip = Izvorna koda (ZIP) release.download.targz = Izvorna koda (TAR.GZ) @@ -446,15 +474,102 @@ activate_email.text = Kliknite naslednjo povezavo, da preverite svoj e-poštni n register_notify.text_1 = to je vaše e-poštno sporočilo s potrditvijo registracije za %s! issue_assigned.pull = @%[1]s vam je dodelil zahtevo za poteg %[2]s v skladišču %[3]s. issue_assigned.issue = @%[1]s vam je dodelil izdajo %[2]s v skladišču %[3]s. -issue.action.force_push = %[1]s sila-potisnila%[2]s < /b > iz %[3]s v %[4]s. +issue.action.force_push = %[1]s orce je potisnil %[2]s od%[3]s to %[4]s. +release.new.subject = %s v %s sproščeno +release.new.text = @%[1]s izdal %[2]s v %[3]s +repo.transfer.subject_to = %s želi prenesti "%s" na %s +repo.transfer.subject_to_you = %s želi prenesti "%s" na vas +repo.collaborator.added.text = Dodani ste kot sodelavec repozitorija: [modal] confirm = Potrdite no = Ne cancel = Prekliči modify = Posodobitev +yes = Da [form] UserName = Uporabniško ime Password = Geslo -Retype = Potrditev gesla \ No newline at end of file +Retype = Potrditev gesla +team_name_been_taken = The name of the organisation is already taken. +password_complexity = Geslo ne izpolnjuje zahtev glede kompleksnosti: +enterred_invalid_org_name = Ime organizacije, ki ste ga vnesli, je napačno. +organization_leave_success = Uspešno ste zapustili organizacijo %s. +admin_cannot_delete_self = Ko ste administrator, se ne morete izbrisati. Najprej odstranite svoje pravice upravitelja. +RepoName = Ime repozitorija +Email = E-poštni naslov +SSHTitle = Ime ključa SSH +PayloadUrl = URL koristnega tovora +TeamName = Ime ekipe +AuthName = Ime avtorizacije +Content = Vsebina +SSPISeparatorReplacement = Ločevalnik +SSPIDefaultLanguage = Privzet jezik +captcha_incorrect = Koda CAPTCHA je napačna. +password_not_match = Gesla se ne ujemajo. +lang_select_error = S seznama izberite jezik. +username_been_taken = Uporabniško ime je že zasedeno. +username_change_not_local_user = Nedomovnim uporabnikom ni dovoljeno spreminjati uporabniškega imena. +username_has_not_been_changed = Uporabniško ime ni bilo spremenjeno +visit_rate_limit = Obisk na daljavo je obravnaval omejitev hitrosti. +2fa_auth_required = Obisk na daljavo je zahteval preverjanje pristnosti z dvema dejavnikoma. +org_name_been_taken = Ime organizacije je že zasedeno. +unknown_error = Neznana napaka: +repo_name_been_taken = Ime skladišča je že uporabljeno. +repository_force_private = Omogočena je možnost Force Private: zasebnih skladišč ni mogoče objaviti. +repository_files_already_exist = Datoteke za to skladišče že obstajajo. Obrnite se na skrbnika sistema. +repository_files_already_exist.adopt = Datoteke za ta repozitorij že obstajajo in jih je mogoče samo sprejeti. +repository_files_already_exist.delete = Datoteke za to skladišče že obstajajo. Morate jih izbrisati. +repository_files_already_exist.adopt_or_delete = Datoteke za to skladišče že obstajajo. Sprejmite jih ali pa jih izbrišite. +openid_been_used = Naslov OpenID "%s" je že uporabljen. +username_password_incorrect = Uporabniško ime ali geslo je napačno. +password_lowercase_one = Vsaj en mali črkovni znak +password_uppercase_one = Vsaj en veliki tiskani znak +password_digit_one = Vsaj ena številka +password_special_one = vsaj en poseben znak (ločila, oklepaji, narekovaji itd.) +enterred_invalid_repo_name = Ime skladišča, ki ste ga vnesli, je napačno. +team_no_units_error = Dovolite dostop do vsaj enega oddelka repozitorija. +email_been_used = E-poštni naslov je že uporabljen. +email_invalid = E-poštni naslov je neveljaven. +enterred_invalid_owner_name = Ime novega lastnika ni veljavno. +invalid_ssh_principal = Nepravilen principal: %s +must_use_public_key = Ključ, ki ste ga navedli, je zasebni ključ. Zasebnega ključa ne nalagajte nikamor. Namesto tega uporabite svoj javni ključ. +auth_failed = Preverjanje pristnosti ni uspelo: %v +enterred_invalid_password = Vneseno geslo je napačno. +user_not_exist = Uporabnik ne obstaja. +team_not_exist = Ekipa ne obstaja. +duplicate_invite_to_team = Uporabnik je bil že povabljen kot član ekipe. +username_error = ` lahko vsebuje samo alfanumerične znake ("0-9", "a-z", "A-Z"), pomišljaj ("-"), podčrtaj ("_") in piko ("."). Ne sme se začeti ali končati z nealfanumeričnimi znaki, zaporedni nealfanumerični znaki pa so prav tako prepovedani.` +still_has_org = Vaš račun je član ene ali več organizacij, najprej zapustite te organizacije. +git_ref_name_error = ` mora biti dobro oblikovano referenčno ime Git.` +size_error = ` mora biti velikosti %s.` +min_size_error = ` mora vsebovati vsaj %s znakov.` +max_size_error = ` mora vsebovati največ %s znakov.` +email_error = ` ni veljaven e-poštni naslov.` +alpha_dash_error = ` mora vsebovati samo alfanumerične znake, pomišljaje ("-") in podčrtanke ("_").` +alpha_dash_dot_error = ` mora vsebovati samo alfanumerične znake, pomišljaj ("-"), podčrtaj ("_") in piko (".").` + +[user] +form.name_chars_not_allowed = Uporabniško ime "%s" vsebuje neveljavne znake. +disabled_public_activity = Ta uporabnik je onemogočil javno vidnost dejavnosti. +change_avatar = Spremeni svoj avatar… +joined_on = Pridružil se je na %s +activity = Javna dejavnost +followers = Sledilci +block_user = Blokiranje uporabnika +overview = Pregled +following = Sledenje +follow = Sledite +unfollow = Neupoštevanje +block = Blok +unblock = Odblokiranje +user_bio = Biografija +projects = Projekti +show_on_map = Prikaži to mesto na zemljevidu +settings = Uporabniške nastavitve +form.name_reserved = Uporabniško ime "%s" je rezervirano. +form.name_pattern_not_allowed = Vzorec "%s" v uporabniškem imenu ni dovoljen. + +[packages] +owner.settings.chef.keypair.description = Za preverjanje pristnosti v registru Chef je potreben par ključev. Če ste par ključev ustvarili že prej, se pri ustvarjanju novega para ključev stari par ključev zavrže. \ No newline at end of file From ff8cb299d1061f090f605dc1aa7a842642b50b44 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 1 Mar 2024 10:23:00 +0800 Subject: [PATCH 268/271] Move migration functions to services layer (#29497) --- modules/repository/repo.go | 266 ------------------------ routers/web/repo/setting/setting.go | 3 +- services/migrations/gitea_uploader.go | 2 +- services/repository/migrate.go | 287 ++++++++++++++++++++++++++ tests/integration/mirror_pull_test.go | 3 +- 5 files changed, 290 insertions(+), 271 deletions(-) create mode 100644 services/repository/migrate.go diff --git a/modules/repository/repo.go b/modules/repository/repo.go index 2f076c5286..a863bec996 100644 --- a/modules/repository/repo.go +++ b/modules/repository/repo.go @@ -6,16 +6,13 @@ package repository import ( "context" - "errors" "fmt" "io" - "net/http" "strings" "time" "code.gitea.io/gitea/models/db" git_model "code.gitea.io/gitea/models/git" - "code.gitea.io/gitea/models/organization" repo_model "code.gitea.io/gitea/models/repo" user_model "code.gitea.io/gitea/models/user" "code.gitea.io/gitea/modules/container" @@ -23,10 +20,8 @@ import ( "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" - "code.gitea.io/gitea/modules/migration" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/timeutil" - "code.gitea.io/gitea/modules/util" ) /* @@ -48,267 +43,6 @@ func WikiRemoteURL(ctx context.Context, remote string) string { return "" } -// MigrateRepositoryGitData starts migrating git related data after created migrating repository -func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, - repo *repo_model.Repository, opts migration.MigrateOptions, - httpTransport *http.Transport, -) (*repo_model.Repository, error) { - repoPath := repo_model.RepoPath(u.Name, opts.RepoName) - - if u.IsOrganization() { - t, err := organization.OrgFromUser(u).GetOwnerTeam(ctx) - if err != nil { - return nil, err - } - repo.NumWatches = t.NumMembers - } else { - repo.NumWatches = 1 - } - - migrateTimeout := time.Duration(setting.Git.Timeout.Migrate) * time.Second - - var err error - if err = util.RemoveAll(repoPath); err != nil { - return repo, fmt.Errorf("Failed to remove %s: %w", repoPath, err) - } - - if err = git.Clone(ctx, opts.CloneAddr, repoPath, git.CloneRepoOptions{ - Mirror: true, - Quiet: true, - Timeout: migrateTimeout, - SkipTLSVerify: setting.Migrations.SkipTLSVerify, - }); err != nil { - if errors.Is(err, context.DeadlineExceeded) { - return repo, fmt.Errorf("Clone timed out. Consider increasing [git.timeout] MIGRATE in app.ini. Underlying Error: %w", err) - } - return repo, fmt.Errorf("Clone: %w", err) - } - - if err := git.WriteCommitGraph(ctx, repoPath); err != nil { - return repo, err - } - - if opts.Wiki { - wikiPath := repo_model.WikiPath(u.Name, opts.RepoName) - wikiRemotePath := WikiRemoteURL(ctx, opts.CloneAddr) - if len(wikiRemotePath) > 0 { - if err := util.RemoveAll(wikiPath); err != nil { - return repo, fmt.Errorf("Failed to remove %s: %w", wikiPath, err) - } - - if err := git.Clone(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{ - Mirror: true, - Quiet: true, - Timeout: migrateTimeout, - SkipTLSVerify: setting.Migrations.SkipTLSVerify, - }); err != nil { - log.Warn("Clone wiki: %v", err) - if err := util.RemoveAll(wikiPath); err != nil { - return repo, fmt.Errorf("Failed to remove %s: %w", wikiPath, err) - } - } else { - // Figure out the branch of the wiki we just cloned. We assume - // that the default branch is to be used, and we'll use the same - // name as the source. - gitRepo, err := git.OpenRepository(ctx, wikiPath) - if err != nil { - log.Warn("Failed to open wiki repository during migration: %v", err) - if err := util.RemoveAll(wikiPath); err != nil { - return repo, fmt.Errorf("Failed to remove %s: %w", wikiPath, err) - } - return repo, err - } - defer gitRepo.Close() - - branch, err := gitRepo.GetDefaultBranch() - if err != nil { - log.Warn("Failed to get the default branch of a migrated wiki repo: %v", err) - if err := util.RemoveAll(wikiPath); err != nil { - return repo, fmt.Errorf("Failed to remove %s: %w", wikiPath, err) - } - - return repo, err - } - repo.WikiBranch = branch - - if err := git.WriteCommitGraph(ctx, wikiPath); err != nil { - return repo, err - } - } - } - } - - if repo.OwnerID == u.ID { - repo.Owner = u - } - - if err = CheckDaemonExportOK(ctx, repo); err != nil { - return repo, fmt.Errorf("checkDaemonExportOK: %w", err) - } - - if stdout, _, err := git.NewCommand(ctx, "update-server-info"). - SetDescription(fmt.Sprintf("MigrateRepositoryGitData(git update-server-info): %s", repoPath)). - RunStdString(&git.RunOpts{Dir: repoPath}); err != nil { - log.Error("MigrateRepositoryGitData(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err) - return repo, fmt.Errorf("error in MigrateRepositoryGitData(git update-server-info): %w", err) - } - - gitRepo, err := git.OpenRepository(ctx, repoPath) - if err != nil { - return repo, fmt.Errorf("OpenRepository: %w", err) - } - defer gitRepo.Close() - - repo.IsEmpty, err = gitRepo.IsEmpty() - if err != nil { - return repo, fmt.Errorf("git.IsEmpty: %w", err) - } - - if !repo.IsEmpty { - if len(repo.DefaultBranch) == 0 { - // Try to get HEAD branch and set it as default branch. - headBranch, err := gitRepo.GetHEADBranch() - if err != nil { - return repo, fmt.Errorf("GetHEADBranch: %w", err) - } - if headBranch != nil { - repo.DefaultBranch = headBranch.Name - } - } - - if _, err := SyncRepoBranchesWithRepo(ctx, repo, gitRepo, u.ID); err != nil { - return repo, fmt.Errorf("SyncRepoBranchesWithRepo: %v", err) - } - - if !opts.Releases { - // note: this will greatly improve release (tag) sync - // for pull-mirrors with many tags - repo.IsMirror = opts.Mirror - if err = SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { - log.Error("Failed to synchronize tags to releases for repository: %v", err) - } - } - - if opts.LFS { - endpoint := lfs.DetermineEndpoint(opts.CloneAddr, opts.LFSEndpoint) - lfsClient := lfs.NewClient(endpoint, httpTransport) - if err = StoreMissingLfsObjectsInRepository(ctx, repo, gitRepo, lfsClient); err != nil { - log.Error("Failed to store missing LFS objects for repository: %v", err) - } - } - } - - ctx, committer, err := db.TxContext(ctx) - if err != nil { - return nil, err - } - defer committer.Close() - - if opts.Mirror { - remoteAddress, err := util.SanitizeURL(opts.CloneAddr) - if err != nil { - return repo, err - } - mirrorModel := repo_model.Mirror{ - RepoID: repo.ID, - Interval: setting.Mirror.DefaultInterval, - EnablePrune: true, - NextUpdateUnix: timeutil.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval), - LFS: opts.LFS, - RemoteAddress: remoteAddress, - } - if opts.LFS { - mirrorModel.LFSEndpoint = opts.LFSEndpoint - } - - if opts.MirrorInterval != "" { - parsedInterval, err := time.ParseDuration(opts.MirrorInterval) - if err != nil { - log.Error("Failed to set Interval: %v", err) - return repo, err - } - if parsedInterval == 0 { - mirrorModel.Interval = 0 - mirrorModel.NextUpdateUnix = 0 - } else if parsedInterval < setting.Mirror.MinInterval { - err := fmt.Errorf("interval %s is set below Minimum Interval of %s", parsedInterval, setting.Mirror.MinInterval) - log.Error("Interval: %s is too frequent", opts.MirrorInterval) - return repo, err - } else { - mirrorModel.Interval = parsedInterval - mirrorModel.NextUpdateUnix = timeutil.TimeStampNow().AddDuration(parsedInterval) - } - } - - if err = repo_model.InsertMirror(ctx, &mirrorModel); err != nil { - return repo, fmt.Errorf("InsertOne: %w", err) - } - - repo.IsMirror = true - if err = UpdateRepository(ctx, repo, false); err != nil { - return nil, err - } - - // this is necessary for sync local tags from remote - configName := fmt.Sprintf("remote.%s.fetch", mirrorModel.GetRemoteName()) - if stdout, _, err := git.NewCommand(ctx, "config"). - AddOptionValues("--add", configName, `+refs/tags/*:refs/tags/*`). - RunStdString(&git.RunOpts{Dir: repoPath}); err != nil { - log.Error("MigrateRepositoryGitData(git config --add +refs/tags/*:refs/tags/*) in %v: Stdout: %s\nError: %v", repo, stdout, err) - return repo, fmt.Errorf("error in MigrateRepositoryGitData(git config --add +refs/tags/*:refs/tags/*): %w", err) - } - } else { - if err = UpdateRepoSize(ctx, repo); err != nil { - log.Error("Failed to update size for repository: %v", err) - } - if repo, err = CleanUpMigrateInfo(ctx, repo); err != nil { - return nil, err - } - } - - return repo, committer.Commit() -} - -// cleanUpMigrateGitConfig removes mirror info which prevents "push --all". -// This also removes possible user credentials. -func cleanUpMigrateGitConfig(ctx context.Context, repoPath string) error { - cmd := git.NewCommand(ctx, "remote", "rm", "origin") - // if the origin does not exist - _, stderr, err := cmd.RunStdString(&git.RunOpts{ - Dir: repoPath, - }) - if err != nil && !strings.HasPrefix(stderr, "fatal: No such remote") { - return err - } - return nil -} - -// CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors. -func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo_model.Repository, error) { - repoPath := repo.RepoPath() - if err := CreateDelegateHooks(repoPath); err != nil { - return repo, fmt.Errorf("createDelegateHooks: %w", err) - } - if repo.HasWiki() { - if err := CreateDelegateHooks(repo.WikiPath()); err != nil { - return repo, fmt.Errorf("createDelegateHooks.(wiki): %w", err) - } - } - - _, _, err := git.NewCommand(ctx, "remote", "rm", "origin").RunStdString(&git.RunOpts{Dir: repoPath}) - if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { - return repo, fmt.Errorf("CleanUpMigrateInfo: %w", err) - } - - if repo.HasWiki() { - if err := cleanUpMigrateGitConfig(ctx, repo.WikiPath()); err != nil { - return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %w", err) - } - } - - return repo, UpdateRepository(ctx, repo, false) -} - // SyncRepoTags synchronizes releases table with repository tags func SyncRepoTags(ctx context.Context, repoID int64) error { repo, err := repo_model.GetRepositoryByID(ctx, repoID) diff --git a/routers/web/repo/setting/setting.go b/routers/web/repo/setting/setting.go index dcb4be7ef8..0fdae7ec56 100644 --- a/routers/web/repo/setting/setting.go +++ b/routers/web/repo/setting/setting.go @@ -25,7 +25,6 @@ import ( "code.gitea.io/gitea/modules/indexer/stats" "code.gitea.io/gitea/modules/lfs" "code.gitea.io/gitea/modules/log" - repo_module "code.gitea.io/gitea/modules/repository" "code.gitea.io/gitea/modules/setting" "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/modules/util" @@ -715,7 +714,7 @@ func SettingsPost(ctx *context.Context) { } repo.IsMirror = false - if _, err := repo_module.CleanUpMigrateInfo(ctx, repo); err != nil { + if _, err := repo_service.CleanUpMigrateInfo(ctx, repo); err != nil { ctx.ServerError("CleanUpMigrateInfo", err) return } else if err = repo_model.DeleteMirrorByRepoID(ctx, ctx.Repo.Repository.ID); err != nil { diff --git a/services/migrations/gitea_uploader.go b/services/migrations/gitea_uploader.go index 99a44dff0f..717be7b7f3 100644 --- a/services/migrations/gitea_uploader.go +++ b/services/migrations/gitea_uploader.go @@ -120,7 +120,7 @@ func (g *GiteaLocalUploader) CreateRepo(repo *base.Repository, opts base.Migrate r.DefaultBranch = repo.DefaultBranch r.Description = repo.Description - r, err = repo_module.MigrateRepositoryGitData(g.ctx, owner, r, base.MigrateOptions{ + r, err = repo_service.MigrateRepositoryGitData(g.ctx, owner, r, base.MigrateOptions{ RepoName: g.repoName, Description: repo.Description, OriginalURL: repo.OriginalURL, diff --git a/services/repository/migrate.go b/services/repository/migrate.go new file mode 100644 index 0000000000..b218a2ef46 --- /dev/null +++ b/services/repository/migrate.go @@ -0,0 +1,287 @@ +// Copyright 2024 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package repository + +import ( + "context" + "errors" + "fmt" + "net/http" + "strings" + "time" + + "code.gitea.io/gitea/models/db" + "code.gitea.io/gitea/models/organization" + repo_model "code.gitea.io/gitea/models/repo" + user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/git" + "code.gitea.io/gitea/modules/lfs" + "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/migration" + repo_module "code.gitea.io/gitea/modules/repository" + "code.gitea.io/gitea/modules/setting" + "code.gitea.io/gitea/modules/timeutil" + "code.gitea.io/gitea/modules/util" +) + +// MigrateRepositoryGitData starts migrating git related data after created migrating repository +func MigrateRepositoryGitData(ctx context.Context, u *user_model.User, + repo *repo_model.Repository, opts migration.MigrateOptions, + httpTransport *http.Transport, +) (*repo_model.Repository, error) { + repoPath := repo_model.RepoPath(u.Name, opts.RepoName) + + if u.IsOrganization() { + t, err := organization.OrgFromUser(u).GetOwnerTeam(ctx) + if err != nil { + return nil, err + } + repo.NumWatches = t.NumMembers + } else { + repo.NumWatches = 1 + } + + migrateTimeout := time.Duration(setting.Git.Timeout.Migrate) * time.Second + + var err error + if err = util.RemoveAll(repoPath); err != nil { + return repo, fmt.Errorf("Failed to remove %s: %w", repoPath, err) + } + + if err = git.Clone(ctx, opts.CloneAddr, repoPath, git.CloneRepoOptions{ + Mirror: true, + Quiet: true, + Timeout: migrateTimeout, + SkipTLSVerify: setting.Migrations.SkipTLSVerify, + }); err != nil { + if errors.Is(err, context.DeadlineExceeded) { + return repo, fmt.Errorf("Clone timed out. Consider increasing [git.timeout] MIGRATE in app.ini. Underlying Error: %w", err) + } + return repo, fmt.Errorf("Clone: %w", err) + } + + if err := git.WriteCommitGraph(ctx, repoPath); err != nil { + return repo, err + } + + if opts.Wiki { + wikiPath := repo_model.WikiPath(u.Name, opts.RepoName) + wikiRemotePath := repo_module.WikiRemoteURL(ctx, opts.CloneAddr) + if len(wikiRemotePath) > 0 { + if err := util.RemoveAll(wikiPath); err != nil { + return repo, fmt.Errorf("Failed to remove %s: %w", wikiPath, err) + } + + if err := git.Clone(ctx, wikiRemotePath, wikiPath, git.CloneRepoOptions{ + Mirror: true, + Quiet: true, + Timeout: migrateTimeout, + SkipTLSVerify: setting.Migrations.SkipTLSVerify, + }); err != nil { + log.Warn("Clone wiki: %v", err) + if err := util.RemoveAll(wikiPath); err != nil { + return repo, fmt.Errorf("Failed to remove %s: %w", wikiPath, err) + } + } else { + // Figure out the branch of the wiki we just cloned. We assume + // that the default branch is to be used, and we'll use the same + // name as the source. + gitRepo, err := git.OpenRepository(ctx, wikiPath) + if err != nil { + log.Warn("Failed to open wiki repository during migration: %v", err) + if err := util.RemoveAll(wikiPath); err != nil { + return repo, fmt.Errorf("Failed to remove %s: %w", wikiPath, err) + } + return repo, err + } + defer gitRepo.Close() + + branch, err := gitRepo.GetDefaultBranch() + if err != nil { + log.Warn("Failed to get the default branch of a migrated wiki repo: %v", err) + if err := util.RemoveAll(wikiPath); err != nil { + return repo, fmt.Errorf("Failed to remove %s: %w", wikiPath, err) + } + + return repo, err + } + repo.WikiBranch = branch + + if err := git.WriteCommitGraph(ctx, wikiPath); err != nil { + return repo, err + } + } + } + } + + if repo.OwnerID == u.ID { + repo.Owner = u + } + + if err = repo_module.CheckDaemonExportOK(ctx, repo); err != nil { + return repo, fmt.Errorf("checkDaemonExportOK: %w", err) + } + + if stdout, _, err := git.NewCommand(ctx, "update-server-info"). + SetDescription(fmt.Sprintf("MigrateRepositoryGitData(git update-server-info): %s", repoPath)). + RunStdString(&git.RunOpts{Dir: repoPath}); err != nil { + log.Error("MigrateRepositoryGitData(git update-server-info) in %v: Stdout: %s\nError: %v", repo, stdout, err) + return repo, fmt.Errorf("error in MigrateRepositoryGitData(git update-server-info): %w", err) + } + + gitRepo, err := git.OpenRepository(ctx, repoPath) + if err != nil { + return repo, fmt.Errorf("OpenRepository: %w", err) + } + defer gitRepo.Close() + + repo.IsEmpty, err = gitRepo.IsEmpty() + if err != nil { + return repo, fmt.Errorf("git.IsEmpty: %w", err) + } + + if !repo.IsEmpty { + if len(repo.DefaultBranch) == 0 { + // Try to get HEAD branch and set it as default branch. + headBranch, err := gitRepo.GetHEADBranch() + if err != nil { + return repo, fmt.Errorf("GetHEADBranch: %w", err) + } + if headBranch != nil { + repo.DefaultBranch = headBranch.Name + } + } + + if _, err := repo_module.SyncRepoBranchesWithRepo(ctx, repo, gitRepo, u.ID); err != nil { + return repo, fmt.Errorf("SyncRepoBranchesWithRepo: %v", err) + } + + if !opts.Releases { + // note: this will greatly improve release (tag) sync + // for pull-mirrors with many tags + repo.IsMirror = opts.Mirror + if err = repo_module.SyncReleasesWithTags(ctx, repo, gitRepo); err != nil { + log.Error("Failed to synchronize tags to releases for repository: %v", err) + } + } + + if opts.LFS { + endpoint := lfs.DetermineEndpoint(opts.CloneAddr, opts.LFSEndpoint) + lfsClient := lfs.NewClient(endpoint, httpTransport) + if err = repo_module.StoreMissingLfsObjectsInRepository(ctx, repo, gitRepo, lfsClient); err != nil { + log.Error("Failed to store missing LFS objects for repository: %v", err) + } + } + } + + ctx, committer, err := db.TxContext(ctx) + if err != nil { + return nil, err + } + defer committer.Close() + + if opts.Mirror { + remoteAddress, err := util.SanitizeURL(opts.CloneAddr) + if err != nil { + return repo, err + } + mirrorModel := repo_model.Mirror{ + RepoID: repo.ID, + Interval: setting.Mirror.DefaultInterval, + EnablePrune: true, + NextUpdateUnix: timeutil.TimeStampNow().AddDuration(setting.Mirror.DefaultInterval), + LFS: opts.LFS, + RemoteAddress: remoteAddress, + } + if opts.LFS { + mirrorModel.LFSEndpoint = opts.LFSEndpoint + } + + if opts.MirrorInterval != "" { + parsedInterval, err := time.ParseDuration(opts.MirrorInterval) + if err != nil { + log.Error("Failed to set Interval: %v", err) + return repo, err + } + if parsedInterval == 0 { + mirrorModel.Interval = 0 + mirrorModel.NextUpdateUnix = 0 + } else if parsedInterval < setting.Mirror.MinInterval { + err := fmt.Errorf("interval %s is set below Minimum Interval of %s", parsedInterval, setting.Mirror.MinInterval) + log.Error("Interval: %s is too frequent", opts.MirrorInterval) + return repo, err + } else { + mirrorModel.Interval = parsedInterval + mirrorModel.NextUpdateUnix = timeutil.TimeStampNow().AddDuration(parsedInterval) + } + } + + if err = repo_model.InsertMirror(ctx, &mirrorModel); err != nil { + return repo, fmt.Errorf("InsertOne: %w", err) + } + + repo.IsMirror = true + if err = UpdateRepository(ctx, repo, false); err != nil { + return nil, err + } + + // this is necessary for sync local tags from remote + configName := fmt.Sprintf("remote.%s.fetch", mirrorModel.GetRemoteName()) + if stdout, _, err := git.NewCommand(ctx, "config"). + AddOptionValues("--add", configName, `+refs/tags/*:refs/tags/*`). + RunStdString(&git.RunOpts{Dir: repoPath}); err != nil { + log.Error("MigrateRepositoryGitData(git config --add +refs/tags/*:refs/tags/*) in %v: Stdout: %s\nError: %v", repo, stdout, err) + return repo, fmt.Errorf("error in MigrateRepositoryGitData(git config --add +refs/tags/*:refs/tags/*): %w", err) + } + } else { + if err = repo_module.UpdateRepoSize(ctx, repo); err != nil { + log.Error("Failed to update size for repository: %v", err) + } + if repo, err = CleanUpMigrateInfo(ctx, repo); err != nil { + return nil, err + } + } + + return repo, committer.Commit() +} + +// cleanUpMigrateGitConfig removes mirror info which prevents "push --all". +// This also removes possible user credentials. +func cleanUpMigrateGitConfig(ctx context.Context, repoPath string) error { + cmd := git.NewCommand(ctx, "remote", "rm", "origin") + // if the origin does not exist + _, stderr, err := cmd.RunStdString(&git.RunOpts{ + Dir: repoPath, + }) + if err != nil && !strings.HasPrefix(stderr, "fatal: No such remote") { + return err + } + return nil +} + +// CleanUpMigrateInfo finishes migrating repository and/or wiki with things that don't need to be done for mirrors. +func CleanUpMigrateInfo(ctx context.Context, repo *repo_model.Repository) (*repo_model.Repository, error) { + repoPath := repo.RepoPath() + if err := repo_module.CreateDelegateHooks(repoPath); err != nil { + return repo, fmt.Errorf("createDelegateHooks: %w", err) + } + if repo.HasWiki() { + if err := repo_module.CreateDelegateHooks(repo.WikiPath()); err != nil { + return repo, fmt.Errorf("createDelegateHooks.(wiki): %w", err) + } + } + + _, _, err := git.NewCommand(ctx, "remote", "rm", "origin").RunStdString(&git.RunOpts{Dir: repoPath}) + if err != nil && !strings.HasPrefix(err.Error(), "exit status 128 - fatal: No such remote ") { + return repo, fmt.Errorf("CleanUpMigrateInfo: %w", err) + } + + if repo.HasWiki() { + if err := cleanUpMigrateGitConfig(ctx, repo.WikiPath()); err != nil { + return repo, fmt.Errorf("cleanUpMigrateGitConfig (wiki): %w", err) + } + } + + return repo, UpdateRepository(ctx, repo, false) +} diff --git a/tests/integration/mirror_pull_test.go b/tests/integration/mirror_pull_test.go index 2e71b80fbb..77050c4bbc 100644 --- a/tests/integration/mirror_pull_test.go +++ b/tests/integration/mirror_pull_test.go @@ -14,7 +14,6 @@ import ( "code.gitea.io/gitea/modules/git" "code.gitea.io/gitea/modules/gitrepo" "code.gitea.io/gitea/modules/migration" - "code.gitea.io/gitea/modules/repository" mirror_service "code.gitea.io/gitea/services/mirror" release_service "code.gitea.io/gitea/services/release" repo_service "code.gitea.io/gitea/services/repository" @@ -52,7 +51,7 @@ func TestMirrorPull(t *testing.T) { ctx := context.Background() - mirror, err := repository.MigrateRepositoryGitData(ctx, user, mirrorRepo, opts, nil) + mirror, err := repo_service.MigrateRepositoryGitData(ctx, user, mirrorRepo, opts, nil) assert.NoError(t, err) gitRepo, err := gitrepo.OpenRepository(git.DefaultContext, repo) From c41b2c73ef21d5c54c7f2658ceffaa163b135131 Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Sat, 2 Mar 2024 20:07:23 +0800 Subject: [PATCH 269/271] Display tag name as title for a tag with no release --- routers/web/repo/release.go | 8 ++++++++ templates/repo/release/list.tmpl | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/routers/web/repo/release.go b/routers/web/repo/release.go index 1998bd8ccd..79f3181ba0 100644 --- a/routers/web/repo/release.go +++ b/routers/web/repo/release.go @@ -184,6 +184,11 @@ func Releases(ctx *context.Context) { ctx.ServerError("getReleaseInfos", err) return } + for _, rel := range releases { + if rel.Release.IsTag && rel.Release.Title == "" { + rel.Release.Title = rel.Release.TagName + } + } ctx.Data["Releases"] = releases @@ -295,6 +300,9 @@ func SingleRelease(ctx *context.Context) { } release := releases[0].Release + if release.IsTag && release.Title == "" { + release.Title = release.TagName + } ctx.Data["PageIsSingleTag"] = release.IsTag if release.IsTag { diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index 6dbeb741db..826480e3fb 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -18,13 +18,13 @@

      - {{$release.Title}} + {{if $.PageIsSingleTag}}{{$release.Title}}{{else}}{{$release.Title}}{{end}} {{template "repo/commit_statuses" dict "Status" $info.CommitStatus "Statuses" $info.CommitStatuses "AdditionalClasses" "gt-df"}} {{if $release.IsDraft}} {{ctx.Locale.Tr "repo.release.draft"}} {{else if $release.IsPrerelease}} {{ctx.Locale.Tr "repo.release.prerelease"}} - {{else}} + {{else if (not $release.IsTag)}} {{ctx.Locale.Tr "repo.release.stable"}} {{end}}

      From 19ff532d4234e2063c1fc15547422b0b5337829b Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 4 Mar 2024 08:16:04 +0100 Subject: [PATCH 270/271] Test that tags without a release display properly Update the `TestTagViewWithoutRelease` test case with another assert: one that checks that the release title is properly displayed. Signed-off-by: Gergely Nagy --- tests/integration/repo_tag_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/integration/repo_tag_test.go b/tests/integration/repo_tag_test.go index 60c73ae63e..5a99453659 100644 --- a/tests/integration/repo_tag_test.go +++ b/tests/integration/repo_tag_test.go @@ -7,6 +7,7 @@ package integration import ( "net/http" "net/url" + "strings" "testing" "code.gitea.io/gitea/models" @@ -56,6 +57,13 @@ func TestTagViewWithoutRelease(t *testing.T) { // Test that the release sub-menu isn't active releaseLink := htmlDoc.Find(".small-menu-items .item[href*='/releases']") assert.False(t, releaseLink.HasClass("active")) + + // Test that the title is displayed + releaseTitle := strings.TrimSpace(htmlDoc.Find("h4.release-list-title").Text()) + assert.Equal(t, "no-release", releaseTitle) + + // Test that there is no "Stable" link + htmlDoc.AssertElement(t, "h4.release-list-title > span.ui.green.label", false) } func TestCreateNewTagProtected(t *testing.T) { From f9894f4c5147447fa2f38a1835a575fd38f54e75 Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Mon, 4 Mar 2024 08:25:07 +0100 Subject: [PATCH 271/271] A release title should always be a link This partially reverts c41b2c73ef21d5c54c7f2658ceffaa163b135131: for the sake of consistency, the title of a release should always be a link, whether it's a tag-only release or not. Signed-off-by: Gergely Nagy --- templates/repo/release/list.tmpl | 2 +- tests/integration/repo_tag_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/repo/release/list.tmpl b/templates/repo/release/list.tmpl index 826480e3fb..d533cf6757 100644 --- a/templates/repo/release/list.tmpl +++ b/templates/repo/release/list.tmpl @@ -18,7 +18,7 @@

      - {{if $.PageIsSingleTag}}{{$release.Title}}{{else}}{{$release.Title}}{{end}} + {{$release.Title}} {{template "repo/commit_statuses" dict "Status" $info.CommitStatus "Statuses" $info.CommitStatuses "AdditionalClasses" "gt-df"}} {{if $release.IsDraft}} {{ctx.Locale.Tr "repo.release.draft"}} diff --git a/tests/integration/repo_tag_test.go b/tests/integration/repo_tag_test.go index 5a99453659..3fa3454343 100644 --- a/tests/integration/repo_tag_test.go +++ b/tests/integration/repo_tag_test.go @@ -59,7 +59,7 @@ func TestTagViewWithoutRelease(t *testing.T) { assert.False(t, releaseLink.HasClass("active")) // Test that the title is displayed - releaseTitle := strings.TrimSpace(htmlDoc.Find("h4.release-list-title").Text()) + releaseTitle := strings.TrimSpace(htmlDoc.Find("h4.release-list-title > a").Text()) assert.Equal(t, "no-release", releaseTitle) // Test that there is no "Stable" link