Merge branch 'master' into remove_user_as_assignee_after_collaboration_removal
This commit is contained in:
commit
5905ed9f9f
130
cmd/doctor.go
Normal file
130
cmd/doctor.go
Normal file
@ -0,0 +1,130 @@
|
||||
// Copyright 2019 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
// CmdDoctor represents the available doctor sub-command.
|
||||
var CmdDoctor = cli.Command{
|
||||
Name: "doctor",
|
||||
Usage: "Diagnose the problems",
|
||||
Description: "A command to diagnose the problems of current gitea instance according the given configuration.",
|
||||
Action: runDoctor,
|
||||
}
|
||||
|
||||
type check struct {
|
||||
title string
|
||||
f func(ctx *cli.Context) ([]string, error)
|
||||
}
|
||||
|
||||
// checklist represents list for all checks
|
||||
var checklist = []check{
|
||||
{
|
||||
title: "Check if OpenSSH authorized_keys file id correct",
|
||||
f: runDoctorLocationMoved,
|
||||
},
|
||||
// more checks please append here
|
||||
}
|
||||
|
||||
func runDoctor(ctx *cli.Context) error {
|
||||
err := initDB()
|
||||
fmt.Println("Using app.ini at", setting.CustomConf)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
fmt.Println("Check if you are using the right config file. You can use a --config directive to specify one.")
|
||||
return nil
|
||||
}
|
||||
|
||||
for i, check := range checklist {
|
||||
fmt.Println("[", i+1, "]", check.title)
|
||||
if messages, err := check.f(ctx); err != nil {
|
||||
fmt.Println("Error:", err)
|
||||
} else if len(messages) > 0 {
|
||||
for _, message := range messages {
|
||||
fmt.Println("-", message)
|
||||
}
|
||||
} else {
|
||||
fmt.Println("OK.")
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func exePath() (string, error) {
|
||||
file, err := exec.LookPath(os.Args[0])
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Abs(file)
|
||||
}
|
||||
|
||||
func runDoctorLocationMoved(ctx *cli.Context) ([]string, error) {
|
||||
if setting.SSH.StartBuiltinServer || !setting.SSH.CreateAuthorizedKeysFile {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
fPath := filepath.Join(setting.SSH.RootPath, "authorized_keys")
|
||||
f, err := os.Open(fPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var firstline string
|
||||
scanner := bufio.NewScanner(f)
|
||||
for scanner.Scan() {
|
||||
firstline = strings.TrimSpace(scanner.Text())
|
||||
if len(firstline) == 0 || firstline[0] == '#' {
|
||||
continue
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
// command="/Volumes/data/Projects/gitea/gitea/gitea --config
|
||||
if len(firstline) > 0 {
|
||||
exp := regexp.MustCompile(`^[ \t]*(?:command=")([^ ]+) --config='([^']+)' serv key-([^"]+)",(?:[^ ]+) ssh-rsa ([^ ]+) ([^ ]+)[ \t]*$`)
|
||||
|
||||
// command="/home/user/gitea --config='/home/user/etc/app.ini' serv key-999",option-1,option-2,option-n ssh-rsa public-key-value key-name
|
||||
res := exp.FindStringSubmatch(firstline)
|
||||
if res == nil {
|
||||
return nil, errors.New("Unknow authorized_keys format")
|
||||
}
|
||||
|
||||
giteaPath := res[1] // => /home/user/gitea
|
||||
iniPath := res[2] // => /home/user/etc/app.ini
|
||||
|
||||
p, err := exePath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p, err = filepath.Abs(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(giteaPath) > 0 && giteaPath != p {
|
||||
return []string{fmt.Sprintf("Gitea exe path wants %s but %s on %s", p, giteaPath, fPath)}, nil
|
||||
}
|
||||
if len(iniPath) > 0 && iniPath != setting.CustomConf {
|
||||
return []string{fmt.Sprintf("Gitea config path wants %s but %s on %s", setting.CustomConf, iniPath, fPath)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
@ -289,3 +289,28 @@ This command is idempotent.
|
||||
|
||||
#### convert
|
||||
Converts an existing MySQL database from utf8 to utf8mb4.
|
||||
|
||||
#### doctor
|
||||
Diagnose the problems of current gitea instance according the given configuration.
|
||||
Currently there are a check list below:
|
||||
|
||||
- Check if OpenSSH authorized_keys file id correct
|
||||
When your gitea instance support OpenSSH, your gitea instance binary path will be written to `authorized_keys`
|
||||
when there is any public key added or changed on your gitea instance.
|
||||
Sometimes if you moved or renamed your gitea binary when upgrade and you haven't run `Update the '.ssh/authorized_keys' file with Gitea SSH keys. (Not needed for the built-in SSH server.)` on your Admin Panel. Then all pull/push via SSH will not be work.
|
||||
This check will help you to check if it works well.
|
||||
|
||||
For contributors, if you want to add more checks, you can wrie ad new function like `func(ctx *cli.Context) ([]string, error)` and
|
||||
append it to `doctor.go`.
|
||||
|
||||
```go
|
||||
var checklist = []check{
|
||||
{
|
||||
title: "Check if OpenSSH authorized_keys file id correct",
|
||||
f: runDoctorLocationMoved,
|
||||
},
|
||||
// more checks please append here
|
||||
}
|
||||
```
|
||||
|
||||
This function will receive a command line context and return a list of details about the problems or error.
|
1
main.go
1
main.go
@ -68,6 +68,7 @@ arguments - which can alternatively be run by running the subcommand web.`
|
||||
cmd.CmdMigrate,
|
||||
cmd.CmdKeys,
|
||||
cmd.CmdConvert,
|
||||
cmd.CmdDoctor,
|
||||
}
|
||||
// Now adjust these commands to add our global configuration options
|
||||
|
||||
|
@ -93,8 +93,8 @@ func (protectBranch *ProtectedBranch) CanUserPush(userID int64) bool {
|
||||
return in
|
||||
}
|
||||
|
||||
// CanUserMerge returns if some user could merge a pull request to this protected branch
|
||||
func (protectBranch *ProtectedBranch) CanUserMerge(userID int64) bool {
|
||||
// IsUserMergeWhitelisted checks if some user is whitelisted to merge to this branch
|
||||
func (protectBranch *ProtectedBranch) IsUserMergeWhitelisted(userID int64) bool {
|
||||
if !protectBranch.EnableMergeWhitelist {
|
||||
return true
|
||||
}
|
||||
@ -348,27 +348,6 @@ func (repo *Repository) IsProtectedBranchForPush(branchName string, doer *User)
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// IsProtectedBranchForMerging checks if branch is protected for merging
|
||||
func (repo *Repository) IsProtectedBranchForMerging(pr *PullRequest, branchName string, doer *User) (bool, error) {
|
||||
if doer == nil {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
protectedBranch := &ProtectedBranch{
|
||||
RepoID: repo.ID,
|
||||
BranchName: branchName,
|
||||
}
|
||||
|
||||
has, err := x.Get(protectedBranch)
|
||||
if err != nil {
|
||||
return true, err
|
||||
} else if has {
|
||||
return !protectedBranch.CanUserMerge(doer.ID) || !protectedBranch.HasEnoughApprovals(pr) || protectedBranch.MergeBlockedByRejectedReview(pr), nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// updateApprovalWhitelist checks whether the user whitelist changed and returns a whitelist with
|
||||
// the users from newWhitelist which have explicit read or write access to the repo.
|
||||
func updateApprovalWhitelist(repo *Repository, currentWhitelist, newWhitelist []int64) (whitelist []int64, err error) {
|
||||
|
@ -403,11 +403,12 @@ func (issue *Issue) apiFormat(e Engine) *api.Issue {
|
||||
apiIssue.Closed = issue.ClosedUnix.AsTimePtr()
|
||||
}
|
||||
|
||||
issue.loadMilestone(e)
|
||||
if issue.Milestone != nil {
|
||||
apiIssue.Milestone = issue.Milestone.APIFormat()
|
||||
}
|
||||
issue.loadAssignees(e)
|
||||
|
||||
issue.loadAssignees(e)
|
||||
if len(issue.Assignees) > 0 {
|
||||
for _, assignee := range issue.Assignees {
|
||||
apiIssue.Assignees = append(apiIssue.Assignees, assignee.APIFormat())
|
||||
|
@ -8,7 +8,6 @@ import (
|
||||
"time"
|
||||
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
|
||||
"xorm.io/builder"
|
||||
"xorm.io/xorm"
|
||||
@ -60,25 +59,6 @@ func (t *TrackedTime) loadAttributes(e Engine) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// APIFormat converts TrackedTime to API format
|
||||
func (t *TrackedTime) APIFormat() (apiT *api.TrackedTime) {
|
||||
apiT = &api.TrackedTime{
|
||||
ID: t.ID,
|
||||
IssueID: t.IssueID,
|
||||
UserID: t.UserID,
|
||||
UserName: t.User.Name,
|
||||
Time: t.Time,
|
||||
Created: t.Created,
|
||||
}
|
||||
if t.Issue != nil {
|
||||
apiT.Issue = t.Issue.APIFormat()
|
||||
}
|
||||
if t.User != nil {
|
||||
apiT.UserName = t.User.Name
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// LoadAttributes load Issue, User
|
||||
func (tl TrackedTimeList) LoadAttributes() (err error) {
|
||||
for _, t := range tl {
|
||||
@ -89,15 +69,6 @@ func (tl TrackedTimeList) LoadAttributes() (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// APIFormat converts TrackedTimeList to API format
|
||||
func (tl TrackedTimeList) APIFormat() api.TrackedTimeList {
|
||||
result := make([]*api.TrackedTime, 0, len(tl))
|
||||
for _, t := range tl {
|
||||
result = append(result, t.APIFormat())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// FindTrackedTimesOptions represent the filters for tracked times. If an ID is 0 it will be ignored.
|
||||
type FindTrackedTimesOptions struct {
|
||||
IssueID int64
|
||||
|
@ -479,31 +479,6 @@ const (
|
||||
MergeStyleSquash MergeStyle = "squash"
|
||||
)
|
||||
|
||||
// CheckUserAllowedToMerge checks whether the user is allowed to merge
|
||||
func (pr *PullRequest) CheckUserAllowedToMerge(doer *User) (err error) {
|
||||
if doer == nil {
|
||||
return ErrNotAllowedToMerge{
|
||||
"Not signed in",
|
||||
}
|
||||
}
|
||||
|
||||
if pr.BaseRepo == nil {
|
||||
if err = pr.GetBaseRepo(); err != nil {
|
||||
return fmt.Errorf("GetBaseRepo: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if protected, err := pr.BaseRepo.IsProtectedBranchForMerging(pr, pr.BaseBranch, doer); err != nil {
|
||||
return fmt.Errorf("IsProtectedBranch: %v", err)
|
||||
} else if protected {
|
||||
return ErrNotAllowedToMerge{
|
||||
"The branch is protected",
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetMerged sets a pull request to merged and closes the corresponding issue
|
||||
func (pr *PullRequest) SetMerged() (err error) {
|
||||
if pr.HasMerged {
|
||||
|
@ -271,7 +271,7 @@ func getUserRepoPermission(e Engine, repo *Repository, user *User) (perm Permiss
|
||||
return
|
||||
}
|
||||
|
||||
// IsUserRepoAdmin return ture if user has admin right of a repo
|
||||
// IsUserRepoAdmin return true if user has admin right of a repo
|
||||
func IsUserRepoAdmin(repo *Repository, user *User) (bool, error) {
|
||||
return isUserRepoAdmin(x, repo, user)
|
||||
}
|
||||
|
@ -503,7 +503,7 @@ func (u *User) ValidatePassword(passwd string) bool {
|
||||
|
||||
// IsPasswordSet checks if the password is set or left empty
|
||||
func (u *User) IsPasswordSet() bool {
|
||||
return len(u.Passwd) > 0
|
||||
return !u.ValidatePassword("")
|
||||
}
|
||||
|
||||
// UploadAvatar saves custom avatar for user.
|
||||
|
@ -448,6 +448,7 @@ type MergePullRequestForm struct {
|
||||
Do string `binding:"Required;In(merge,rebase,rebase-merge,squash)"`
|
||||
MergeTitleField string
|
||||
MergeMessageField string
|
||||
ForceMerge *bool `json:"force_merge,omitempty"`
|
||||
}
|
||||
|
||||
// Validate validates the fields
|
||||
|
@ -51,7 +51,7 @@ func ToBranch(repo *models.Repository, b *git.Branch, c *git.Commit, bp *models.
|
||||
EnableStatusCheck: bp.EnableStatusCheck,
|
||||
StatusCheckContexts: bp.StatusCheckContexts,
|
||||
UserCanPush: bp.CanUserPush(user.ID),
|
||||
UserCanMerge: bp.CanUserMerge(user.ID),
|
||||
UserCanMerge: bp.IsUserMergeWhitelisted(user.ID),
|
||||
}
|
||||
}
|
||||
|
||||
|
38
modules/convert/issue.go
Normal file
38
modules/convert/issue.go
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2020 The Gitea Authors. All rights reserved.
|
||||
// Use of this source code is governed by a MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package convert
|
||||
|
||||
import (
|
||||
"code.gitea.io/gitea/models"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// ToTrackedTime converts TrackedTime to API format
|
||||
func ToTrackedTime(t *models.TrackedTime) (apiT *api.TrackedTime) {
|
||||
apiT = &api.TrackedTime{
|
||||
ID: t.ID,
|
||||
IssueID: t.IssueID,
|
||||
UserID: t.UserID,
|
||||
UserName: t.User.Name,
|
||||
Time: t.Time,
|
||||
Created: t.Created,
|
||||
}
|
||||
if t.Issue != nil {
|
||||
apiT.Issue = t.Issue.APIFormat()
|
||||
}
|
||||
if t.User != nil {
|
||||
apiT.UserName = t.User.Name
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ToTrackedTimeList converts TrackedTimeList to API format
|
||||
func ToTrackedTimeList(tl models.TrackedTimeList) api.TrackedTimeList {
|
||||
result := make([]*api.TrackedTime, 0, len(tl))
|
||||
for _, t := range tl {
|
||||
result = append(result, ToTrackedTime(t))
|
||||
}
|
||||
return result
|
||||
}
|
@ -138,9 +138,11 @@ func UpdateIssuesCommit(doer *models.User, repo *models.Repository, commits []*r
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if err := changeIssueStatus(refRepo, refIssue, doer, ref.Action == references.XRefActionCloses); err != nil {
|
||||
return err
|
||||
close := (ref.Action == references.XRefActionCloses)
|
||||
if close != refIssue.IsClosed {
|
||||
if err := changeIssueStatus(refRepo, refIssue, doer, close); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1036,7 +1036,6 @@ pulls.cannot_auto_merge_helper=Pro vyřešení konfliktů proveďte ruční slou
|
||||
pulls.no_merge_desc=Tento požadavek na natažení nemůže být sloučen, protože všechny možnosti repozitáře na sloučení jsou zakázány.
|
||||
pulls.no_merge_helper=Povolte možnosti sloučení v nastavení repozitáře nebo proveďte sloučení požadavku na natažení ručně.
|
||||
pulls.no_merge_wip=Požadavek na natažení nemůže být sloučen protože je označen jako nedokončený.
|
||||
pulls.no_merge_status_check=Tento požadavek na natažení nemůže být sloučen, protože ne všechny požadované kontroly stavu byly úspěšné.
|
||||
pulls.merge_pull_request=Sloučit požadavek na natažení
|
||||
pulls.rebase_merge_pull_request=Rebase a sloučit
|
||||
pulls.rebase_merge_commit_pull_request=Rebase a sloučit (--no-ff)
|
||||
|
@ -1061,7 +1061,6 @@ pulls.cannot_auto_merge_helper=Bitte manuell zusammenführen, um die Konflikte z
|
||||
pulls.no_merge_desc=Dieser Pull-Request kann nicht gemerged werden, da keine Mergeoptionen aktiviert 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 zusammengeführt werden, da er als Work In Progress gekennzeichnet ist.
|
||||
pulls.no_merge_status_check=Dieser Pull-Request kann nicht zusammengeführt werden, da nicht alle erforderlichen Statusprüfungen erfolgreich waren.
|
||||
pulls.merge_pull_request=Pull-Request zusammenführen
|
||||
pulls.rebase_merge_pull_request=Rebase und Mergen
|
||||
pulls.rebase_merge_commit_pull_request=Rebasen und Mergen (--no-ff)
|
||||
|
@ -1062,7 +1062,8 @@ pulls.cannot_auto_merge_helper = Merge manually to resolve the conflicts.
|
||||
pulls.no_merge_desc = This pull request cannot be merged because all repository merge options are disabled.
|
||||
pulls.no_merge_helper = Enable merge options in the repository settings or merge the pull request manually.
|
||||
pulls.no_merge_wip = This pull request can not be merged because it is marked as being a work in progress.
|
||||
pulls.no_merge_status_check = This pull request cannot be merged because not all required status checkes are successful.
|
||||
pulls.no_merge_not_ready = This pull request is not ready to be merged, check review status and status checks.
|
||||
pulls.no_merge_access = You are not authorized to merge this pull request.
|
||||
pulls.merge_pull_request = Merge Pull Request
|
||||
pulls.rebase_merge_pull_request = Rebase and Merge
|
||||
pulls.rebase_merge_commit_pull_request = Rebase and Merge (--no-ff)
|
||||
|
@ -1057,7 +1057,6 @@ pulls.cannot_auto_merge_helper=Combinar manualmente para resolver los conflictos
|
||||
pulls.no_merge_desc=Este pull request no se puede combinar porque todas las opciones de combinación del repositorio están deshabilitadas.
|
||||
pulls.no_merge_helper=Habilite las opciones de combinación en la configuración del repositorio o fusione el pull request manualmente.
|
||||
pulls.no_merge_wip=Este pull request no se puede combinar porque está marcada como un trabajo en progreso.
|
||||
pulls.no_merge_status_check=No se puede fusionar este pull request porque no todas las comprobaciones de estado requeridas resultaron exitosas.
|
||||
pulls.merge_pull_request=Fusionar Pull Request
|
||||
pulls.rebase_merge_pull_request=Hacer Rebase y Fusionar
|
||||
pulls.rebase_merge_commit_pull_request=Hacer Rebase y Fusionar (--no-ff)
|
||||
|
@ -1062,7 +1062,6 @@ pulls.cannot_auto_merge_helper=به صورتی دستی ادغام کنید تا
|
||||
pulls.no_merge_desc=این تقاضای واکشی قابل ادغام نیست لذا تمامی گزینه های ادغام مخزن غیر فعال هستند.
|
||||
pulls.no_merge_helper=گزینه های ادغام را در تنظیمات مخزن فعال کنید یا از تقاضای واکشی به صورت دستی ادغام نمایید.
|
||||
pulls.no_merge_wip=این تقاضای واکشی قابل ادغام نیست لذا اکنون به این مخزن درحال پردازش علامت گذاری شده است.
|
||||
pulls.no_merge_status_check=این تقاضای واکشی نمیتواند ادغام شود لذا تمامی حالت های ضروری با موفقیت بررسی نشدند.
|
||||
pulls.merge_pull_request=ادغام تقاضای واکشی
|
||||
pulls.rebase_merge_pull_request=بازگردانی و ادغام
|
||||
pulls.rebase_merge_commit_pull_request=بازگردانی و ادغام (--no-ff)
|
||||
|
@ -1054,7 +1054,6 @@ pulls.cannot_auto_merge_helper=Fusionner manuellement pour résoudre les conflit
|
||||
pulls.no_merge_desc=Cette demande de fusion ne peut être appliquée directement car toutes les options de fusion du dépôt sont désactivées.
|
||||
pulls.no_merge_helper=Activez des options de fusion dans les paramètres du dépôt ou fusionnez la demande manuellement.
|
||||
pulls.no_merge_wip=Cette demande d'ajout ne peut pas être fusionnée car elle est marquée comme en cours de chantier.
|
||||
pulls.no_merge_status_check=Cette demande de pull ne peut pas être fusionnée car tous les contrôles de statut requis ne sont pas réussis.
|
||||
pulls.merge_pull_request=Fusionner la demande d'ajout
|
||||
pulls.rebase_merge_pull_request=Rebase et fusionner
|
||||
pulls.rebase_merge_commit_pull_request=Rebase et Fusion (--no-ff)
|
||||
|
@ -1061,7 +1061,8 @@ pulls.cannot_auto_merge_helper=コンフリクトを解消するため手動で
|
||||
pulls.no_merge_desc=リポジトリのマージオプションがすべて無効になっているため、このプルリクエストをマージすることはできせん。
|
||||
pulls.no_merge_helper=リポジトリ設定でマージを有効にするか、手動でマージしてください。
|
||||
pulls.no_merge_wip=このプルリクエストはWork in Progressとマークされているため、マージすることはできません。
|
||||
pulls.no_merge_status_check=すべての必要なステータスチェックが成功していないため、このプルリクエストはマージできません。
|
||||
pulls.no_merge_not_ready=このプルリクエストはマージする準備ができていません。 レビュー状況とステータスチェックを確認してください。
|
||||
pulls.no_merge_access=このプルリクエストをマージする権限がありません。
|
||||
pulls.merge_pull_request=プルリクエストをマージ
|
||||
pulls.rebase_merge_pull_request=リベースしてマージ
|
||||
pulls.rebase_merge_commit_pull_request=リベースしてマージ(--no-ff)
|
||||
@ -1412,6 +1413,8 @@ settings.protect_approvals_whitelist_enabled=ホワイトリストに登録し
|
||||
settings.protect_approvals_whitelist_enabled_desc=ホワイトリストに登録したユーザーやチームによるレビューのみを、必要な承認とみなします。 承認のホワイトリストが無い場合は、書き込み権限がある人によるレビューを必要な承認とみなします。
|
||||
settings.protect_approvals_whitelist_users=ホワイトリストに含めるレビューア:
|
||||
settings.protect_approvals_whitelist_teams=ホワイトリストに含めるレビューチーム:
|
||||
settings.dismiss_stale_approvals=古くなった承認を取り消す
|
||||
settings.dismiss_stale_approvals_desc=プルリクエストの内容を変える新たなコミットがブランチにプッシュされた場合、以前の承認を取り消します。
|
||||
settings.add_protected_branch=保護を有効にする
|
||||
settings.delete_protected_branch=保護を無効にする
|
||||
settings.update_protect_branch_success=ブランチ '%s' の保護を更新しました。
|
||||
@ -2025,8 +2028,54 @@ monitor.execute_time=実行時間
|
||||
monitor.process.cancel=処理をキャンセル
|
||||
monitor.process.cancel_desc=処理をキャンセルするとデータが失われる可能性があります
|
||||
monitor.process.cancel_notices=キャンセル: <strong>%s</strong>?
|
||||
monitor.queues=キュー
|
||||
monitor.queue=キュー: %s
|
||||
monitor.queue.name=キュー名
|
||||
monitor.queue.type=種類
|
||||
monitor.queue.exemplar=要素の型
|
||||
monitor.queue.numberworkers=ワーカー数
|
||||
monitor.queue.maxnumberworkers=ワーカー数上限
|
||||
monitor.queue.review=設定確認
|
||||
monitor.queue.review_add=確認/ワーカー追加
|
||||
monitor.queue.configuration=初期設定
|
||||
monitor.queue.nopool.title=ワーカープールはありません
|
||||
monitor.queue.nopool.desc=このキューは他のキューをラップし、これ自体にはワーカープールがありません。
|
||||
monitor.queue.wrapped.desc=wrappedキューは、すぐに開始されないキューをラップし、入ってきたリクエストをチャンネルにバッファリングします。 これ自体にはワーカープールがありません。
|
||||
monitor.queue.persistable-channel.desc=persistable-channelキューは二つのキューをラップします。 一つはchannelキューで、自分のワーカープールを持ちます。もう一つはlevelキューで、前回のシャットダウンからリクエストを引き継ぐためのものです。 これ自体にはワーカープールがありません。
|
||||
monitor.queue.pool.timeout=タイムアウト
|
||||
monitor.queue.pool.addworkers.title=ワーカーの追加
|
||||
monitor.queue.pool.addworkers.submit=ワーカーを追加
|
||||
monitor.queue.pool.addworkers.desc=このプールに、タイムアウト付きまたはタイムアウト無しでワーカーを追加します。 タイムアウトを指定した場合は、タイムアウト後にそれらのワーカーがこのプールから取り除かれます。
|
||||
monitor.queue.pool.addworkers.numberworkers.placeholder=ワーカー数
|
||||
monitor.queue.pool.addworkers.timeout.placeholder=0でタイムアウト無し
|
||||
monitor.queue.pool.addworkers.mustnumbergreaterzero=追加するワーカー数は1以上にしてください
|
||||
monitor.queue.pool.addworkers.musttimeoutduration=タイムアウトは 、Go言語の時間差表記(例 5m)、または0にしてください
|
||||
|
||||
monitor.queue.settings.title=プール設定
|
||||
monitor.queue.settings.desc=ワーカーへのキューのブロックが発生すると、それに応じてプール数がブースト分ずつ動的に増えます。 これらの変更は現在のワーカーグループには影響しません。
|
||||
monitor.queue.settings.timeout=ブースト分のタイムアウト
|
||||
monitor.queue.settings.timeout.placeholder=現在の設定 %[1]v
|
||||
monitor.queue.settings.timeout.error=タイムアウトは 、Go言語の時間差表記(例 5m)、または0にしてください
|
||||
monitor.queue.settings.numberworkers=ブースト分のワーカー数
|
||||
monitor.queue.settings.numberworkers.placeholder=現在の設定 %[1]d
|
||||
monitor.queue.settings.numberworkers.error=追加するワーカー数はゼロ以上にしてください
|
||||
monitor.queue.settings.maxnumberworkers=ワーカー数上限
|
||||
monitor.queue.settings.maxnumberworkers.placeholder=現在の設定 %[1]d
|
||||
monitor.queue.settings.maxnumberworkers.error=ワーカー数上限は数値にしてください
|
||||
monitor.queue.settings.submit=設定を更新
|
||||
monitor.queue.settings.changed=設定を更新しました
|
||||
monitor.queue.settings.blocktimeout=現在のブロックタイムアウト
|
||||
monitor.queue.settings.blocktimeout.value=%[1]v
|
||||
|
||||
monitor.queue.pool.none=このキューにはプールがありません
|
||||
monitor.queue.pool.added=ワーカーグループを追加しました
|
||||
monitor.queue.pool.max_changed=ワーカー数の上限を変更しました
|
||||
monitor.queue.pool.workers.title=アクティブなワーカーグループ
|
||||
monitor.queue.pool.workers.none=ワーカーグループはありません。
|
||||
monitor.queue.pool.cancel=ワーカーグループのシャットダウン
|
||||
monitor.queue.pool.cancelling=ワーカーグループをシャットダウンしています
|
||||
monitor.queue.pool.cancel_notices=このワーカー数 %s のグループをシャットダウンしますか?
|
||||
monitor.queue.pool.cancel_desc=キューをワーカーグループ無しのままにすると、リクエストがブロックし続ける原因となります。
|
||||
|
||||
notices.system_notice_list=システム通知
|
||||
notices.view_detail_header=通知の詳細を表示
|
||||
|
@ -1061,7 +1061,6 @@ pulls.cannot_auto_merge_helper=Sapludiniet manuāli, lai atrisinātu konfliktus.
|
||||
pulls.no_merge_desc=Šo izmaiņu pieprasījumu nav iespējams sapludināt, jo nav atļauts neviens sapludināšanas veids.
|
||||
pulls.no_merge_helper=Lai sapludinātu šo izmaiņu pieprasījumu, iespējojiet vismaz vienu sapludināšanas veidu repozitorija iestatījumos vai sapludiniet to manuāli.
|
||||
pulls.no_merge_wip=Šo izmaiņu pieprasījumu nav iespējams sapludināt, jo tas ir atzīmēts, ka darbs pie tā vēl nav pabeigts.
|
||||
pulls.no_merge_status_check=Šo izmaiņu pieprasījumu nevar saplusināt, jo nav veiksmīgi izildītas visas obligātas statusa pārbaudes.
|
||||
pulls.merge_pull_request=Izmaiņu pieprasījuma sapludināšana
|
||||
pulls.rebase_merge_pull_request=Pārbāzēt un sapludināt
|
||||
pulls.rebase_merge_commit_pull_request=Pārbāzēt un sapludināt (--no-ff)
|
||||
|
@ -1055,7 +1055,6 @@ pulls.cannot_auto_merge_helper=Scal ręcznie, aby rozwiązać konflikty.
|
||||
pulls.no_merge_desc=Ten Pull Request nie może zostać scalony, ponieważ wszystkie opcje scalania dla tego repozytorium są wyłączone.
|
||||
pulls.no_merge_helper=Włącz opcje scalania w ustawieniach repozytorium, lub scal ten Pull Request ręcznie.
|
||||
pulls.no_merge_wip=Ten pull request nie może być automatycznie scalony, ponieważ jest oznaczony jako praca w toku.
|
||||
pulls.no_merge_status_check=Ten Pull Request nie może być scalony, bo nie wszystkie kontrole stanów były pomyślne.
|
||||
pulls.merge_pull_request=Scal Pull Request
|
||||
pulls.rebase_merge_pull_request=Zmień bazę i scal
|
||||
pulls.rebase_merge_commit_pull_request=Zmień bazę i scal (--no-ff)
|
||||
|
@ -1061,7 +1061,6 @@ pulls.cannot_auto_merge_helper=Faça o merge manualmente para resolver os confli
|
||||
pulls.no_merge_desc=O merge deste pull request não pode ser aplicado porque todas as opções de mesclagem do repositório estão desabilitadas.
|
||||
pulls.no_merge_helper=Habilite as opções de merge nas configurações do repositório ou faça o merge do pull request manualmente.
|
||||
pulls.no_merge_wip=O merge deste pull request não pode ser aplicado porque está marcado como um trabalho em andamento.
|
||||
pulls.no_merge_status_check=Este pull request não pode ter seu merge aplicado porque nem todas as verificações de status necessárias foram bem sucedidas.
|
||||
pulls.merge_pull_request=Aplicar merge do pull request
|
||||
pulls.rebase_merge_pull_request=Aplicar Rebase e Merge
|
||||
pulls.rebase_merge_commit_pull_request=Aplicar Rebase e Merge (--no-ff)
|
||||
|
@ -1054,7 +1054,6 @@ pulls.cannot_auto_merge_helper=Çakışmaları çözmek için el ile birleştiri
|
||||
pulls.no_merge_desc=Tüm depo birleştirme seçenekleri devre dışı bırakıldığından, bu değişiklik isteği birleştirilemez.
|
||||
pulls.no_merge_helper=Depo ayarlarındaki birleştirme seçeneklerini etkinleştirin veya değişiklik isteğini el ile birleştirin.
|
||||
pulls.no_merge_wip=Bu değişiklik isteği birleştirilemez çünkü devam eden bir çalışma olarak işaretlendi.
|
||||
pulls.no_merge_status_check=Gerekli olan tüm durum denetimleri başarılı olmadığından bu değişiklik isteği birleştirilemez.
|
||||
pulls.merge_pull_request=Değişiklik İsteğini Birleştir
|
||||
pulls.rebase_merge_pull_request=Rebase ve Merge
|
||||
pulls.rebase_merge_commit_pull_request=Rebase ve Merge (--no-ff)
|
||||
|
@ -1058,7 +1058,6 @@ pulls.cannot_auto_merge_helper=Злийте вручну для вирішенн
|
||||
pulls.no_merge_desc=Цей запити на злиття неможливо злити, оскільки всі параметри об'єднання репозиторія вимкнено.
|
||||
pulls.no_merge_helper=Увімкніть параметри злиття в налаштуваннях репозиторія або злийте запити на злиття вручну.
|
||||
pulls.no_merge_wip=Цей пулл-реквест не можливо об'єднати, тому-що він вже виконується.
|
||||
pulls.no_merge_status_check=Не вдалося об'єднати цей запит на злиття, оскільки не всі обов'язкові перевірки були успішними.
|
||||
pulls.merge_pull_request=Об'єднати запит на злиття
|
||||
pulls.rebase_merge_pull_request=Зробити Rebase і злити
|
||||
pulls.rebase_merge_commit_pull_request=Rebase та злитя (--no-ff)
|
||||
|
@ -1061,7 +1061,6 @@ pulls.cannot_auto_merge_helper=手动合并解决此冲突
|
||||
pulls.no_merge_desc=由于未启用合并选项,此合并请求无法被合并。
|
||||
pulls.no_merge_helper=在仓库设置中启用合并选项或者手工合并请求。
|
||||
pulls.no_merge_wip=这个合并请求无法合并,因为被标记为尚未完成的工作。
|
||||
pulls.no_merge_status_check=此合并请求不能合并,因为不是所有的状态检查都是成功的。
|
||||
pulls.merge_pull_request=合并请求
|
||||
pulls.rebase_merge_pull_request=变基并合并
|
||||
pulls.rebase_merge_commit_pull_request=变基合并 (--no-ff)
|
||||
|
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/convert"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
)
|
||||
@ -93,7 +94,7 @@ func ListTrackedTimes(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, trackedTimes.APIFormat())
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes))
|
||||
}
|
||||
|
||||
// AddTime add time manual to the given issue
|
||||
@ -178,7 +179,7 @@ func AddTime(ctx *context.APIContext, form api.AddTimeOption) {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, trackedTime.APIFormat())
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTime(trackedTime))
|
||||
}
|
||||
|
||||
// ResetIssueTime reset time manual to the given issue
|
||||
@ -399,7 +400,7 @@ func ListTrackedTimesByUser(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, trackedTimes.APIFormat())
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes))
|
||||
}
|
||||
|
||||
// ListTrackedTimesByRepository lists all tracked times of the repository
|
||||
@ -486,7 +487,7 @@ func ListTrackedTimesByRepository(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "LoadAttributes", err)
|
||||
return
|
||||
}
|
||||
ctx.JSON(http.StatusOK, trackedTimes.APIFormat())
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes))
|
||||
}
|
||||
|
||||
// ListMyTrackedTimes lists all tracked times of the current user
|
||||
@ -530,5 +531,5 @@ func ListMyTrackedTimes(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, trackedTimes.APIFormat())
|
||||
ctx.JSON(http.StatusOK, convert.ToTrackedTimeList(trackedTimes))
|
||||
}
|
||||
|
@ -600,20 +600,43 @@ func MergePullRequest(ctx *context.APIContext, form auth.MergePullRequestForm) {
|
||||
return
|
||||
}
|
||||
|
||||
perm, err := models.GetUserRepoPermission(ctx.Repo.Repository, ctx.User)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
|
||||
return
|
||||
}
|
||||
|
||||
allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, perm, ctx.User)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "IsUSerAllowedToMerge", err)
|
||||
return
|
||||
}
|
||||
if !allowedMerge {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "Merge", "User not allowed to merge PR")
|
||||
return
|
||||
}
|
||||
|
||||
if !pr.CanAutoMerge() || pr.HasMerged || pr.IsWorkInProgress() {
|
||||
ctx.Status(http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
isPass, err := pull_service.IsPullCommitStatusPass(pr)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "IsPullCommitStatusPass", err)
|
||||
return
|
||||
}
|
||||
|
||||
if !isPass && !ctx.IsUserRepoAdmin() {
|
||||
ctx.Status(http.StatusMethodNotAllowed)
|
||||
return
|
||||
if err := pull_service.CheckPRReadyToMerge(pr); err != nil {
|
||||
if !models.IsErrNotAllowedToMerge(err) {
|
||||
ctx.Error(http.StatusInternalServerError, "CheckPRReadyToMerge", err)
|
||||
return
|
||||
}
|
||||
if form.ForceMerge != nil && *form.ForceMerge {
|
||||
if isRepoAdmin, err := models.IsUserRepoAdmin(pr.BaseRepo, ctx.User); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "IsUserRepoAdmin", err)
|
||||
return
|
||||
} else if !isRepoAdmin {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "Merge", "Only repository admin can merge if not all checks are ok (force merge)")
|
||||
}
|
||||
} else {
|
||||
ctx.Error(http.StatusMethodNotAllowed, "PR is not ready to be merged", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(form.Do) == 0 {
|
||||
|
@ -319,7 +319,6 @@ func CreateOrgRepo(ctx *context.APIContext, opt api.CreateRepoOption) {
|
||||
// swagger:operation POST /orgs/{org}/repos organization createOrgRepo
|
||||
// ---
|
||||
// summary: Create a repository in an organization
|
||||
// deprecated: true
|
||||
// consumes:
|
||||
// - application/json
|
||||
// produces:
|
||||
|
@ -17,6 +17,7 @@ import (
|
||||
"code.gitea.io/gitea/modules/private"
|
||||
"code.gitea.io/gitea/modules/repofiles"
|
||||
"code.gitea.io/gitea/modules/util"
|
||||
pull_service "code.gitea.io/gitea/services/pull"
|
||||
|
||||
"gitea.com/macaron/macaron"
|
||||
)
|
||||
@ -98,6 +99,7 @@ func HookPreReceive(ctx *macaron.Context, opts private.HookOptions) {
|
||||
canPush = protectBranch.CanUserPush(opts.UserID)
|
||||
}
|
||||
if !canPush && opts.ProtectedBranchID > 0 {
|
||||
// Manual merge
|
||||
pr, err := models.GetPullRequestByID(opts.ProtectedBranchID)
|
||||
if err != nil {
|
||||
log.Error("Unable to get PullRequest %d Error: %v", opts.ProtectedBranchID, err)
|
||||
@ -106,24 +108,55 @@ func HookPreReceive(ctx *macaron.Context, opts private.HookOptions) {
|
||||
})
|
||||
return
|
||||
}
|
||||
if !protectBranch.HasEnoughApprovals(pr) {
|
||||
log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v and pr #%d does not have enough approvals", opts.UserID, branchName, repo, pr.Index)
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": fmt.Sprintf("protected branch %s can not be pushed to and pr #%d does not have enough approvals", branchName, opts.ProtectedBranchID),
|
||||
user, err := models.GetUserByID(opts.UserID)
|
||||
if err != nil {
|
||||
log.Error("Unable to get User id %d Error: %v", opts.UserID, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Unable to get User id %d Error: %v", opts.UserID, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
if protectBranch.MergeBlockedByRejectedReview(pr) {
|
||||
log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v and pr #%d has requested changes", opts.UserID, branchName, repo, pr.Index)
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": fmt.Sprintf("protected branch %s can not be pushed to and pr #%d has requested changes", branchName, opts.ProtectedBranchID),
|
||||
perm, err := models.GetUserRepoPermission(repo, user)
|
||||
if err != nil {
|
||||
log.Error("Unable to get Repo permission of repo %s/%s of User %s", repo.OwnerName, repo.Name, user.Name, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Unable to get Repo permission of repo %s/%s of User %s: %v", repo.OwnerName, repo.Name, user.Name, err),
|
||||
})
|
||||
return
|
||||
}
|
||||
allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, perm, user)
|
||||
if err != nil {
|
||||
log.Error("Error calculating if allowed to merge: %v", err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Error calculating if allowed to merge: %v", err),
|
||||
})
|
||||
return
|
||||
}
|
||||
if !allowedMerge {
|
||||
log.Warn("Forbidden: User %d is not allowed to push to protected branch: %s in %-v and is not allowed to merge pr #%d", opts.UserID, branchName, repo, pr.Index)
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Not allowed to push to protected branch %s", branchName),
|
||||
})
|
||||
return
|
||||
}
|
||||
// Manual merge only allowed if PR is ready (even if admin)
|
||||
if err := pull_service.CheckPRReadyToMerge(pr); err != nil {
|
||||
if models.IsErrNotAllowedToMerge(err) {
|
||||
log.Warn("Forbidden: User %d is not allowed push to protected branch %s in %-v and pr #%d is not ready to be merged: %s", opts.UserID, branchName, repo, pr.Index, err.Error())
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Not allowed to push to protected branch %s and pr #%d is not ready to be merged: %s", branchName, opts.ProtectedBranchID, err.Error()),
|
||||
})
|
||||
return
|
||||
}
|
||||
log.Error("Unable to check if mergable: protected branch %s in %-v and pr #%d. Error: %v", opts.UserID, branchName, repo, pr.Index, err)
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]interface{}{
|
||||
"err": fmt.Sprintf("Unable to get status of pull request %d. Error: %v", opts.ProtectedBranchID, err),
|
||||
})
|
||||
}
|
||||
} else if !canPush {
|
||||
log.Warn("Forbidden: User %d cannot push to protected branch: %s in %-v", opts.UserID, branchName, repo)
|
||||
log.Warn("Forbidden: User %d is not allowed to push to protected branch: %s in %-v", opts.UserID, branchName, repo)
|
||||
ctx.JSON(http.StatusForbidden, map[string]interface{}{
|
||||
"err": fmt.Sprintf("protected branch %s can not be pushed to", branchName),
|
||||
"err": fmt.Sprintf("Not allowed to push to protected branch %s", branchName),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
@ -903,6 +903,7 @@ func ViewIssue(ctx *context.Context) {
|
||||
pull := issue.PullRequest
|
||||
pull.Issue = issue
|
||||
canDelete := false
|
||||
ctx.Data["AllowMerge"] = false
|
||||
|
||||
if ctx.IsSigned {
|
||||
if err := pull.GetHeadRepo(); err != nil {
|
||||
@ -923,6 +924,20 @@ func ViewIssue(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := pull.GetBaseRepo(); err != nil {
|
||||
log.Error("GetBaseRepo: %v", err)
|
||||
}
|
||||
perm, err := models.GetUserRepoPermission(pull.BaseRepo, ctx.User)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserRepoPermission", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["AllowMerge"], err = pull_service.IsUserAllowedToMerge(pull, perm, ctx.User)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsUserAllowedToMerge", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
prUnit, err := repo.GetUnit(models.UnitTypePullRequests)
|
||||
@ -932,15 +947,6 @@ func ViewIssue(ctx *context.Context) {
|
||||
}
|
||||
prConfig := prUnit.PullRequestsConfig()
|
||||
|
||||
ctx.Data["AllowMerge"] = ctx.Repo.CanWrite(models.UnitTypeCode)
|
||||
if err := pull.CheckUserAllowedToMerge(ctx.User); err != nil {
|
||||
if !models.IsErrNotAllowedToMerge(err) {
|
||||
ctx.ServerError("CheckUserAllowedToMerge", err)
|
||||
return
|
||||
}
|
||||
ctx.Data["AllowMerge"] = false
|
||||
}
|
||||
|
||||
// Check correct values and select default
|
||||
if ms, ok := ctx.Data["MergeStyle"].(models.MergeStyle); !ok ||
|
||||
!prConfig.IsMergeStyleAllowed(ms) {
|
||||
|
@ -600,6 +600,16 @@ func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) {
|
||||
|
||||
pr := issue.PullRequest
|
||||
|
||||
allowedMerge, err := pull_service.IsUserAllowedToMerge(pr, ctx.Repo.Permission, ctx.User)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsUserAllowedToMerge", err)
|
||||
return
|
||||
}
|
||||
if !allowedMerge {
|
||||
ctx.NotFound("MergePullRequest", nil)
|
||||
return
|
||||
}
|
||||
|
||||
if !pr.CanAutoMerge() || pr.HasMerged {
|
||||
ctx.NotFound("MergePullRequest", nil)
|
||||
return
|
||||
@ -611,15 +621,19 @@ func MergePullRequest(ctx *context.Context, form auth.MergePullRequestForm) {
|
||||
return
|
||||
}
|
||||
|
||||
isPass, err := pull_service.IsPullCommitStatusPass(pr)
|
||||
if err != nil {
|
||||
ctx.ServerError("IsPullCommitStatusPass", err)
|
||||
return
|
||||
}
|
||||
if !isPass && !ctx.IsUserRepoAdmin() {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_status_check"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
|
||||
return
|
||||
if err := pull_service.CheckPRReadyToMerge(pr); err != nil {
|
||||
if !models.IsErrNotAllowedToMerge(err) {
|
||||
ctx.ServerError("Merge PR status", err)
|
||||
return
|
||||
}
|
||||
if isRepoAdmin, err := models.IsUserRepoAdmin(pr.BaseRepo, ctx.User); err != nil {
|
||||
ctx.ServerError("IsUserRepoAdmin", err)
|
||||
return
|
||||
} else if !isRepoAdmin {
|
||||
ctx.Flash.Error(ctx.Tr("repo.pulls.no_merge_not_ready"))
|
||||
ctx.Redirect(ctx.Repo.RepoLink + "/pulls/" + com.ToStr(pr.Index))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.HasError() {
|
||||
|
@ -30,6 +30,7 @@ import (
|
||||
)
|
||||
|
||||
// Merge merges pull request to base repository.
|
||||
// Caller should check PR is ready to be merged (review and status checks)
|
||||
// FIXME: add repoWorkingPull make sure two merges does not happen at same time.
|
||||
func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repository, mergeStyle models.MergeStyle, message string) (err error) {
|
||||
binVersion, err := git.BinVersion()
|
||||
@ -53,11 +54,6 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
|
||||
}
|
||||
prConfig := prUnit.PullRequestsConfig()
|
||||
|
||||
if err := pr.CheckUserAllowedToMerge(doer); err != nil {
|
||||
log.Error("CheckUserAllowedToMerge(%v): %v", doer, err)
|
||||
return fmt.Errorf("CheckUserAllowedToMerge: %v", err)
|
||||
}
|
||||
|
||||
// Check if merge style is correct and allowed
|
||||
if !prConfig.IsMergeStyleAllowed(mergeStyle) {
|
||||
return models.ErrInvalidMergeStyle{ID: pr.BaseRepo.ID, Style: mergeStyle}
|
||||
@ -374,8 +370,10 @@ func Merge(pr *models.PullRequest, doer *models.User, baseGitRepo *git.Repositor
|
||||
return err
|
||||
}
|
||||
close := (ref.RefAction == references.XRefActionCloses)
|
||||
if err = issue_service.ChangeStatus(ref.Issue, doer, close); err != nil {
|
||||
return err
|
||||
if close != ref.Issue.IsClosed {
|
||||
if err = issue_service.ChangeStatus(ref.Issue, doer, close); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -471,3 +469,64 @@ func getDiffTree(repoPath, baseBranch, headBranch string) (string, error) {
|
||||
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
// IsUserAllowedToMerge check if user is allowed to merge PR with given permissions and branch protections
|
||||
func IsUserAllowedToMerge(pr *models.PullRequest, p models.Permission, user *models.User) (bool, error) {
|
||||
if p.IsAdmin() {
|
||||
return true, nil
|
||||
}
|
||||
if !p.CanWrite(models.UnitTypeCode) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
err := pr.LoadProtectedBranch()
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if pr.ProtectedBranch == nil || pr.ProtectedBranch.IsUserMergeWhitelisted(user.ID) {
|
||||
return true, nil
|
||||
}
|
||||
|
||||
return false, nil
|
||||
}
|
||||
|
||||
// CheckPRReadyToMerge checks whether the PR is ready to be merged (reviews and status checks)
|
||||
func CheckPRReadyToMerge(pr *models.PullRequest) (err error) {
|
||||
if pr.BaseRepo == nil {
|
||||
if err = pr.GetBaseRepo(); err != nil {
|
||||
return fmt.Errorf("GetBaseRepo: %v", err)
|
||||
}
|
||||
}
|
||||
if pr.ProtectedBranch == nil {
|
||||
if err = pr.LoadProtectedBranch(); err != nil {
|
||||
return fmt.Errorf("LoadProtectedBranch: %v", err)
|
||||
}
|
||||
if pr.ProtectedBranch == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
isPass, err := IsPullCommitStatusPass(pr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !isPass {
|
||||
return models.ErrNotAllowedToMerge{
|
||||
Reason: "Not all required status checks successful",
|
||||
}
|
||||
}
|
||||
|
||||
if enoughApprovals := pr.ProtectedBranch.HasEnoughApprovals(pr); !enoughApprovals {
|
||||
return models.ErrNotAllowedToMerge{
|
||||
Reason: "Does not have enough approvals",
|
||||
}
|
||||
}
|
||||
if rejected := pr.ProtectedBranch.MergeBlockedByRejectedReview(pr); rejected {
|
||||
return models.ErrNotAllowedToMerge{
|
||||
Reason: "There are requested changes",
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -101,35 +101,31 @@
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.cannot_merge_work_in_progress" .WorkInProgressPrefix | Str2html}}
|
||||
</div>
|
||||
{{else if .IsBlockedByApprovals}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.blocked_by_approvals" .GrantedApprovals .Issue.PullRequest.ProtectedBranch.RequiredApprovals}}
|
||||
</div>
|
||||
{{else if .IsBlockedByRejection}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.blocked_by_rejection"}}
|
||||
</div>
|
||||
{{else if .Issue.PullRequest.IsChecking}}
|
||||
<div class="item text yellow">
|
||||
<span class="octicon octicon-sync"></span>
|
||||
{{$.i18n.Tr "repo.pulls.is_checking"}}
|
||||
</div>
|
||||
{{else if and (not .Issue.PullRequest.CanAutoMerge) .EnableStatusCheck (not .IsRequiredStatusCheckSuccess)}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.required_status_check_failed"}}
|
||||
</div>
|
||||
{{else if .Issue.PullRequest.CanAutoMerge}}
|
||||
{{if and .EnableStatusCheck (not .IsRequiredStatusCheckSuccess)}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.required_status_check_failed"}}
|
||||
</div>
|
||||
{{if .IsBlockedByApprovals}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.blocked_by_approvals" .GrantedApprovals .Issue.PullRequest.ProtectedBranch.RequiredApprovals}}
|
||||
</div>
|
||||
{{else if .IsBlockedByRejection}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.blocked_by_rejection"}}
|
||||
</div>
|
||||
{{else if and .EnableStatusCheck (not .IsRequiredStatusCheckSuccess)}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.required_status_check_failed"}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{if or $.IsRepoAdmin (not .EnableStatusCheck) .IsRequiredStatusCheckSuccess}}
|
||||
{{if and $.IsRepoAdmin .EnableStatusCheck (not .IsRequiredStatusCheckSuccess)}}
|
||||
{{$notAllOk := or .IsBlockedByApprovals .IsBlockedByRejection (and .EnableStatusCheck (not .IsRequiredStatusCheckSuccess))}}
|
||||
{{if or $.IsRepoAdmin (not $notAllOk)}}
|
||||
{{if $notAllOk}}
|
||||
<div class="item text yellow">
|
||||
<span class="octicon octicon-primitive-dot"></span>
|
||||
{{$.i18n.Tr "repo.pulls.required_status_check_administrator"}}
|
||||
@ -216,7 +212,7 @@
|
||||
</form>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="ui green buttons merge-button">
|
||||
<div class="ui {{if $notAllOk}}red{{else}}green{{end}} buttons merge-button">
|
||||
<button class="ui button" data-do="{{.MergeStyle}}">
|
||||
<span class="octicon octicon-git-merge"></span>
|
||||
<span class="button-text">
|
||||
@ -262,17 +258,40 @@
|
||||
{{$.i18n.Tr "repo.pulls.no_merge_helper"}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="item text grey">
|
||||
<span class="octicon octicon-info"></span>
|
||||
{{$.i18n.Tr "repo.pulls.no_merge_access"}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
{{else}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.cannot_auto_merge_desc"}}
|
||||
</div>
|
||||
<div class="item text grey">
|
||||
<span class="octicon octicon-info"></span>
|
||||
{{$.i18n.Tr "repo.pulls.cannot_auto_merge_helper"}}
|
||||
</div>
|
||||
{{/* Merge conflict without specific file. Suggest manual merge, only if all reviews and status checks OK. */}}
|
||||
{{if .IsBlockedByApprovals}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.blocked_by_approvals" .GrantedApprovals .Issue.PullRequest.ProtectedBranch.RequiredApprovals}}
|
||||
</div>
|
||||
{{else if .IsBlockedByRejection}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.blocked_by_rejection"}}
|
||||
</div>
|
||||
{{else if and .EnableStatusCheck (not .IsRequiredStatusCheckSuccess)}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.required_status_check_failed"}}
|
||||
</div>
|
||||
{{else}}
|
||||
<div class="item text red">
|
||||
<span class="octicon octicon-x"></span>
|
||||
{{$.i18n.Tr "repo.pulls.cannot_auto_merge_desc"}}
|
||||
</div>
|
||||
<div class="item text grey">
|
||||
<span class="octicon octicon-info"></span>
|
||||
{{$.i18n.Tr "repo.pulls.cannot_auto_merge_helper"}}
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -66,7 +66,6 @@
|
||||
<div id="swagger-ui"></div>
|
||||
|
||||
<script src="{{StaticUrlPrefix}}/vendor/assets/swagger-ui/swagger-ui-bundle.js"> </script>
|
||||
<script src="{{StaticUrlPrefix}}/vendor/assets/swagger-ui/swagger-ui-standalone-preset.js"> </script>
|
||||
<script>
|
||||
|
||||
window.onload = function() {
|
||||
|
@ -1154,7 +1154,6 @@
|
||||
],
|
||||
"summary": "Create a repository in an organization",
|
||||
"operationId": "createOrgRepo",
|
||||
"deprecated": true,
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
@ -10746,6 +10745,10 @@
|
||||
},
|
||||
"MergeTitleField": {
|
||||
"type": "string"
|
||||
},
|
||||
"force_merge": {
|
||||
"type": "boolean",
|
||||
"x-go-name": "ForceMerge"
|
||||
}
|
||||
},
|
||||
"x-go-name": "MergePullRequestForm",
|
||||
|
Loading…
Reference in New Issue
Block a user