Skip to content

Commit 45a4056

Browse files
committed
Add branch protection checking
1 parent 2ad3451 commit 45a4056

File tree

1 file changed

+50
-1
lines changed

1 file changed

+50
-1
lines changed

services/repository/branch.go

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
webhook_module "code.gitea.io/gitea/modules/webhook"
3333
actions_service "code.gitea.io/gitea/services/actions"
3434
notify_service "code.gitea.io/gitea/services/notify"
35+
pull_service "code.gitea.io/gitea/services/pull"
3536
release_service "code.gitea.io/gitea/services/release"
3637

3738
"xorm.io/builder"
@@ -546,10 +547,58 @@ func UpdateBranch(ctx context.Context, repo *repo_model.Repository, doer *user_m
546547
return &git.ErrPushOutOfDate{Err: errors.New("non fast-forward update requires force"), StdErr: "non-fast-forward", StdOut: ""}
547548
}
548549

550+
pushEnv := repo_module.PushingEnvironment(doer, repo)
551+
552+
protectedBranch, err := git_model.GetFirstMatchProtectedBranchRule(ctx, repo.ID, branchName)
553+
if err != nil {
554+
return fmt.Errorf("GetFirstMatchProtectedBranchRule: %w", err)
555+
}
556+
if protectedBranch != nil {
557+
protectedBranch.Repo = repo
558+
globsProtected := protectedBranch.GetProtectedFilePatterns()
559+
if len(globsProtected) > 0 {
560+
changedProtectedFiles, protectErr := pull_service.CheckFileProtection(gitRepo, branchName, currentCommitID, newCommit.ID.String(), globsProtected, 1, pushEnv)
561+
if protectErr != nil {
562+
if !pull_service.IsErrFilePathProtected(protectErr) {
563+
return fmt.Errorf("CheckFileProtection: %w", protectErr)
564+
}
565+
protectedPath := ""
566+
if len(changedProtectedFiles) > 0 {
567+
protectedPath = changedProtectedFiles[0]
568+
} else if pathErr, ok := protectErr.(pull_service.ErrFilePathProtected); ok {
569+
protectedPath = pathErr.Path
570+
}
571+
if protectedPath == "" {
572+
protectedPath = branchName
573+
}
574+
return &git.ErrPushRejected{Message: fmt.Sprintf("branch %s is protected from changing file %s", branchName, protectedPath)}
575+
}
576+
}
577+
578+
if isForcePush {
579+
if !protectedBranch.CanUserForcePush(ctx, doer) {
580+
return &git.ErrPushRejected{Message: "Not allowed to force-push to protected branch " + branchName}
581+
}
582+
} else if !protectedBranch.CanUserPush(ctx, doer) {
583+
globsUnprotected := protectedBranch.GetUnprotectedFilePatterns()
584+
if len(globsUnprotected) > 0 {
585+
unprotectedOnly, unprotectedErr := pull_service.CheckUnprotectedFiles(gitRepo, branchName, currentCommitID, newCommit.ID.String(), globsUnprotected, pushEnv)
586+
if unprotectedErr != nil {
587+
return fmt.Errorf("CheckUnprotectedFiles: %w", unprotectedErr)
588+
}
589+
if !unprotectedOnly {
590+
return &git.ErrPushRejected{Message: "Not allowed to push to protected branch " + branchName}
591+
}
592+
} else {
593+
return &git.ErrPushRejected{Message: "Not allowed to push to protected branch " + branchName}
594+
}
595+
}
596+
}
597+
549598
pushOpts := git.PushOptions{
550599
Remote: repo.RepoPath(),
551600
Branch: fmt.Sprintf("%s:%s%s", newCommit.ID.String(), git.BranchPrefix, branchName),
552-
Env: repo_module.PushingEnvironment(doer, repo),
601+
Env: pushEnv,
553602
}
554603

555604
if expectedOldCommitID != "" {

0 commit comments

Comments
 (0)