From cbc9a0fe477b1b8af249ca0b8dac5fc2be64e9f6 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Tue, 28 Feb 2023 18:20:36 +0800 Subject: [PATCH 1/7] Avoid too long names for actions (#23162) The name of the job or step comes from the workflow file, while the name of the runner comes from its registration. If the strings used for these names are too long, they could cause db issues. --- models/actions/run.go | 1 + models/actions/task.go | 3 ++- routers/api/actions/runner/runner.go | 4 +++- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/models/actions/run.go b/models/actions/run.go index a8d991471e635..d5ab45a51958a 100644 --- a/models/actions/run.go +++ b/models/actions/run.go @@ -192,6 +192,7 @@ func InsertRun(ctx context.Context, run *ActionRun, jobs []*jobparser.SingleWork if len(needs) > 0 || run.NeedApproval { status = StatusBlocked } + job.Name, _ = util.SplitStringAtByteN(job.Name, 255) runJobs = append(runJobs, &ActionRunJob{ RunID: run.ID, RepoID: run.RepoID, diff --git a/models/actions/task.go b/models/actions/task.go index 5b6206c346425..ffec4c92aa8d8 100644 --- a/models/actions/task.go +++ b/models/actions/task.go @@ -298,8 +298,9 @@ func CreateTaskForRunner(ctx context.Context, runner *ActionRunner) (*ActionTask if len(workflowJob.Steps) > 0 { steps := make([]*ActionTaskStep, len(workflowJob.Steps)) for i, v := range workflowJob.Steps { + name, _ := util.SplitStringAtByteN(v.String(), 255) steps[i] = &ActionTaskStep{ - Name: v.String(), + Name: name, TaskID: task.ID, Index: int64(i), RepoID: task.RepoID, diff --git a/routers/api/actions/runner/runner.go b/routers/api/actions/runner/runner.go index 7dbab9da0a2b9..d0bfb2363e0c5 100644 --- a/routers/api/actions/runner/runner.go +++ b/routers/api/actions/runner/runner.go @@ -12,6 +12,7 @@ import ( "code.gitea.io/gitea/modules/actions" "code.gitea.io/gitea/modules/json" "code.gitea.io/gitea/modules/log" + "code.gitea.io/gitea/modules/util" actions_service "code.gitea.io/gitea/services/actions" runnerv1 "code.gitea.io/actions-proto-go/runner/v1" @@ -55,9 +56,10 @@ func (s *Service) Register( } // create new runner + name, _ := util.SplitStringAtByteN(req.Msg.Name, 255) runner := &actions_model.ActionRunner{ UUID: gouuid.New().String(), - Name: req.Msg.Name, + Name: name, OwnerID: runnerToken.OwnerID, RepoID: runnerToken.RepoID, AgentLabels: req.Msg.AgentLabels, From 443dcc2db0676cae8b1b72c1b0b99e675c21177c Mon Sep 17 00:00:00 2001 From: Yarden Shoham Date: Tue, 28 Feb 2023 17:30:43 +0200 Subject: [PATCH 2/7] Write Gitpod `app.ini` only once (#23192) Before this change, the `app.ini` would get overwritten on each workspace start, confusing Gitea. It asked for reinstallation each time. This makes sure the file is written only once by checking it does not exist before creating it. --- [Review without whitespace diff](https://github.com/go-gitea/gitea/pull/23192/files?w=1) --------- Co-authored-by: delvh --- .gitpod.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.gitpod.yml b/.gitpod.yml index a184e6376ebb3..506c620458354 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -10,9 +10,12 @@ tasks: - name: Run backend command: | gp sync-await setup - mkdir -p custom/conf/ - echo -e "[server]\nROOT_URL=$(gp url 3000)/" > custom/conf/app.ini - echo -e "\n[database]\nDB_TYPE = sqlite3\nPATH = $GITPOD_REPO_ROOT/data/gitea.db" >> custom/conf/app.ini + if [ ! -f custom/conf/app.ini ] + then + mkdir -p custom/conf/ + echo -e "[server]\nROOT_URL=$(gp url 3000)/" > custom/conf/app.ini + echo -e "\n[database]\nDB_TYPE = sqlite3\nPATH = $GITPOD_REPO_ROOT/data/gitea.db" >> custom/conf/app.ini + fi export TAGS="sqlite sqlite_unlock_notify" make watch-backend - name: Run frontend From f5987c24e2b561952ebf9a2485b863325c16ee48 Mon Sep 17 00:00:00 2001 From: wxiaoguang Date: Wed, 1 Mar 2023 04:33:10 +0800 Subject: [PATCH 3/7] Make `gitea serv` respect git binary home (#23138) Close #23137 The old code is too old (8-9 years ago) Let's try to execute the git commands from git bin home directly. The verb has been checked above, it could only be: * git-upload-pack * git-upload-archive * git-receive-pack * git-lfs-authenticate --- cmd/serv.go | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/cmd/serv.go b/cmd/serv.go index 145d1b9e935f6..d7510845a5beb 100644 --- a/cmd/serv.go +++ b/cmd/serv.go @@ -11,6 +11,7 @@ import ( "net/url" "os" "os/exec" + "path/filepath" "regexp" "strconv" "strings" @@ -290,17 +291,21 @@ func runServ(c *cli.Context) error { return nil } - // Special handle for Windows. - if setting.IsWindows { - verb = strings.Replace(verb, "-", " ", 1) - } - var gitcmd *exec.Cmd - verbs := strings.Split(verb, " ") - if len(verbs) == 2 { - gitcmd = exec.CommandContext(ctx, verbs[0], verbs[1], repoPath) - } else { - gitcmd = exec.CommandContext(ctx, verb, repoPath) + gitBinPath := filepath.Dir(git.GitExecutable) // e.g. /usr/bin + gitBinVerb := filepath.Join(gitBinPath, verb) // e.g. /usr/bin/git-upload-pack + if _, err := os.Stat(gitBinVerb); err != nil { + // if the command "git-upload-pack" doesn't exist, try to split "git-upload-pack" to use the sub-command with git + // ps: Windows only has "git.exe" in the bin path, so Windows always uses this way + verbFields := strings.SplitN(verb, "-", 2) + if len(verbFields) == 2 { + // use git binary with the sub-command part: "C:\...\bin\git.exe", "upload-pack", ... + gitcmd = exec.CommandContext(ctx, git.GitExecutable, verbFields[1], repoPath) + } + } + if gitcmd == nil { + // by default, use the verb (it has been checked above by allowedCommands) + gitcmd = exec.CommandContext(ctx, gitBinVerb, repoPath) } process.SetSysProcAttribute(gitcmd) From cbbd3726b4edb3c2bda59610050fd5af149fbdb1 Mon Sep 17 00:00:00 2001 From: Philip Peterson Date: Tue, 28 Feb 2023 16:26:19 -0500 Subject: [PATCH 4/7] Pass `--global` when calling `git config --get`, for consistency with `git config --set` (#23157) This arose out of #22451; it seems we are checking using non-global settings to see if a config value is set, in order to decide whether to call another global(-indeed) configuration command. This PR changes it so that both the check and the set are for global configuration. --- modules/git/git.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/git/git.go b/modules/git/git.go index 2feb242ac5dd0..24cfea8c7fe17 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -312,7 +312,7 @@ func CheckGitVersionAtLeast(atLeast string) error { } func configSet(key, value string) error { - stdout, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil) + stdout, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key).RunStdString(nil) if err != nil && !err.IsExitCode(1) { return fmt.Errorf("failed to get git config %s, err: %w", key, err) } @@ -331,7 +331,7 @@ func configSet(key, value string) error { } func configSetNonExist(key, value string) error { - _, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil) + _, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key).RunStdString(nil) if err == nil { // already exist return nil @@ -349,7 +349,7 @@ func configSetNonExist(key, value string) error { } func configAddNonExist(key, value string) error { - _, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(nil) + _, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(nil) if err == nil { // already exist return nil @@ -366,7 +366,7 @@ func configAddNonExist(key, value string) error { } func configUnsetAll(key, value string) error { - _, _, err := NewCommand(DefaultContext, "config", "--get").AddDynamicArguments(key).RunStdString(nil) + _, _, err := NewCommand(DefaultContext, "config", "--global", "--get").AddDynamicArguments(key).RunStdString(nil) if err == nil { // exist, need to remove _, _, err = NewCommand(DefaultContext, "config", "--global", "--unset-all").AddDynamicArguments(key, regexp.QuoteMeta(value)).RunStdString(nil) From 04347eb810689db799003cc342bbbc756716ff12 Mon Sep 17 00:00:00 2001 From: Jason Song Date: Wed, 1 Mar 2023 06:17:51 +0800 Subject: [PATCH 5/7] Use context parameter in services/repository (#23186) Use context parameter in `services/repository`. And use `cache.WithCacheContext(ctx)` to generate push action history feeds. Fix #23160 --- routers/api/v1/admin/adopt.go | 6 ++--- routers/api/v1/repo/branch.go | 2 +- routers/api/v1/repo/pull.go | 2 +- routers/api/v1/repo/repo.go | 8 +++---- routers/private/serv.go | 2 +- routers/web/admin/repos.go | 6 ++--- routers/web/org/setting.go | 2 +- routers/web/repo/branch.go | 2 +- routers/web/repo/http.go | 2 +- routers/web/repo/issue.go | 2 +- routers/web/repo/pull.go | 2 +- routers/web/repo/repo.go | 6 ++--- routers/web/repo/setting.go | 16 ++++++------- routers/web/repo/setting_protected_branch.go | 2 +- routers/web/user/setting/adopt.go | 4 ++-- services/repository/adopt.go | 22 ++++++++--------- services/repository/adopt_test.go | 10 ++++---- services/repository/avatar.go | 10 ++++---- services/repository/avatar_test.go | 9 +++---- services/repository/branch.go | 15 ++++++------ services/repository/fork.go | 4 ++-- services/repository/push.go | 25 ++++++++++---------- services/repository/repository.go | 14 +++++------ services/repository/review.go | 9 +++---- services/repository/review_test.go | 5 ++-- services/repository/template.go | 6 ++--- services/repository/transfer.go | 5 ++-- tests/integration/pull_merge_test.go | 2 +- tests/integration/pull_update_test.go | 2 +- 29 files changed, 102 insertions(+), 100 deletions(-) diff --git a/routers/api/v1/admin/adopt.go b/routers/api/v1/admin/adopt.go index 0e4e498e98243..47fd0ef3c30cf 100644 --- a/routers/api/v1/admin/adopt.go +++ b/routers/api/v1/admin/adopt.go @@ -45,7 +45,7 @@ func ListUnadoptedRepositories(ctx *context.APIContext) { if listOptions.Page == 0 { listOptions.Page = 1 } - repoNames, count, err := repo_service.ListUnadoptedRepositories(ctx.FormString("query"), &listOptions) + repoNames, count, err := repo_service.ListUnadoptedRepositories(ctx, ctx.FormString("query"), &listOptions) if err != nil { ctx.InternalServerError(err) return @@ -109,7 +109,7 @@ func AdoptRepository(ctx *context.APIContext) { ctx.NotFound() return } - if _, err := repo_service.AdoptRepository(ctx.Doer, ctxUser, repo_module.CreateRepoOptions{ + if _, err := repo_service.AdoptRepository(ctx, ctx.Doer, ctxUser, repo_module.CreateRepoOptions{ Name: repoName, IsPrivate: true, }); err != nil { @@ -172,7 +172,7 @@ func DeleteUnadoptedRepository(ctx *context.APIContext) { return } - if err := repo_service.DeleteUnadoptedRepository(ctx.Doer, ctxUser, repoName); err != nil { + if err := repo_service.DeleteUnadoptedRepository(ctx, ctx.Doer, ctxUser, repoName); err != nil { ctx.InternalServerError(err) return } diff --git a/routers/api/v1/repo/branch.go b/routers/api/v1/repo/branch.go index 8acaeaffb4173..dff47fbcf15b7 100644 --- a/routers/api/v1/repo/branch.go +++ b/routers/api/v1/repo/branch.go @@ -118,7 +118,7 @@ func DeleteBranch(ctx *context.APIContext) { branchName := ctx.Params("*") - if err := repo_service.DeleteBranch(ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil { + if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil { switch { case git.IsErrBranchNotExist(err): ctx.NotFound(err) diff --git a/routers/api/v1/repo/pull.go b/routers/api/v1/repo/pull.go index 84eebeb94dd7d..9b5ec0b3f8ed6 100644 --- a/routers/api/v1/repo/pull.go +++ b/routers/api/v1/repo/pull.go @@ -904,7 +904,7 @@ func MergePullRequest(ctx *context.APIContext) { } defer headRepo.Close() } - if err := repo_service.DeleteBranch(ctx.Doer, pr.HeadRepo, headRepo, pr.HeadBranch); err != nil { + if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, headRepo, pr.HeadBranch); err != nil { switch { case git.IsErrBranchNotExist(err): ctx.NotFound(err) diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 0395198e209a3..2f32ea956f150 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -230,7 +230,7 @@ func CreateUserRepo(ctx *context.APIContext, owner *user_model.User, opt api.Cre if opt.AutoInit && opt.Readme == "" { opt.Readme = "Default" } - repo, err := repo_service.CreateRepository(ctx.Doer, owner, repo_module.CreateRepoOptions{ + repo, err := repo_service.CreateRepository(ctx, ctx.Doer, owner, repo_module.CreateRepoOptions{ Name: opt.Name, Description: opt.Description, IssueLabels: opt.IssueLabels, @@ -393,7 +393,7 @@ func Generate(ctx *context.APIContext) { } } - repo, err := repo_service.GenerateRepository(ctx.Doer, ctxUser, ctx.Repo.Repository, opts) + repo, err := repo_service.GenerateRepository(ctx, ctx.Doer, ctxUser, ctx.Repo.Repository, opts) if err != nil { if repo_model.IsErrRepoAlreadyExist(err) { ctx.Error(http.StatusConflict, "", "The repository with the same name already exists.") @@ -637,7 +637,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err } // Check if repository name has been changed and not just a case change if repo.LowerName != strings.ToLower(newRepoName) { - if err := repo_service.ChangeRepositoryName(ctx.Doer, repo, newRepoName); err != nil { + if err := repo_service.ChangeRepositoryName(ctx, ctx.Doer, repo, newRepoName); err != nil { switch { case repo_model.IsErrRepoAlreadyExist(err): ctx.Error(http.StatusUnprocessableEntity, fmt.Sprintf("repo name is already taken [name: %s]", newRepoName), err) @@ -714,7 +714,7 @@ func updateBasicProperties(ctx *context.APIContext, opts api.EditRepoOption) err repo.DefaultBranch = *opts.DefaultBranch } - if err := repo_service.UpdateRepository(repo, visibilityChanged); err != nil { + if err := repo_service.UpdateRepository(ctx, repo, visibilityChanged); err != nil { ctx.Error(http.StatusInternalServerError, "UpdateRepository", err) return err } diff --git a/routers/private/serv.go b/routers/private/serv.go index 17f966e3e40a7..23ac011cf5a69 100644 --- a/routers/private/serv.go +++ b/routers/private/serv.go @@ -368,7 +368,7 @@ func ServCommand(ctx *context.PrivateContext) { return } - repo, err = repo_service.PushCreateRepo(user, owner, results.RepoName) + repo, err = repo_service.PushCreateRepo(ctx, user, owner, results.RepoName) if err != nil { log.Error("pushCreateRepo: %v", err) ctx.JSON(http.StatusNotFound, private.ErrServCommand{ diff --git a/routers/web/admin/repos.go b/routers/web/admin/repos.go index 1c4754f6d8a3e..53b609af966ba 100644 --- a/routers/web/admin/repos.go +++ b/routers/web/admin/repos.go @@ -96,7 +96,7 @@ func UnadoptedRepos(ctx *context.Context) { } ctx.Data["Keyword"] = q - repoNames, count, err := repo_service.ListUnadoptedRepositories(q, &opts) + repoNames, count, err := repo_service.ListUnadoptedRepositories(ctx, q, &opts) if err != nil { ctx.ServerError("ListUnadoptedRepositories", err) } @@ -148,7 +148,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) { if has || !isDir { // Fallthrough to failure mode } else if action == "adopt" { - if _, err := repo_service.AdoptRepository(ctx.Doer, ctxUser, repo_module.CreateRepoOptions{ + if _, err := repo_service.AdoptRepository(ctx, ctx.Doer, ctxUser, repo_module.CreateRepoOptions{ Name: dirSplit[1], IsPrivate: true, }); err != nil { @@ -157,7 +157,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("repo.adopt_preexisting_success", dir)) } else if action == "delete" { - if err := repo_service.DeleteUnadoptedRepository(ctx.Doer, ctxUser, dirSplit[1]); err != nil { + if err := repo_service.DeleteUnadoptedRepository(ctx, ctx.Doer, ctxUser, dirSplit[1]); err != nil { ctx.ServerError("repository.AdoptRepository", err) return } diff --git a/routers/web/org/setting.go b/routers/web/org/setting.go index 5c9b7967c3dc2..f713d096639bc 100644 --- a/routers/web/org/setting.go +++ b/routers/web/org/setting.go @@ -137,7 +137,7 @@ func SettingsPost(ctx *context.Context) { } for _, repo := range repos { repo.OwnerName = org.Name - if err := repo_service.UpdateRepository(repo, true); err != nil { + if err := repo_service.UpdateRepository(ctx, repo, true); err != nil { ctx.ServerError("UpdateRepository", err) return } diff --git a/routers/web/repo/branch.go b/routers/web/repo/branch.go index b34ccf8538553..d23367e04790d 100644 --- a/routers/web/repo/branch.go +++ b/routers/web/repo/branch.go @@ -91,7 +91,7 @@ func DeleteBranchPost(ctx *context.Context) { defer redirect(ctx) branchName := ctx.FormString("name") - if err := repo_service.DeleteBranch(ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil { + if err := repo_service.DeleteBranch(ctx, ctx.Doer, ctx.Repo.Repository, ctx.Repo.GitRepo, branchName); err != nil { switch { case git.IsErrBranchNotExist(err): log.Debug("DeleteBranch: Can't delete non existing branch '%s'", branchName) diff --git a/routers/web/repo/http.go b/routers/web/repo/http.go index 002843d103c9a..cd32d99533dc2 100644 --- a/routers/web/repo/http.go +++ b/routers/web/repo/http.go @@ -276,7 +276,7 @@ func httpBase(ctx *context.Context) (h *serviceHandler) { return } - repo, err = repo_service.PushCreateRepo(ctx.Doer, owner, reponame) + repo, err = repo_service.PushCreateRepo(ctx, ctx.Doer, owner, reponame) if err != nil { log.Error("pushCreateRepo: %v", err) ctx.Status(http.StatusNotFound) diff --git a/routers/web/repo/issue.go b/routers/web/repo/issue.go index e4f94006155a2..57575061ae535 100644 --- a/routers/web/repo/issue.go +++ b/routers/web/repo/issue.go @@ -589,7 +589,7 @@ func RetrieveRepoReviewers(ctx *context.Context, repo *repo_model.Repository, is return } - teamReviewers, err = repo_service.GetReviewerTeams(repo) + teamReviewers, err = repo_service.GetReviewerTeams(ctx, repo) if err != nil { ctx.ServerError("GetReviewerTeams", err) return diff --git a/routers/web/repo/pull.go b/routers/web/repo/pull.go index 38b9f22cbf3dd..78c935a8c8b7d 100644 --- a/routers/web/repo/pull.go +++ b/routers/web/repo/pull.go @@ -1399,7 +1399,7 @@ func CleanUpPullRequest(ctx *context.Context) { func deleteBranch(ctx *context.Context, pr *issues_model.PullRequest, gitRepo *git.Repository) { fullBranchName := pr.HeadRepo.FullName() + ":" + pr.HeadBranch - if err := repo_service.DeleteBranch(ctx.Doer, pr.HeadRepo, gitRepo, pr.HeadBranch); err != nil { + if err := repo_service.DeleteBranch(ctx, ctx.Doer, pr.HeadRepo, gitRepo, pr.HeadBranch); err != nil { switch { case git.IsErrBranchNotExist(err): ctx.Flash.Error(ctx.Tr("repo.branch.deletion_failed", fullBranchName)) diff --git a/routers/web/repo/repo.go b/routers/web/repo/repo.go index 9f2add1fe6de6..b4e7b5a46e2a7 100644 --- a/routers/web/repo/repo.go +++ b/routers/web/repo/repo.go @@ -248,14 +248,14 @@ func CreatePost(ctx *context.Context) { return } - repo, err = repo_service.GenerateRepository(ctx.Doer, ctxUser, templateRepo, opts) + repo, err = repo_service.GenerateRepository(ctx, ctx.Doer, ctxUser, templateRepo, opts) if err == nil { log.Trace("Repository generated [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name) ctx.Redirect(repo.Link()) return } } else { - repo, err = repo_service.CreateRepository(ctx.Doer, ctxUser, repo_module.CreateRepoOptions{ + repo, err = repo_service.CreateRepository(ctx, ctx.Doer, ctxUser, repo_module.CreateRepoOptions{ Name: form.RepoName, Description: form.Description, Gitignores: form.Gitignores, @@ -302,7 +302,7 @@ func Action(ctx *context.Context) { ctx.Repo.Repository.Description = ctx.FormString("desc") ctx.Repo.Repository.Website = ctx.FormString("site") - err = repo_service.UpdateRepository(ctx.Repo.Repository, false) + err = repo_service.UpdateRepository(ctx, ctx.Repo.Repository, false) } if err != nil { diff --git a/routers/web/repo/setting.go b/routers/web/repo/setting.go index 387a917412f1e..0c36503b3c4fc 100644 --- a/routers/web/repo/setting.go +++ b/routers/web/repo/setting.go @@ -134,7 +134,7 @@ func SettingsPost(ctx *context.Context) { ctx.Repo.GitRepo.Close() ctx.Repo.GitRepo = nil } - if err := repo_service.ChangeRepositoryName(ctx.Doer, repo, newRepoName); err != nil { + if err := repo_service.ChangeRepositoryName(ctx, ctx.Doer, repo, newRepoName); err != nil { ctx.Data["Err_RepoName"] = true switch { case repo_model.IsErrRepoAlreadyExist(err): @@ -183,7 +183,7 @@ func SettingsPost(ctx *context.Context) { } repo.IsPrivate = form.Private - if err := repo_service.UpdateRepository(repo, visibilityChanged); err != nil { + if err := repo_service.UpdateRepository(ctx, repo, visibilityChanged); err != nil { ctx.ServerError("UpdateRepository", err) return } @@ -541,7 +541,7 @@ func SettingsPost(ctx *context.Context) { return } if repoChanged { - if err := repo_service.UpdateRepository(repo, false); err != nil { + if err := repo_service.UpdateRepository(ctx, repo, false); err != nil { ctx.ServerError("UpdateRepository", err) return } @@ -560,7 +560,7 @@ func SettingsPost(ctx *context.Context) { } if changed { - if err := repo_service.UpdateRepository(repo, false); err != nil { + if err := repo_service.UpdateRepository(ctx, repo, false); err != nil { ctx.ServerError("UpdateRepository", err) return } @@ -580,7 +580,7 @@ func SettingsPost(ctx *context.Context) { repo.IsFsckEnabled = form.EnableHealthCheck } - if err := repo_service.UpdateRepository(repo, false); err != nil { + if err := repo_service.UpdateRepository(ctx, repo, false); err != nil { ctx.ServerError("UpdateRepository", err) return } @@ -672,7 +672,7 @@ func SettingsPost(ctx *context.Context) { return } - if err := repo_service.ConvertForkToNormalRepository(repo); err != nil { + if err := repo_service.ConvertForkToNormalRepository(ctx, repo); err != nil { log.Error("Unable to convert repository %-v from fork. Error: %v", repo, err) ctx.ServerError("Convert Fork", err) return @@ -1244,7 +1244,7 @@ func UpdateAvatarSetting(ctx *context.Context, form forms.AvatarForm) error { if !(st.IsImage() && !st.IsSvgImage()) { return errors.New(ctx.Tr("settings.uploaded_avatar_not_a_image")) } - if err = repo_service.UploadAvatar(ctxRepo, data); err != nil { + if err = repo_service.UploadAvatar(ctx, ctxRepo, data); err != nil { return fmt.Errorf("UploadAvatar: %w", err) } return nil @@ -1264,7 +1264,7 @@ func SettingsAvatar(ctx *context.Context) { // SettingsDeleteAvatar delete repository avatar func SettingsDeleteAvatar(ctx *context.Context) { - if err := repo_service.DeleteAvatar(ctx.Repo.Repository); err != nil { + if err := repo_service.DeleteAvatar(ctx, ctx.Repo.Repository); err != nil { ctx.Flash.Error(fmt.Sprintf("DeleteAvatar: %v", err)) } ctx.Redirect(ctx.Repo.RepoLink + "/settings") diff --git a/routers/web/repo/setting_protected_branch.go b/routers/web/repo/setting_protected_branch.go index 0a8c39fef0735..34e84c4656827 100644 --- a/routers/web/repo/setting_protected_branch.go +++ b/routers/web/repo/setting_protected_branch.go @@ -356,7 +356,7 @@ func RenameBranchPost(ctx *context.Context) { return } - msg, err := repository.RenameBranch(ctx.Repo.Repository, ctx.Doer, ctx.Repo.GitRepo, form.From, form.To) + msg, err := repository.RenameBranch(ctx, ctx.Repo.Repository, ctx.Doer, ctx.Repo.GitRepo, form.From, form.To) if err != nil { ctx.ServerError("RenameBranch", err) return diff --git a/routers/web/user/setting/adopt.go b/routers/web/user/setting/adopt.go index 844d6fa166a1d..c9995e7278c55 100644 --- a/routers/web/user/setting/adopt.go +++ b/routers/web/user/setting/adopt.go @@ -45,7 +45,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) { if has || !isDir { // Fallthrough to failure mode } else if action == "adopt" && allowAdopt { - if _, err := repo_service.AdoptRepository(ctxUser, ctxUser, repo_module.CreateRepoOptions{ + if _, err := repo_service.AdoptRepository(ctx, ctxUser, ctxUser, repo_module.CreateRepoOptions{ Name: dir, IsPrivate: true, }); err != nil { @@ -54,7 +54,7 @@ func AdoptOrDeleteRepository(ctx *context.Context) { } ctx.Flash.Success(ctx.Tr("repo.adopt_preexisting_success", dir)) } else if action == "delete" && allowDelete { - if err := repo_service.DeleteUnadoptedRepository(ctxUser, ctxUser, dir); err != nil { + if err := repo_service.DeleteUnadoptedRepository(ctx, ctxUser, ctxUser, dir); err != nil { ctx.ServerError("repository.AdoptRepository", err) return } diff --git a/services/repository/adopt.go b/services/repository/adopt.go index 280c4cc035a8f..94b2c3f3d573f 100644 --- a/services/repository/adopt.go +++ b/services/repository/adopt.go @@ -26,7 +26,7 @@ import ( ) // AdoptRepository adopts pre-existing repository files for the user/organization. -func AdoptRepository(doer, u *user_model.User, opts repo_module.CreateRepoOptions) (*repo_model.Repository, error) { +func AdoptRepository(ctx context.Context, doer, u *user_model.User, opts repo_module.CreateRepoOptions) (*repo_model.Repository, error) { if !doer.IsAdmin && !u.CanCreateRepo() { return nil, repo_model.ErrReachLimitOfRepo{ Limit: u.MaxRepoCreation, @@ -53,7 +53,7 @@ func AdoptRepository(doer, u *user_model.User, opts repo_module.CreateRepoOption IsEmpty: !opts.AutoInit, } - if err := db.WithTx(db.DefaultContext, func(ctx context.Context) error { + if err := db.WithTx(ctx, func(ctx context.Context) error { repoPath := repo_model.RepoPath(u.Name, repo.Name) isExist, err := util.IsExist(repoPath) if err != nil { @@ -95,7 +95,7 @@ func AdoptRepository(doer, u *user_model.User, opts repo_module.CreateRepoOption return nil, err } - notification.NotifyCreateRepository(db.DefaultContext, doer, u, repo) + notification.NotifyCreateRepository(ctx, doer, u, repo) return repo, nil } @@ -188,7 +188,7 @@ func adoptRepository(ctx context.Context, repoPath string, u *user_model.User, r } // DeleteUnadoptedRepository deletes unadopted repository files from the filesystem -func DeleteUnadoptedRepository(doer, u *user_model.User, repoName string) error { +func DeleteUnadoptedRepository(ctx context.Context, doer, u *user_model.User, repoName string) error { if err := repo_model.IsUsableRepoName(repoName); err != nil { return err } @@ -206,7 +206,7 @@ func DeleteUnadoptedRepository(doer, u *user_model.User, repoName string) error } } - if exist, err := repo_model.IsRepositoryExist(db.DefaultContext, u, repoName); err != nil { + if exist, err := repo_model.IsRepositoryExist(ctx, u, repoName); err != nil { return err } else if exist { return repo_model.ErrRepoAlreadyExist{ @@ -232,11 +232,11 @@ func (unadopted *unadoptedRepositories) add(repository string) { unadopted.index++ } -func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unadopted *unadoptedRepositories) error { +func checkUnadoptedRepositories(ctx context.Context, userName string, repoNamesToCheck []string, unadopted *unadoptedRepositories) error { if len(repoNamesToCheck) == 0 { return nil } - ctxUser, err := user_model.GetUserByName(db.DefaultContext, userName) + ctxUser, err := user_model.GetUserByName(ctx, userName) if err != nil { if user_model.IsErrUserNotExist(err) { log.Debug("Missing user: %s", userName) @@ -271,7 +271,7 @@ func checkUnadoptedRepositories(userName string, repoNamesToCheck []string, unad } // ListUnadoptedRepositories lists all the unadopted repositories that match the provided query -func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, int, error) { +func ListUnadoptedRepositories(ctx context.Context, query string, opts *db.ListOptions) ([]string, int, error) { globUser, _ := glob.Compile("*") globRepo, _ := glob.Compile("*") @@ -315,7 +315,7 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in if !strings.ContainsRune(path[len(root)+1:], filepath.Separator) { // Got a new user - if err = checkUnadoptedRepositories(userName, repoNamesToCheck, unadopted); err != nil { + if err = checkUnadoptedRepositories(ctx, userName, repoNamesToCheck, unadopted); err != nil { return err } repoNamesToCheck = repoNamesToCheck[:0] @@ -338,7 +338,7 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in repoNamesToCheck = append(repoNamesToCheck, name) if len(repoNamesToCheck) >= setting.Database.IterateBufferSize { - if err = checkUnadoptedRepositories(userName, repoNamesToCheck, unadopted); err != nil { + if err = checkUnadoptedRepositories(ctx, userName, repoNamesToCheck, unadopted); err != nil { return err } repoNamesToCheck = repoNamesToCheck[:0] @@ -349,7 +349,7 @@ func ListUnadoptedRepositories(query string, opts *db.ListOptions) ([]string, in return nil, 0, err } - if err := checkUnadoptedRepositories(userName, repoNamesToCheck, unadopted); err != nil { + if err := checkUnadoptedRepositories(ctx, userName, repoNamesToCheck, unadopted); err != nil { return nil, 0, err } diff --git a/services/repository/adopt_test.go b/services/repository/adopt_test.go index be8897693eb8f..3f777c425fb15 100644 --- a/services/repository/adopt_test.go +++ b/services/repository/adopt_test.go @@ -39,7 +39,7 @@ func TestCheckUnadoptedRepositories(t *testing.T) { // Non existent user // unadopted := &unadoptedRepositories{start: 0, end: 100} - err := checkUnadoptedRepositories("notauser", []string{"repo"}, unadopted) + err := checkUnadoptedRepositories(db.DefaultContext, "notauser", []string{"repo"}, unadopted) assert.NoError(t, err) assert.Equal(t, 0, len(unadopted.repositories)) // @@ -50,14 +50,14 @@ func TestCheckUnadoptedRepositories(t *testing.T) { repoName := "repo2" unadoptedRepoName := "unadopted" unadopted = &unadoptedRepositories{start: 0, end: 100} - err = checkUnadoptedRepositories(userName, []string{repoName, unadoptedRepoName}, unadopted) + err = checkUnadoptedRepositories(db.DefaultContext, userName, []string{repoName, unadoptedRepoName}, unadopted) assert.NoError(t, err) assert.Equal(t, []string{path.Join(userName, unadoptedRepoName)}, unadopted.repositories) // // Existing (adopted) repository is not returned // unadopted = &unadoptedRepositories{start: 0, end: 100} - err = checkUnadoptedRepositories(userName, []string{repoName}, unadopted) + err = checkUnadoptedRepositories(db.DefaultContext, userName, []string{repoName}, unadopted) assert.NoError(t, err) assert.Equal(t, 0, len(unadopted.repositories)) assert.Equal(t, 0, unadopted.index) @@ -72,13 +72,13 @@ func TestListUnadoptedRepositories_ListOptions(t *testing.T) { } opts := db.ListOptions{Page: 1, PageSize: 1} - repoNames, count, err := ListUnadoptedRepositories("", &opts) + repoNames, count, err := ListUnadoptedRepositories(db.DefaultContext, "", &opts) assert.NoError(t, err) assert.Equal(t, 2, count) assert.Equal(t, unadoptedList[0], repoNames[0]) opts = db.ListOptions{Page: 2, PageSize: 1} - repoNames, count, err = ListUnadoptedRepositories("", &opts) + repoNames, count, err = ListUnadoptedRepositories(db.DefaultContext, "", &opts) assert.NoError(t, err) assert.Equal(t, 2, count) assert.Equal(t, unadoptedList[1], repoNames[0]) diff --git a/services/repository/avatar.go b/services/repository/avatar.go index 5fe8bd2c72f88..74e5de877e0ca 100644 --- a/services/repository/avatar.go +++ b/services/repository/avatar.go @@ -20,7 +20,7 @@ import ( // UploadAvatar saves custom avatar for repository. // FIXME: split uploads to different subdirs in case we have massive number of repos. -func UploadAvatar(repo *repo_model.Repository, data []byte) error { +func UploadAvatar(ctx context.Context, repo *repo_model.Repository, data []byte) error { m, err := avatar.Prepare(data) if err != nil { return err @@ -31,7 +31,7 @@ func UploadAvatar(repo *repo_model.Repository, data []byte) error { return nil } - ctx, committer, err := db.TxContext(db.DefaultContext) + ctx, committer, err := db.TxContext(ctx) if err != nil { return err } @@ -65,7 +65,7 @@ func UploadAvatar(repo *repo_model.Repository, data []byte) error { } // DeleteAvatar deletes the repos's custom avatar. -func DeleteAvatar(repo *repo_model.Repository) error { +func DeleteAvatar(ctx context.Context, repo *repo_model.Repository) error { // Avatar not exists if len(repo.Avatar) == 0 { return nil @@ -74,7 +74,7 @@ func DeleteAvatar(repo *repo_model.Repository) error { avatarPath := repo.CustomAvatarRelativePath() log.Trace("DeleteAvatar[%d]: %s", repo.ID, avatarPath) - ctx, committer, err := db.TxContext(db.DefaultContext) + ctx, committer, err := db.TxContext(ctx) if err != nil { return err } @@ -102,7 +102,7 @@ func RemoveRandomAvatars(ctx context.Context) error { } stringifiedID := strconv.FormatInt(repository.ID, 10) if repository.Avatar == stringifiedID { - return DeleteAvatar(repository) + return DeleteAvatar(ctx, repository) } return nil }) diff --git a/services/repository/avatar_test.go b/services/repository/avatar_test.go index 5ec899ec3f9a7..4a0ba61853895 100644 --- a/services/repository/avatar_test.go +++ b/services/repository/avatar_test.go @@ -9,6 +9,7 @@ import ( "image/png" "testing" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" "code.gitea.io/gitea/modules/avatar" @@ -25,7 +26,7 @@ func TestUploadAvatar(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}) - err := UploadAvatar(repo, buff.Bytes()) + err := UploadAvatar(db.DefaultContext, repo, buff.Bytes()) assert.NoError(t, err) assert.Equal(t, avatar.HashAvatar(10, buff.Bytes()), repo.Avatar) } @@ -39,7 +40,7 @@ func TestUploadBigAvatar(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}) - err := UploadAvatar(repo, buff.Bytes()) + err := UploadAvatar(db.DefaultContext, repo, buff.Bytes()) assert.Error(t, err) } @@ -52,10 +53,10 @@ func TestDeleteAvatar(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 10}) - err := UploadAvatar(repo, buff.Bytes()) + err := UploadAvatar(db.DefaultContext, repo, buff.Bytes()) assert.NoError(t, err) - err = DeleteAvatar(repo) + err = DeleteAvatar(db.DefaultContext, repo) assert.NoError(t, err) assert.Equal(t, "", repo.Avatar) diff --git a/services/repository/branch.go b/services/repository/branch.go index 291fb4a92b374..a085026ae1563 100644 --- a/services/repository/branch.go +++ b/services/repository/branch.go @@ -10,7 +10,6 @@ import ( "strings" "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" user_model "code.gitea.io/gitea/models/user" @@ -106,7 +105,7 @@ func CreateNewBranchFromCommit(ctx context.Context, doer *user_model.User, repo } // RenameBranch rename a branch -func RenameBranch(repo *repo_model.Repository, doer *user_model.User, gitRepo *git.Repository, from, to string) (string, error) { +func RenameBranch(ctx context.Context, repo *repo_model.Repository, doer *user_model.User, gitRepo *git.Repository, from, to string) (string, error) { if from == to { return "target_exist", nil } @@ -119,7 +118,7 @@ func RenameBranch(repo *repo_model.Repository, doer *user_model.User, gitRepo *g return "from_not_exist", nil } - if err := git_model.RenameBranch(db.DefaultContext, repo, from, to, func(isDefault bool) error { + if err := git_model.RenameBranch(ctx, repo, from, to, func(isDefault bool) error { err2 := gitRepo.RenameBranch(from, to) if err2 != nil { return err2 @@ -141,8 +140,8 @@ func RenameBranch(repo *repo_model.Repository, doer *user_model.User, gitRepo *g return "", err } - notification.NotifyDeleteRef(db.DefaultContext, doer, repo, "branch", git.BranchPrefix+from) - notification.NotifyCreateRef(db.DefaultContext, doer, repo, "branch", git.BranchPrefix+to, refID) + notification.NotifyDeleteRef(ctx, doer, repo, "branch", git.BranchPrefix+from) + notification.NotifyCreateRef(ctx, doer, repo, "branch", git.BranchPrefix+to, refID) return "", nil } @@ -153,12 +152,12 @@ var ( ) // DeleteBranch delete branch -func DeleteBranch(doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branchName string) error { +func DeleteBranch(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, gitRepo *git.Repository, branchName string) error { if branchName == repo.DefaultBranch { return ErrBranchIsDefault } - isProtected, err := git_model.IsBranchProtected(db.DefaultContext, repo.ID, branchName) + isProtected, err := git_model.IsBranchProtected(ctx, repo.ID, branchName) if err != nil { return err } @@ -195,7 +194,7 @@ func DeleteBranch(doer *user_model.User, repo *repo_model.Repository, gitRepo *g log.Error("Update: %v", err) } - if err := git_model.AddDeletedBranch(db.DefaultContext, repo.ID, branchName, commit.ID.String(), doer.ID); err != nil { + if err := git_model.AddDeletedBranch(ctx, repo.ID, branchName, commit.ID.String(), doer.ID); err != nil { log.Warn("AddDeletedBranch: %v", err) } diff --git a/services/repository/fork.go b/services/repository/fork.go index c3ca89e02e7f8..fb93b10f1c312 100644 --- a/services/repository/fork.go +++ b/services/repository/fork.go @@ -189,8 +189,8 @@ func ForkRepository(ctx context.Context, doer, owner *user_model.User, opts Fork } // ConvertForkToNormalRepository convert the provided repo from a forked repo to normal repo -func ConvertForkToNormalRepository(repo *repo_model.Repository) error { - err := db.WithTx(db.DefaultContext, func(ctx context.Context) error { +func ConvertForkToNormalRepository(ctx context.Context, repo *repo_model.Repository) error { + err := db.WithTx(ctx, func(ctx context.Context) error { repo, err := repo_model.GetRepositoryByID(ctx, repo.ID) if err != nil { return err diff --git a/services/repository/push.go b/services/repository/push.go index 8aa8be6aa2fd6..355c2878113fd 100644 --- a/services/repository/push.go +++ b/services/repository/push.go @@ -80,6 +80,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { ctx, _, finished := process.GetManager().AddContext(graceful.GetManager().HammerContext(), fmt.Sprintf("PushUpdates: %s/%s", optsList[0].RepoUserName, optsList[0].RepoName)) defer finished() + ctx = cache.WithCacheContext(ctx) repo, err := repo_model.GetRepositoryByOwnerAndName(ctx, optsList[0].RepoUserName, optsList[0].RepoName) if err != nil { @@ -122,7 +123,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { tagName := opts.TagName() if opts.IsDelRef() { notification.NotifyPushCommits( - db.DefaultContext, pusher, repo, + ctx, pusher, repo, &repo_module.PushUpdateOptions{ RefFullName: git.TagPrefix + tagName, OldCommitID: opts.OldCommitID, @@ -130,7 +131,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { }, repo_module.NewPushCommits()) delTags = append(delTags, tagName) - notification.NotifyDeleteRef(db.DefaultContext, pusher, repo, "tag", opts.RefFullName) + notification.NotifyDeleteRef(ctx, pusher, repo, "tag", opts.RefFullName) } else { // is new tag newCommit, err := gitRepo.GetCommit(opts.NewCommitID) if err != nil { @@ -142,7 +143,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { commits.CompareURL = repo.ComposeCompareURL(git.EmptySHA, opts.NewCommitID) notification.NotifyPushCommits( - db.DefaultContext, pusher, repo, + ctx, pusher, repo, &repo_module.PushUpdateOptions{ RefFullName: git.TagPrefix + tagName, OldCommitID: git.EmptySHA, @@ -150,7 +151,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { }, commits) addTags = append(addTags, tagName) - notification.NotifyCreateRef(db.DefaultContext, pusher, repo, "tag", opts.RefFullName, opts.NewCommitID) + notification.NotifyCreateRef(ctx, pusher, repo, "tag", opts.RefFullName, opts.NewCommitID) } } else if opts.IsBranch() { // If is branch reference if pusher == nil || pusher.ID != opts.PusherID { @@ -190,7 +191,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } } // Update the is empty and default_branch columns - if err := repo_model.UpdateRepositoryCols(db.DefaultContext, repo, "default_branch", "is_empty"); err != nil { + if err := repo_model.UpdateRepositoryCols(ctx, repo, "default_branch", "is_empty"); err != nil { return fmt.Errorf("UpdateRepositoryCols: %w", err) } } @@ -199,7 +200,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { if err != nil { return fmt.Errorf("newCommit.CommitsBeforeLimit: %w", err) } - notification.NotifyCreateRef(db.DefaultContext, pusher, repo, "branch", opts.RefFullName, opts.NewCommitID) + notification.NotifyCreateRef(ctx, pusher, repo, "branch", opts.RefFullName, opts.NewCommitID) } else { l, err = newCommit.CommitsBeforeUntil(opts.OldCommitID) if err != nil { @@ -259,7 +260,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { commits.Commits = commits.Commits[:setting.UI.FeedMaxCommitNum] } - notification.NotifyPushCommits(db.DefaultContext, pusher, repo, opts, commits) + notification.NotifyPushCommits(ctx, pusher, repo, opts, commits) if err = git_model.RemoveDeletedBranchByName(ctx, repo.ID, branch); err != nil { log.Error("models.RemoveDeletedBranch %s/%s failed: %v", repo.ID, branch, err) @@ -270,7 +271,7 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { log.Error("repo_module.CacheRef %s/%s failed: %v", repo.ID, branch, err) } } else { - notification.NotifyDeleteRef(db.DefaultContext, pusher, repo, "branch", opts.RefFullName) + notification.NotifyDeleteRef(ctx, pusher, repo, "branch", opts.RefFullName) if err = pull_service.CloseBranchPulls(pusher, repo.ID, branch); err != nil { // close all related pulls log.Error("close related pull request failed: %v", err) @@ -278,14 +279,14 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } // Even if user delete a branch on a repository which he didn't watch, he will be watch that. - if err = repo_model.WatchIfAuto(db.DefaultContext, opts.PusherID, repo.ID, true); err != nil { + if err = repo_model.WatchIfAuto(ctx, opts.PusherID, repo.ID, true); err != nil { log.Warn("Fail to perform auto watch on user %v for repo %v: %v", opts.PusherID, repo.ID, err) } } else { log.Trace("Non-tag and non-branch commits pushed.") } } - if err := PushUpdateAddDeleteTags(repo, gitRepo, addTags, delTags); err != nil { + if err := PushUpdateAddDeleteTags(ctx, repo, gitRepo, addTags, delTags); err != nil { return fmt.Errorf("PushUpdateAddDeleteTags: %w", err) } @@ -298,8 +299,8 @@ func pushUpdates(optsList []*repo_module.PushUpdateOptions) error { } // PushUpdateAddDeleteTags updates a number of added and delete tags -func PushUpdateAddDeleteTags(repo *repo_model.Repository, gitRepo *git.Repository, addTags, delTags []string) error { - return db.WithTx(db.DefaultContext, func(ctx context.Context) error { +func PushUpdateAddDeleteTags(ctx context.Context, repo *repo_model.Repository, gitRepo *git.Repository, addTags, delTags []string) error { + return db.WithTx(ctx, func(ctx context.Context) error { if err := repo_model.PushUpdateDeleteTagsContext(ctx, repo, delTags); err != nil { return err } diff --git a/services/repository/repository.go b/services/repository/repository.go index 3c3e7e82c3f8f..000b1a3da6bce 100644 --- a/services/repository/repository.go +++ b/services/repository/repository.go @@ -24,14 +24,14 @@ import ( ) // CreateRepository creates a repository for the user/organization. -func CreateRepository(doer, owner *user_model.User, opts repo_module.CreateRepoOptions) (*repo_model.Repository, error) { +func CreateRepository(ctx context.Context, doer, owner *user_model.User, opts repo_module.CreateRepoOptions) (*repo_model.Repository, error) { repo, err := repo_module.CreateRepository(doer, owner, opts) if err != nil { // No need to rollback here we should do this in CreateRepository... return nil, err } - notification.NotifyCreateRepository(db.DefaultContext, doer, owner, repo) + notification.NotifyCreateRepository(ctx, doer, owner, repo) return repo, nil } @@ -55,10 +55,10 @@ func DeleteRepository(ctx context.Context, doer *user_model.User, repo *repo_mod } // PushCreateRepo creates a repository when a new repository is pushed to an appropriate namespace -func PushCreateRepo(authUser, owner *user_model.User, repoName string) (*repo_model.Repository, error) { +func PushCreateRepo(ctx context.Context, authUser, owner *user_model.User, repoName string) (*repo_model.Repository, error) { if !authUser.IsAdmin { if owner.IsOrganization() { - if ok, err := organization.CanCreateOrgRepo(db.DefaultContext, owner.ID, authUser.ID); err != nil { + if ok, err := organization.CanCreateOrgRepo(ctx, owner.ID, authUser.ID); err != nil { return nil, err } else if !ok { return nil, fmt.Errorf("cannot push-create repository for org") @@ -68,7 +68,7 @@ func PushCreateRepo(authUser, owner *user_model.User, repoName string) (*repo_mo } } - repo, err := CreateRepository(authUser, owner, repo_module.CreateRepoOptions{ + repo, err := CreateRepository(ctx, authUser, owner, repo_module.CreateRepoOptions{ Name: repoName, IsPrivate: setting.Repository.DefaultPushCreatePrivate, }) @@ -88,8 +88,8 @@ func Init() error { } // UpdateRepository updates a repository -func UpdateRepository(repo *repo_model.Repository, visibilityChanged bool) (err error) { - ctx, committer, err := db.TxContext(db.DefaultContext) +func UpdateRepository(ctx context.Context, repo *repo_model.Repository, visibilityChanged bool) (err error) { + ctx, committer, err := db.TxContext(ctx) if err != nil { return err } diff --git a/services/repository/review.go b/services/repository/review.go index 6b5f096053214..40513e6bc67ba 100644 --- a/services/repository/review.go +++ b/services/repository/review.go @@ -4,20 +4,21 @@ package repository import ( - "code.gitea.io/gitea/models/db" + "context" + "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" repo_model "code.gitea.io/gitea/models/repo" ) // GetReviewerTeams get all teams can be requested to review -func GetReviewerTeams(repo *repo_model.Repository) ([]*organization.Team, error) { - if err := repo.LoadOwner(db.DefaultContext); err != nil { +func GetReviewerTeams(ctx context.Context, repo *repo_model.Repository) ([]*organization.Team, error) { + if err := repo.LoadOwner(ctx); err != nil { return nil, err } if !repo.Owner.IsOrganization() { return nil, nil } - return organization.GetTeamsWithAccessToRepo(db.DefaultContext, repo.OwnerID, repo.ID, perm.AccessModeRead) + return organization.GetTeamsWithAccessToRepo(ctx, repo.OwnerID, repo.ID, perm.AccessModeRead) } diff --git a/services/repository/review_test.go b/services/repository/review_test.go index 2bf4cdbf5cdf2..2db56d4e8a9cc 100644 --- a/services/repository/review_test.go +++ b/services/repository/review_test.go @@ -6,6 +6,7 @@ package repository import ( "testing" + "code.gitea.io/gitea/models/db" repo_model "code.gitea.io/gitea/models/repo" "code.gitea.io/gitea/models/unittest" @@ -16,12 +17,12 @@ func TestRepoGetReviewerTeams(t *testing.T) { assert.NoError(t, unittest.PrepareTestDatabase()) repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) - teams, err := GetReviewerTeams(repo2) + teams, err := GetReviewerTeams(db.DefaultContext, repo2) assert.NoError(t, err) assert.Empty(t, teams) repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) - teams, err = GetReviewerTeams(repo3) + teams, err = GetReviewerTeams(db.DefaultContext, repo3) assert.NoError(t, err) assert.Len(t, teams, 2) } diff --git a/services/repository/template.go b/services/repository/template.go index 8c75948c418b8..42174d095b035 100644 --- a/services/repository/template.go +++ b/services/repository/template.go @@ -40,7 +40,7 @@ func GenerateIssueLabels(ctx context.Context, templateRepo, generateRepo *repo_m } // GenerateRepository generates a repository from a template -func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.Repository, opts repo_module.GenerateRepoOptions) (_ *repo_model.Repository, err error) { +func GenerateRepository(ctx context.Context, doer, owner *user_model.User, templateRepo *repo_model.Repository, opts repo_module.GenerateRepoOptions) (_ *repo_model.Repository, err error) { if !doer.IsAdmin && !owner.CanCreateRepo() { return nil, repo_model.ErrReachLimitOfRepo{ Limit: owner.MaxRepoCreation, @@ -48,7 +48,7 @@ func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.R } var generateRepo *repo_model.Repository - if err = db.WithTx(db.DefaultContext, func(ctx context.Context) error { + if err = db.WithTx(ctx, func(ctx context.Context) error { generateRepo, err = repo_module.GenerateRepository(ctx, doer, owner, templateRepo, opts) if err != nil { return err @@ -101,7 +101,7 @@ func GenerateRepository(doer, owner *user_model.User, templateRepo *repo_model.R return nil, err } - notification.NotifyCreateRepository(db.DefaultContext, doer, owner, generateRepo) + notification.NotifyCreateRepository(ctx, doer, owner, generateRepo) return generateRepo, nil } diff --git a/services/repository/transfer.go b/services/repository/transfer.go index 8c167552da06f..b9b26f314c2c3 100644 --- a/services/repository/transfer.go +++ b/services/repository/transfer.go @@ -8,7 +8,6 @@ import ( "fmt" "code.gitea.io/gitea/models" - "code.gitea.io/gitea/models/db" "code.gitea.io/gitea/models/organization" "code.gitea.io/gitea/models/perm" access_model "code.gitea.io/gitea/models/perm/access" @@ -61,7 +60,7 @@ func TransferOwnership(ctx context.Context, doer, newOwner *user_model.User, rep } // ChangeRepositoryName changes all corresponding setting from old repository name to new one. -func ChangeRepositoryName(doer *user_model.User, repo *repo_model.Repository, newRepoName string) error { +func ChangeRepositoryName(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, newRepoName string) error { log.Trace("ChangeRepositoryName: %s/%s -> %s", doer.Name, repo.Name, newRepoName) oldRepoName := repo.Name @@ -78,7 +77,7 @@ func ChangeRepositoryName(doer *user_model.User, repo *repo_model.Repository, ne repoWorkingPool.CheckOut(fmt.Sprint(repo.ID)) repo.Name = newRepoName - notification.NotifyRenameRepository(db.DefaultContext, doer, repo, oldRepoName) + notification.NotifyRenameRepository(ctx, doer, repo, oldRepoName) return nil } diff --git a/tests/integration/pull_merge_test.go b/tests/integration/pull_merge_test.go index 090a27c39efbc..55cf295257a17 100644 --- a/tests/integration/pull_merge_test.go +++ b/tests/integration/pull_merge_test.go @@ -356,7 +356,7 @@ func TestConflictChecking(t *testing.T) { user := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 2}) // Create new clean repo to test conflict checking. - baseRepo, err := repo_service.CreateRepository(user, user, repo_module.CreateRepoOptions{ + baseRepo, err := repo_service.CreateRepository(db.DefaultContext, user, user, repo_module.CreateRepoOptions{ Name: "conflict-checking", Description: "Tempo repo", AutoInit: true, diff --git a/tests/integration/pull_update_test.go b/tests/integration/pull_update_test.go index bd416e5bcfac3..1b66656518a30 100644 --- a/tests/integration/pull_update_test.go +++ b/tests/integration/pull_update_test.go @@ -80,7 +80,7 @@ func TestAPIPullUpdateByRebase(t *testing.T) { } func createOutdatedPR(t *testing.T, actor, forkOrg *user_model.User) *issues_model.PullRequest { - baseRepo, err := repo_service.CreateRepository(actor, actor, repo_module.CreateRepoOptions{ + baseRepo, err := repo_service.CreateRepository(db.DefaultContext, actor, actor, repo_module.CreateRepoOptions{ Name: "repo-pr-update", Description: "repo-tmp-pr-update description", AutoInit: true, From 27e49cd01cf33c7adfd7e1a897d95884f7714aca Mon Sep 17 00:00:00 2001 From: zeripath Date: Tue, 28 Feb 2023 22:55:43 +0000 Subject: [PATCH 6/7] Properly flush unique queues on startup (#23154) There have been a number of reports of PRs being blocked whilst being checked which have been difficult to debug. In investigating #23050 I have realised that whilst the Warn there is somewhat of a miscall there was a real bug in the way that the LevelUniqueQueue was being restored on start-up of the PersistableChannelUniqueQueue. Next there is a conflict in the setting of the internal leveldb queue name - This wasn't being set so it was being overridden by other unique queues. This PR fixes these bugs and adds a testcase. Thanks to @brechtvl for noticing the second issue. Fix #23050 and others --------- Signed-off-by: Andrew Thornton Co-authored-by: techknowlogick --- modules/queue/queue_channel.go | 5 +- modules/queue/queue_disk_channel.go | 29 +- modules/queue/queue_disk_channel_test.go | 8 +- modules/queue/unique_queue_channel.go | 4 +- modules/queue/unique_queue_channel_test.go | 7 + modules/queue/unique_queue_disk_channel.go | 41 ++- .../queue/unique_queue_disk_channel_test.go | 259 ++++++++++++++++++ 7 files changed, 332 insertions(+), 21 deletions(-) create mode 100644 modules/queue/unique_queue_disk_channel_test.go diff --git a/modules/queue/queue_channel.go b/modules/queue/queue_channel.go index 6f75b8357eabc..baac097393956 100644 --- a/modules/queue/queue_channel.go +++ b/modules/queue/queue_channel.go @@ -124,7 +124,10 @@ func (q *ChannelQueue) Shutdown() { log.Trace("ChannelQueue: %s Flushing", q.name) // We can't use Cleanup here because that will close the channel if err := q.FlushWithContext(q.terminateCtx); err != nil { - log.Warn("ChannelQueue: %s Terminated before completed flushing", q.name) + count := atomic.LoadInt64(&q.numInQueue) + if count > 0 { + log.Warn("ChannelQueue: %s Terminated before completed flushing", q.name) + } return } log.Debug("ChannelQueue: %s Flushed", q.name) diff --git a/modules/queue/queue_disk_channel.go b/modules/queue/queue_disk_channel.go index c7526714c65cb..91f91f0dfc80d 100644 --- a/modules/queue/queue_disk_channel.go +++ b/modules/queue/queue_disk_channel.go @@ -94,7 +94,8 @@ func NewPersistableChannelQueue(handle HandlerFunc, cfg, exemplar interface{}) ( }, Workers: 0, }, - DataDir: config.DataDir, + DataDir: config.DataDir, + QueueName: config.Name + "-level", } levelQueue, err := NewLevelQueue(wrappedHandle, levelCfg, exemplar) @@ -172,16 +173,18 @@ func (q *PersistableChannelQueue) Run(atShutdown, atTerminate func(func())) { atShutdown(q.Shutdown) atTerminate(q.Terminate) - if lq, ok := q.internal.(*LevelQueue); ok && lq.byteFIFO.Len(lq.shutdownCtx) != 0 { + if lq, ok := q.internal.(*LevelQueue); ok && lq.byteFIFO.Len(lq.terminateCtx) != 0 { // Just run the level queue - we shut it down once it's flushed go q.internal.Run(func(_ func()) {}, func(_ func()) {}) go func() { - for !q.IsEmpty() { - _ = q.internal.Flush(0) + for !lq.IsEmpty() { + _ = lq.Flush(0) select { case <-time.After(100 * time.Millisecond): - case <-q.internal.(*LevelQueue).shutdownCtx.Done(): - log.Warn("LevelQueue: %s shut down before completely flushed", q.internal.(*LevelQueue).Name()) + case <-lq.shutdownCtx.Done(): + if lq.byteFIFO.Len(lq.terminateCtx) > 0 { + log.Warn("LevelQueue: %s shut down before completely flushed", q.internal.(*LevelQueue).Name()) + } return } } @@ -316,10 +319,22 @@ func (q *PersistableChannelQueue) Shutdown() { // Redirect all remaining data in the chan to the internal channel log.Trace("PersistableChannelQueue: %s Redirecting remaining data", q.delayedStarter.name) close(q.channelQueue.dataChan) + countOK, countLost := 0, 0 for data := range q.channelQueue.dataChan { - _ = q.internal.Push(data) + err := q.internal.Push(data) + if err != nil { + log.Error("PersistableChannelQueue: %s Unable redirect %v due to: %v", q.delayedStarter.name, data, err) + countLost++ + } else { + countOK++ + } atomic.AddInt64(&q.channelQueue.numInQueue, -1) } + if countLost > 0 { + log.Warn("PersistableChannelQueue: %s %d will be restored on restart, %d lost", q.delayedStarter.name, countOK, countLost) + } else if countOK > 0 { + log.Warn("PersistableChannelQueue: %s %d will be restored on restart", q.delayedStarter.name, countOK) + } log.Trace("PersistableChannelQueue: %s Done Redirecting remaining data", q.delayedStarter.name) log.Debug("PersistableChannelQueue: %s Shutdown", q.delayedStarter.name) diff --git a/modules/queue/queue_disk_channel_test.go b/modules/queue/queue_disk_channel_test.go index 318610355e433..4f14a5d79df92 100644 --- a/modules/queue/queue_disk_channel_test.go +++ b/modules/queue/queue_disk_channel_test.go @@ -39,7 +39,7 @@ func TestPersistableChannelQueue(t *testing.T) { Workers: 1, BoostWorkers: 0, MaxWorkers: 10, - Name: "first", + Name: "test-queue", }, &testData{}) assert.NoError(t, err) @@ -135,7 +135,7 @@ func TestPersistableChannelQueue(t *testing.T) { Workers: 1, BoostWorkers: 0, MaxWorkers: 10, - Name: "second", + Name: "test-queue", }, &testData{}) assert.NoError(t, err) @@ -227,7 +227,7 @@ func TestPersistableChannelQueue_Pause(t *testing.T) { Workers: 1, BoostWorkers: 0, MaxWorkers: 10, - Name: "first", + Name: "test-queue", }, &testData{}) assert.NoError(t, err) @@ -433,7 +433,7 @@ func TestPersistableChannelQueue_Pause(t *testing.T) { Workers: 1, BoostWorkers: 0, MaxWorkers: 10, - Name: "second", + Name: "test-queue", }, &testData{}) assert.NoError(t, err) pausable, ok = queue.(Pausable) diff --git a/modules/queue/unique_queue_channel.go b/modules/queue/unique_queue_channel.go index c43bd1db3f7da..62c051aa3935e 100644 --- a/modules/queue/unique_queue_channel.go +++ b/modules/queue/unique_queue_channel.go @@ -177,7 +177,9 @@ func (q *ChannelUniqueQueue) Shutdown() { go func() { log.Trace("ChannelUniqueQueue: %s Flushing", q.name) if err := q.FlushWithContext(q.terminateCtx); err != nil { - log.Warn("ChannelUniqueQueue: %s Terminated before completed flushing", q.name) + if !q.IsEmpty() { + log.Warn("ChannelUniqueQueue: %s Terminated before completed flushing", q.name) + } return } log.Debug("ChannelUniqueQueue: %s Flushed", q.name) diff --git a/modules/queue/unique_queue_channel_test.go b/modules/queue/unique_queue_channel_test.go index 9372694b87a6d..824015b834fee 100644 --- a/modules/queue/unique_queue_channel_test.go +++ b/modules/queue/unique_queue_channel_test.go @@ -8,10 +8,13 @@ import ( "testing" "time" + "code.gitea.io/gitea/modules/log" + "github.com/stretchr/testify/assert" ) func TestChannelUniqueQueue(t *testing.T) { + _ = log.NewLogger(1000, "console", "console", `{"level":"warn","stacktracelevel":"NONE","stderr":true}`) handleChan := make(chan *testData) handle := func(data ...Data) []Data { for _, datum := range data { @@ -52,6 +55,8 @@ func TestChannelUniqueQueue(t *testing.T) { } func TestChannelUniqueQueue_Batch(t *testing.T) { + _ = log.NewLogger(1000, "console", "console", `{"level":"warn","stacktracelevel":"NONE","stderr":true}`) + handleChan := make(chan *testData) handle := func(data ...Data) []Data { for _, datum := range data { @@ -98,6 +103,8 @@ func TestChannelUniqueQueue_Batch(t *testing.T) { } func TestChannelUniqueQueue_Pause(t *testing.T) { + _ = log.NewLogger(1000, "console", "console", `{"level":"warn","stacktracelevel":"NONE","stderr":true}`) + lock := sync.Mutex{} var queue Queue var err error diff --git a/modules/queue/unique_queue_disk_channel.go b/modules/queue/unique_queue_disk_channel.go index 405726182dcbc..cc8a807c67237 100644 --- a/modules/queue/unique_queue_disk_channel.go +++ b/modules/queue/unique_queue_disk_channel.go @@ -94,7 +94,8 @@ func NewPersistableChannelUniqueQueue(handle HandlerFunc, cfg, exemplar interfac }, Workers: 0, }, - DataDir: config.DataDir, + DataDir: config.DataDir, + QueueName: config.Name + "-level", } queue.channelQueue = channelUniqueQueue.(*ChannelUniqueQueue) @@ -209,17 +210,29 @@ func (q *PersistableChannelUniqueQueue) Run(atShutdown, atTerminate func(func()) atTerminate(q.Terminate) _ = q.channelQueue.AddWorkers(q.channelQueue.workers, 0) - if luq, ok := q.internal.(*LevelUniqueQueue); ok && luq.ByteFIFOUniqueQueue.byteFIFO.Len(luq.shutdownCtx) != 0 { + if luq, ok := q.internal.(*LevelUniqueQueue); ok && !luq.IsEmpty() { // Just run the level queue - we shut it down once it's flushed - go q.internal.Run(func(_ func()) {}, func(_ func()) {}) + go luq.Run(func(_ func()) {}, func(_ func()) {}) go func() { - _ = q.internal.Flush(0) - log.Debug("LevelUniqueQueue: %s flushed so shutting down", q.internal.(*LevelUniqueQueue).Name()) - q.internal.(*LevelUniqueQueue).Shutdown() - GetManager().Remove(q.internal.(*LevelUniqueQueue).qid) + _ = luq.Flush(0) + for !luq.IsEmpty() { + _ = luq.Flush(0) + select { + case <-time.After(100 * time.Millisecond): + case <-luq.shutdownCtx.Done(): + if luq.byteFIFO.Len(luq.terminateCtx) > 0 { + log.Warn("LevelUniqueQueue: %s shut down before completely flushed", luq.Name()) + } + return + } + } + log.Debug("LevelUniqueQueue: %s flushed so shutting down", luq.Name()) + luq.Shutdown() + GetManager().Remove(luq.qid) }() } else { log.Debug("PersistableChannelUniqueQueue: %s Skipping running the empty level queue", q.delayedStarter.name) + _ = q.internal.Flush(0) q.internal.(*LevelUniqueQueue).Shutdown() GetManager().Remove(q.internal.(*LevelUniqueQueue).qid) } @@ -285,8 +298,20 @@ func (q *PersistableChannelUniqueQueue) Shutdown() { // Redirect all remaining data in the chan to the internal channel close(q.channelQueue.dataChan) log.Trace("PersistableChannelUniqueQueue: %s Redirecting remaining data", q.delayedStarter.name) + countOK, countLost := 0, 0 for data := range q.channelQueue.dataChan { - _ = q.internal.Push(data) + err := q.internal.(*LevelUniqueQueue).Push(data) + if err != nil { + log.Error("PersistableChannelUniqueQueue: %s Unable redirect %v due to: %v", q.delayedStarter.name, data, err) + countLost++ + } else { + countOK++ + } + } + if countLost > 0 { + log.Warn("PersistableChannelUniqueQueue: %s %d will be restored on restart, %d lost", q.delayedStarter.name, countOK, countLost) + } else if countOK > 0 { + log.Warn("PersistableChannelUniqueQueue: %s %d will be restored on restart", q.delayedStarter.name, countOK) } log.Trace("PersistableChannelUniqueQueue: %s Done Redirecting remaining data", q.delayedStarter.name) diff --git a/modules/queue/unique_queue_disk_channel_test.go b/modules/queue/unique_queue_disk_channel_test.go new file mode 100644 index 0000000000000..fd76163f4aaca --- /dev/null +++ b/modules/queue/unique_queue_disk_channel_test.go @@ -0,0 +1,259 @@ +// Copyright 2023 The Gitea Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package queue + +import ( + "fmt" + "strconv" + "sync" + "testing" + "time" + + "code.gitea.io/gitea/modules/log" + + "github.com/stretchr/testify/assert" +) + +func TestPersistableChannelUniqueQueue(t *testing.T) { + tmpDir := t.TempDir() + fmt.Printf("TempDir %s\n", tmpDir) + _ = log.NewLogger(1000, "console", "console", `{"level":"warn","stacktracelevel":"NONE","stderr":true}`) + + // Common function to create the Queue + newQueue := func(name string, handle func(data ...Data) []Data) Queue { + q, err := NewPersistableChannelUniqueQueue(handle, + PersistableChannelUniqueQueueConfiguration{ + Name: name, + DataDir: tmpDir, + QueueLength: 200, + MaxWorkers: 1, + BlockTimeout: 1 * time.Second, + BoostTimeout: 5 * time.Minute, + BoostWorkers: 1, + Workers: 0, + }, "task-0") + assert.NoError(t, err) + return q + } + + // runs the provided queue and provides some timer function + type channels struct { + readyForShutdown chan struct{} // closed when shutdown functions have been assigned + readyForTerminate chan struct{} // closed when terminate functions have been assigned + signalShutdown chan struct{} // Should close to signal shutdown + doneShutdown chan struct{} // closed when shutdown function is done + queueTerminate []func() // list of atTerminate functions to call atTerminate - need to be accessed with lock + } + runQueue := func(q Queue, lock *sync.Mutex) *channels { + chans := &channels{ + readyForShutdown: make(chan struct{}), + readyForTerminate: make(chan struct{}), + signalShutdown: make(chan struct{}), + doneShutdown: make(chan struct{}), + } + go q.Run(func(atShutdown func()) { + go func() { + lock.Lock() + select { + case <-chans.readyForShutdown: + default: + close(chans.readyForShutdown) + } + lock.Unlock() + <-chans.signalShutdown + atShutdown() + close(chans.doneShutdown) + }() + }, func(atTerminate func()) { + lock.Lock() + defer lock.Unlock() + select { + case <-chans.readyForTerminate: + default: + close(chans.readyForTerminate) + } + chans.queueTerminate = append(chans.queueTerminate, atTerminate) + }) + + return chans + } + + // call to shutdown and terminate the queue associated with the channels + doTerminate := func(chans *channels, lock *sync.Mutex) { + <-chans.readyForTerminate + + lock.Lock() + callbacks := []func(){} + callbacks = append(callbacks, chans.queueTerminate...) + lock.Unlock() + + for _, callback := range callbacks { + callback() + } + } + + mapLock := sync.Mutex{} + executedInitial := map[string][]string{} + hasInitial := map[string][]string{} + + fillQueue := func(name string, done chan struct{}) { + t.Run("Initial Filling: "+name, func(t *testing.T) { + lock := sync.Mutex{} + + startAt100Queued := make(chan struct{}) + stopAt20Shutdown := make(chan struct{}) // stop and shutdown at the 20th item + + handle := func(data ...Data) []Data { + <-startAt100Queued + for _, datum := range data { + s := datum.(string) + mapLock.Lock() + executedInitial[name] = append(executedInitial[name], s) + mapLock.Unlock() + if s == "task-20" { + close(stopAt20Shutdown) + } + } + return nil + } + + q := newQueue(name, handle) + + // add 100 tasks to the queue + for i := 0; i < 100; i++ { + _ = q.Push("task-" + strconv.Itoa(i)) + } + close(startAt100Queued) + + chans := runQueue(q, &lock) + + <-chans.readyForShutdown + <-stopAt20Shutdown + close(chans.signalShutdown) + <-chans.doneShutdown + _ = q.Push("final") + + // check which tasks are still in the queue + for i := 0; i < 100; i++ { + if has, _ := q.(UniqueQueue).Has("task-" + strconv.Itoa(i)); has { + mapLock.Lock() + hasInitial[name] = append(hasInitial[name], "task-"+strconv.Itoa(i)) + mapLock.Unlock() + } + } + if has, _ := q.(UniqueQueue).Has("final"); has { + mapLock.Lock() + hasInitial[name] = append(hasInitial[name], "final") + mapLock.Unlock() + } else { + assert.Fail(t, "UnqueQueue %s should have \"final\"", name) + } + doTerminate(chans, &lock) + mapLock.Lock() + assert.Equal(t, 101, len(executedInitial[name])+len(hasInitial[name])) + mapLock.Unlock() + }) + close(done) + } + + doneA := make(chan struct{}) + doneB := make(chan struct{}) + + go fillQueue("QueueA", doneA) + go fillQueue("QueueB", doneB) + + <-doneA + <-doneB + + executedEmpty := map[string][]string{} + hasEmpty := map[string][]string{} + emptyQueue := func(name string, done chan struct{}) { + t.Run("Empty Queue: "+name, func(t *testing.T) { + lock := sync.Mutex{} + stop := make(chan struct{}) + + // collect the tasks that have been executed + handle := func(data ...Data) []Data { + lock.Lock() + for _, datum := range data { + mapLock.Lock() + executedEmpty[name] = append(executedEmpty[name], datum.(string)) + mapLock.Unlock() + if datum.(string) == "final" { + close(stop) + } + } + lock.Unlock() + return nil + } + + q := newQueue(name, handle) + chans := runQueue(q, &lock) + + <-chans.readyForShutdown + <-stop + close(chans.signalShutdown) + <-chans.doneShutdown + + // check which tasks are still in the queue + for i := 0; i < 100; i++ { + if has, _ := q.(UniqueQueue).Has("task-" + strconv.Itoa(i)); has { + mapLock.Lock() + hasEmpty[name] = append(hasEmpty[name], "task-"+strconv.Itoa(i)) + mapLock.Unlock() + } + } + doTerminate(chans, &lock) + + mapLock.Lock() + assert.Equal(t, 101, len(executedInitial[name])+len(executedEmpty[name])) + assert.Equal(t, 0, len(hasEmpty[name])) + mapLock.Unlock() + }) + close(done) + } + + doneA = make(chan struct{}) + doneB = make(chan struct{}) + + go emptyQueue("QueueA", doneA) + go emptyQueue("QueueB", doneB) + + <-doneA + <-doneB + + mapLock.Lock() + t.Logf("TestPersistableChannelUniqueQueue executedInitiallyA=%v, executedInitiallyB=%v, executedToEmptyA=%v, executedToEmptyB=%v", + len(executedInitial["QueueA"]), len(executedInitial["QueueB"]), len(executedEmpty["QueueA"]), len(executedEmpty["QueueB"])) + + // reset and rerun + executedInitial = map[string][]string{} + hasInitial = map[string][]string{} + executedEmpty = map[string][]string{} + hasEmpty = map[string][]string{} + mapLock.Unlock() + + doneA = make(chan struct{}) + doneB = make(chan struct{}) + + go fillQueue("QueueA", doneA) + go fillQueue("QueueB", doneB) + + <-doneA + <-doneB + + doneA = make(chan struct{}) + doneB = make(chan struct{}) + + go emptyQueue("QueueA", doneA) + go emptyQueue("QueueB", doneB) + + <-doneA + <-doneB + + mapLock.Lock() + t.Logf("TestPersistableChannelUniqueQueue executedInitiallyA=%v, executedInitiallyB=%v, executedToEmptyA=%v, executedToEmptyB=%v", + len(executedInitial["QueueA"]), len(executedInitial["QueueB"]), len(executedEmpty["QueueA"]), len(executedEmpty["QueueB"])) + mapLock.Unlock() +} From 3e426bba78dbdd3e1369dd929de4ef7186f3d630 Mon Sep 17 00:00:00 2001 From: GiteaBot Date: Wed, 1 Mar 2023 00:16:03 +0000 Subject: [PATCH 7/7] [skip ci] Updated translations via Crowdin --- options/locale/locale_pt-BR.ini | 12 +++++++ options/locale/locale_pt-PT.ini | 59 +++++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/options/locale/locale_pt-BR.ini b/options/locale/locale_pt-BR.ini index 38127391a2dbd..c0878d563d412 100644 --- a/options/locale/locale_pt-BR.ini +++ b/options/locale/locale_pt-BR.ini @@ -248,6 +248,7 @@ no_reply_address_helper=Nome de domínio para usuários com um endereço de e-ma password_algorithm=Algoritmo Hash de Senha password_algorithm_helper=Escolha o algoritmo de hash para as senhas. Diferentes algoritmos têm requerimentos e forças diversos. O `Argon2` possui boa qualidade, porém usa muita memória e pode ser inapropriado para sistemas com menos recursos. enable_update_checker=Habilitar Verificador de Atualizações +enable_update_checker_helper=Procura por novas versões periodicamente conectando-se ao gitea.io. [home] uname_holder=Usuário ou e-mail @@ -283,7 +284,9 @@ organizations=Organizações search=Pesquisar code=Código search.fuzzy=Similar +search.fuzzy.tooltip=Incluir resultados que sejam próximos ao termo de busca search.match=Correspondência +search.match.tooltip=Incluir somente resultados que correspondam exatamente ao termo de busca code_search_unavailable=A pesquisa por código não está disponível no momento. Entre em contato com o administrador do site. repo_no_results=Nenhum repositório correspondente foi encontrado. user_no_results=Nenhum usuário correspondente foi encontrado. @@ -365,6 +368,7 @@ password_pwned_err=Não foi possível concluir a requisição ao HaveIBeenPwned [mail] view_it_on=Veja em %s +reply=ou responda diretamente a este email link_not_working_do_paste=Não está funcionando? Tente copiá-lo e colá-lo no seu navegador. hi_user_x=Olá %s, @@ -423,6 +427,10 @@ repo.transfer.body=Para o aceitar ou rejeitar visite %s, ou simplesmente o ignor repo.collaborator.added.subject=%s adicionou você a %s repo.collaborator.added.text=Você foi adicionado como um colaborador do repositório: +team_invite.subject=%[1]s convidou você para participar da organização %[2]s +team_invite.text_1=%[1]s convidou você para participar da equipe %[2]s na organização %[3]s. +team_invite.text_2=Por favor, clique no seguinte link para se juntar à equipe: +team_invite.text_3=Nota: este convite foi destinado a %[1]s. Se você não estava esperando este convite, você pode ignorar este e-mail. [modal] yes=Sim @@ -503,6 +511,7 @@ cannot_add_org_to_team=Uma organização não pode ser adicionada como membro de invalid_ssh_key=Não é possível verificar sua chave SSH: %s invalid_gpg_key=Não é possível verificar sua chave GPG: %s invalid_ssh_principal=Nome principal inválido: %s +must_use_public_key=A chave que você forneceu é uma chave privada. Por favor, não envie sua chave privada em nenhum lugar. Use sua chave pública em vez disso. unable_verify_ssh_key=Não é possível verificar sua chave SSH auth_failed=Autenticação falhou: %v @@ -1200,6 +1209,7 @@ projects.type.uncategorized=Sem categoria projects.column.edit_title=Nome projects.column.new_title=Nome projects.column.new=Nova Coluna +projects.column.delete=Excluir coluna projects.column.color=Colorido projects.open=Abrir projects.close=Fechar @@ -1773,7 +1783,9 @@ activity.git_stats_deletion_n=%d exclusões search=Pesquisar search.search_repo=Pesquisar no repositório... search.fuzzy=Aproximada +search.fuzzy.tooltip=Incluir resultados que sejam próximos ao termo de busca search.match=Corresponde +search.match.tooltip=Incluir somente resultados que correspondam exatamente ao termo de busca search.results=Resultados da pesquisa para "%s" em %s search.code_no_results=Nenhum código-fonte correspondente ao seu termo de pesquisa foi encontrado. search.code_search_unavailable=A pesquisa por código não está disponível no momento. Entre em contato com o administrador do site. diff --git a/options/locale/locale_pt-PT.ini b/options/locale/locale_pt-PT.ini index d7497224ad0fa..31d1e7e2f8ef4 100644 --- a/options/locale/locale_pt-PT.ini +++ b/options/locale/locale_pt-PT.ini @@ -57,6 +57,7 @@ new_mirror=Nova réplica new_fork=Nova derivação do repositório new_org=Nova organização new_project=Novo planeamento +new_project_column=Nova coluna manage_org=Gerir organizações admin_panel=Administração do sítio account_settings=Configurações da conta @@ -90,6 +91,7 @@ disabled=Desabilitado copy=Copiar copy_url=Copiar URL +copy_content=Copiar conteúdo copy_branch=Copiar nome do ramo copy_success=Copiado! copy_error=Falha ao copiar @@ -109,6 +111,10 @@ never=Nunca rss_feed=Fonte RSS [aria] +navbar=Barra de navegação +footer=Rodapé +footer.software=Sobre o Software +footer.links=Ligações [filter] string.asc=A - Z @@ -364,6 +370,7 @@ password_pwned_err=Não foi possível completar o pedido ao HaveIBeenPwned [mail] view_it_on=Ver em %s +reply=ou responda a este email imediatamente link_not_working_do_paste=Não está a funcionar? Tente copiar e colar no seu navegador. hi_user_x=Olá %s, @@ -467,6 +474,8 @@ url_error=`'%s' não é um URL válido.` include_error=` tem que conter o texto '%s'.` glob_pattern_error=` o padrão glob é inválido: %s.` regex_pattern_error=` o padrão regex é inválido: %s.` +username_error=` só pode conter caracteres alfanuméricos ('0-9','a-z','A-Z'), hífen ('-'), sublinhado ('_') e ponto ('.') Não pode começar nem terminar com caracteres não alfanuméricos, e caracteres não alfanuméricos consecutivos também são proibidos.` +invalid_group_team_map_error=` o mapeamento é inválido: %s` unknown_error=Erro desconhecido: captcha_incorrect=O código CAPTCHA está errado. password_not_match=As senhas não coincidem. @@ -503,10 +512,12 @@ team_not_exist=A equipa não existe. last_org_owner=Não pode remover o último utilizador da equipa 'proprietários'. Tem que haver pelo menos um proprietário numa organização. cannot_add_org_to_team=Uma organização não pode ser adicionada como membro de uma equipa. duplicate_invite_to_team=O(A) utilizador(a) já tinha sido convidado(a) para ser membro da equipa. +organization_leave_success=Você deixou a organização %s com sucesso. invalid_ssh_key=Não é possível validar a sua chave SSH: %s invalid_gpg_key=Não é possível validar a sua chave GPG: %s invalid_ssh_principal=Protagonista inválido: %s +must_use_public_key=A chave que você forneceu é privada. Não carregue a sua chave em lugar nenhum, em vez disso use a sua chave pública. unable_verify_ssh_key=Não é possível validar a chave SSH auth_failed=Falhou a autenticação: %v @@ -743,6 +754,8 @@ access_token_deletion_cancel_action=Cancelar access_token_deletion_confirm_action=Eliminar access_token_deletion_desc=Eliminar um código revoga o acesso à sua conta nas aplicações que o usem. Esta operação não poderá ser revertida. Quer continuar? delete_token_success=O código foi eliminado. Aplicações que o usavam deixaram de ter acesso à sua conta. +select_scopes=Escolha os âmbitos +scopes_list=Âmbitos: manage_oauth2_applications=Gerir aplicações OAuth2 edit_oauth2_application=Editar aplicação OAuth2 @@ -1015,10 +1028,12 @@ unstar=Tirar dos favoritos star=Juntar aos favoritos fork=Derivar download_archive=Descarregar repositório +more_operations=Mais operações no_desc=Sem descrição quick_guide=Guia rápido clone_this_repo=Clonar este repositório +cite_this_repo=Citar este repositório create_new_repo_command=Criando um novo repositório na linha de comandos push_exist_repo=Enviando, pela linha de comandos, um repositório existente empty_message=Este repositório não contém qualquer conteúdo. @@ -1117,6 +1132,7 @@ editor.commit_directly_to_this_branch=Cometer imediatamente no ramo novo ramo para este cometimento e inicie um pedido de integração. editor.create_new_branch_np=Criar um novo ramo para este cometimento. editor.propose_file_change=Propor modificação do ficheiro +editor.new_branch_name=Dê um nome ao novo ramo para este cometimento editor.new_branch_name_desc=Nome do novo ramo… editor.cancel=Cancelar editor.filename_cannot_be_empty=O nome do ficheiro não pode estar em branco. @@ -1168,6 +1184,7 @@ commits.signed_by_untrusted_user_unmatched=Assinado por um utilizador não fiáv commits.gpg_key_id=ID da chave GPG commits.ssh_key_fingerprint=Identificação digital da chave SSH +commit.operations=Operações commit.revert=Reverter commit.revert-header=Reverter: %s commit.revert-content=Escolha o ramo para onde vai reverter: @@ -1200,11 +1217,22 @@ projects.type.bug_triage=Triagem de erros projects.template.desc=Modelo de planeamento projects.template.desc_helper=Escolha um modelo de planeamento para começar projects.type.uncategorized=Sem categoria +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.set_default=Definir como padrão +projects.column.set_default_desc=Defina esta coluna como padrão para questões e pedidos de integração não categorizados +projects.column.delete=Eliminar coluna +projects.column.deletion_desc=Eliminar uma coluna de um planeamento faz com que todas as questões que nela constam sejam movidas para a coluna 'Sem categoria'. Continuar? projects.column.color=Colorido projects.open=Abrir projects.close=Fechar +projects.column.assigned_to=Atribuída a +projects.card_type.desc=Previsão dos cartões +projects.card_type.images_and_text=Imagens e texto +projects.card_type.text_only=Apenas texto issues.desc=Organize relatórios de erros, tarefas e etapas. issues.filter_assignees=Filtrar encarregado @@ -1281,6 +1309,7 @@ issues.filter_label_no_select=Todos os rótulos issues.filter_milestone=Etapa issues.filter_milestone_no_select=Todas as etapas issues.filter_project=Planeamento +issues.filter_project_all=Todos os planeamentos issues.filter_project_none=Nenhum planeamento issues.filter_assignee=Encarregado issues.filter_assginee_no_select=Todos os encarregados @@ -1292,6 +1321,7 @@ issues.filter_type.assigned_to_you=Atribuídas a si issues.filter_type.created_by_you=Criadas por si issues.filter_type.mentioning_you=Mencionando a si issues.filter_type.review_requested=Revisão solicitada +issues.filter_type.reviewed_by_you=Revistos por si issues.filter_sort=Ordem issues.filter_sort.latest=Mais recentes issues.filter_sort.oldest=Mais antigas @@ -1313,6 +1343,8 @@ issues.action_milestone=Etapa issues.action_milestone_no_select=Sem etapa issues.action_assignee=Encarregado issues.action_assignee_no_select=Sem encarregado +issues.action_check=Marcar/desmarcar +issues.action_check_all=Marcar/desmarcar todos os itens issues.opened_by=aberta %[1]s por %[3]s pulls.merged_by=por %[3]s foi executado %[1]s pulls.merged_by_fake=por %[2]s foi executado %[1]s @@ -1366,6 +1398,9 @@ issues.save=Guardar issues.label_title=Nome do rótulo issues.label_description=Descrição do rótulo issues.label_color=Cor do rótulo +issues.label_exclusive=Exclusivo +issues.label_exclusive_desc=Nomeie o rótulo âmbito/item para torná-lo mutuamente exclusivo com outros rótulos do âmbito/. +issues.label_exclusive_warning=Quaisquer rótulos com âmbito que estejam em conflito irão ser removidos ao editar os rótulos de uma questão ou de um pedido de integração. issues.label_count=%d rótulos issues.label_open_issues=%d questões abertas issues.label_edit=Editar @@ -1619,6 +1654,8 @@ pulls.reopened_at=`reabriu este pedido de integração instruções para a linha de comandos.` pulls.merge_instruction_step1_desc=No seu repositório, crie um novo ramo e teste as modificações. pulls.merge_instruction_step2_desc=Integre as modificações e envie para o Gitea. +pulls.clear_merge_message=Apagar mensagem de integração +pulls.clear_merge_message_hint=Apagar a mensagem de integração apenas remove o conteúdo da mensagem de cometimento e mantém os rodapés do git, tais como "Co-Autorado-Por …". pulls.auto_merge_button_when_succeed=(quando as verificações forem bem-sucedidas) pulls.auto_merge_when_succeed=Integrar automaticamente quando todas as verificações forem bem-sucedidas @@ -1810,6 +1847,7 @@ settings.mirror_sync_in_progress=A sincronização da réplica está em andament settings.site=Sítio web settings.update_settings=Modificar configurações settings.branches.update_default_branch=Definir o ramo principal +settings.branches.add_new_rule=Adicionar nova regra settings.advanced_settings=Configurações avançadas settings.wiki_desc=Habilitar wiki do repositório settings.use_internal_wiki=Usar o wiki nativo @@ -1839,8 +1877,11 @@ settings.pulls.ignore_whitespace=Ignorar espaços em branco nos conflitos settings.pulls.enable_autodetect_manual_merge=Habilitar a identificação automática de integrações manuais (obs.: nalguns casos especiais a avaliação pode ser errada) settings.pulls.allow_rebase_update=Habilitar a modificação do ramo do pedido de integração através da mudança de base settings.pulls.default_delete_branch_after_merge=Eliminar o ramo do pedido de integração depois de finalizada a integração, como predefinição +settings.pulls.default_allow_edits_from_maintainers=Permitir, por padrão, que os responsáveis editem +settings.releases_desc=Habilitar lançamentos no repositório settings.packages_desc=Habilitar o registo de pacotes do repositório settings.projects_desc=Habilitar planeamentos no repositório +settings.actions_desc=Habilitar operações no repositório settings.admin_settings=Configurações do administrador settings.admin_enable_health_check=Habilitar verificações de integridade (git fsck) no repositório settings.admin_code_indexer=Indexador de código @@ -2970,6 +3011,7 @@ monitor.queue.pool.cancel_desc=Deixar uma fila sem quaisquer grupos de trabalhad notices.system_notice_list=Notificações do sistema notices.view_detail_header=Ver os detalhes da notificação +notices.operations=Operações notices.select_all=Marcar todas notices.deselect_all=Desmarcar todas notices.inverse_selection=Inverter as marcações @@ -3170,6 +3212,15 @@ settings.delete.notice=Está prestes a eliminar %s (%s). Esta operação é irre settings.delete.success=O pacote foi eliminado. settings.delete.error=Falhou a eliminação do pacote. owner.settings.cleanuprules.enabled=Habilitado +owner.settings.cleanuprules.keep.count=Manter a mais recente +owner.settings.cleanuprules.keep.count.1=1 versão por pacote +owner.settings.cleanuprules.keep.count.n=%d versões por pacote +owner.settings.cleanuprules.keep.pattern=Manter as versões correspondentes +owner.settings.cleanuprules.keep.pattern.container=A última versão será sempre mantida para pacotes de contentor. +owner.settings.cleanuprules.remove.title=Versões que correspondam a estas regras serão removidos, a não ser que a regra acima diga para os manter. +owner.settings.cleanuprules.remove.days=Remover versões mais antigas do que +owner.settings.cleanuprules.remove.pattern=Remover as versões correspondentes +owner.settings.cleanuprules.success.update=A regra de limpeza foi modificada. [secrets] value=Valor @@ -3187,8 +3238,16 @@ runners.labels=Rótulos runners.task_list.run=Executar runners.task_list.repository=Repositório runners.task_list.commit=Cometimento +runners.delete_runner=Eliminar o executor +runners.status.idle=Parada runners.status.active=Em funcionamento +runners.status.offline=Desconectada +runs.all_workflows=Todos os fluxos de trabalho +runs.open_tab=%d abertas +runs.closed_tab=%d fechadas runs.commit=Cometimento +runs.pushed_by=Enviada por +need_approval_desc=É necessária aprovação para executar fluxos de trabalho para a derivação do pedido de integração.