-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Supports wildcard protected branch #20825
Changes from 20 commits
9f9f488
1dfa785
bf27be6
963e438
6f39f75
588c5b7
997a01d
9740a6d
b12ac62
5e55875
553869a
aba5aad
80d2396
e894bdf
957a20b
d87e6bd
30bbfd0
a82b8d1
1837aae
a5cc6d3
ad304ec
3425685
17a7982
86b0029
c0989ed
562077e
fbb17f3
220d385
0c234ab
31d6595
0c1ef7f
b23a742
b428771
300e623
3e0eefc
398123b
94b5ae6
e392b85
4ec2dcb
accc65c
5b23369
fee37c3
e50aad2
b0fa0ef
71310ec
d95f214
bdf75cc
7e43b21
5e8aa8c
11fcf57
841cf18
361baca
6a55815
40577d7
39e28c5
8fbe8ad
16ca6c2
9f18da1
ded5545
e40338c
8ab38d9
389dce2
2eeb920
9e3a06d
73a589c
e378263
59c0714
01c2057
5d078d4
f5c010b
c1c1a8b
1bbc980
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,87 @@ | ||||||||||||
// Copyright 2022 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 git | ||||||||||||
|
||||||||||||
import ( | ||||||||||||
"context" | ||||||||||||
"sort" | ||||||||||||
|
||||||||||||
"code.gitea.io/gitea/models/db" | ||||||||||||
"code.gitea.io/gitea/modules/git" | ||||||||||||
|
||||||||||||
"github.com/gobwas/glob" | ||||||||||||
) | ||||||||||||
|
||||||||||||
type ProtectedBranchRules []*ProtectedBranch | ||||||||||||
|
||||||||||||
func (rules ProtectedBranchRules) GetFirstMatched(branchName string) *ProtectedBranch { | ||||||||||||
lunny marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
for _, rule := range rules { | ||||||||||||
if rule.Match(branchName) { | ||||||||||||
return rule | ||||||||||||
} | ||||||||||||
} | ||||||||||||
return nil | ||||||||||||
} | ||||||||||||
|
||||||||||||
func (rules ProtectedBranchRules) Sort() { | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
lunny marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
sort.Slice(rules, func(i, j int) bool { | ||||||||||||
rules[i].loadGlob() | ||||||||||||
rules[j].loadGlob() | ||||||||||||
if rules[i].isPlainName { | ||||||||||||
if !rules[j].isPlainName { | ||||||||||||
return true | ||||||||||||
} | ||||||||||||
} else if rules[j].isPlainName { | ||||||||||||
return true | ||||||||||||
} | ||||||||||||
return rules[i].CreatedUnix < rules[j].CreatedUnix | ||||||||||||
lunny marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||
}) | ||||||||||||
} | ||||||||||||
|
||||||||||||
// FindRepoProtectedBranchRules load all repository's protected rules | ||||||||||||
func FindRepoProtectedBranchRules(ctx context.Context, repoID int64) (ProtectedBranchRules, error) { | ||||||||||||
var rules ProtectedBranchRules | ||||||||||||
err := db.GetEngine(ctx).Where("repo_id = ?", repoID).Asc("created_unix").Find(&rules) | ||||||||||||
if err != nil { | ||||||||||||
return nil, err | ||||||||||||
} | ||||||||||||
rules.Sort() | ||||||||||||
return rules, nil | ||||||||||||
} | ||||||||||||
|
||||||||||||
// FindAllMatchedBranches find all matched branches | ||||||||||||
func FindAllMatchedBranches(ctx context.Context, gitRepo *git.Repository, ruleName string) ([]string, error) { | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the purpose of this function? |
||||||||||||
// FIXME: how many should we get? | ||||||||||||
branches, _, err := gitRepo.GetBranchNames(0, 9999999) | ||||||||||||
if err != nil { | ||||||||||||
return nil, err | ||||||||||||
} | ||||||||||||
rule := glob.MustCompile(ruleName) | ||||||||||||
results := make([]string, 0, len(branches)) | ||||||||||||
for _, branch := range branches { | ||||||||||||
if rule.Match(branch) { | ||||||||||||
results = append(results, branch) | ||||||||||||
} | ||||||||||||
} | ||||||||||||
return results, nil | ||||||||||||
} | ||||||||||||
|
||||||||||||
// GetFirstMatchProtectedBranchRule returns the first matched rules | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
func GetFirstMatchProtectedBranchRule(ctx context.Context, repoID int64, branchName string) (*ProtectedBranch, error) { | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, should we perhaps omit the |
||||||||||||
rules, err := FindRepoProtectedBranchRules(ctx, repoID) | ||||||||||||
if err != nil { | ||||||||||||
return nil, err | ||||||||||||
} | ||||||||||||
return rules.GetFirstMatched(branchName), nil | ||||||||||||
} | ||||||||||||
|
||||||||||||
// IsBranchProtected checks if branch is protected | ||||||||||||
func IsBranchProtected(repoID int64, branchName string) (bool, error) { | ||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, again I'm wondering: When do you need this information without requiring the underlying branch protection? |
||||||||||||
rule, err := GetFirstMatchProtectedBranchRule(db.DefaultContext, repoID, branchName) | ||||||||||||
if err != nil { | ||||||||||||
return false, err | ||||||||||||
} | ||||||||||||
return rule != nil, nil | ||||||||||||
Comment on lines
+82
to
+85
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// Copyright 2022 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 git | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestBranchRuleMatch(t *testing.T) { | ||
kases := []struct { | ||
Rule string | ||
BranchName string | ||
ExpectedMatch bool | ||
}{ | ||
{ | ||
Rule: "release/*", | ||
BranchName: "release/v1.17", | ||
ExpectedMatch: true, | ||
}, | ||
{ | ||
Rule: "release/**/v1.17", | ||
BranchName: "release/test/v1.17", | ||
Comment on lines
+25
to
+26
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I do have to say, I find especially the edge case {
"Rule": "release/**/v1.17",
"BranchName": "release/v1.17",
"ExpectedMatch": true,
} interesting. |
||
ExpectedMatch: true, | ||
}, | ||
{ | ||
Rule: "release/**/v1.17", | ||
BranchName: "release/test/1/v1.17", | ||
ExpectedMatch: true, | ||
}, | ||
{ | ||
Rule: "release/*/v1.17", | ||
BranchName: "release/test/1/v1.17", | ||
ExpectedMatch: false, | ||
}, | ||
{ | ||
Rule: "release/v*", | ||
BranchName: "release/v1.16", | ||
ExpectedMatch: true, | ||
}, | ||
{ | ||
Rule: "*", | ||
BranchName: "release/v1.16", | ||
ExpectedMatch: false, | ||
}, | ||
{ | ||
Rule: "**", | ||
BranchName: "release/v1.16", | ||
ExpectedMatch: true, | ||
}, | ||
} | ||
|
||
for _, kase := range kases { | ||
pb := ProtectedBranch{BranchName: kase.Rule} | ||
var should, infact string | ||
if !kase.ExpectedMatch { | ||
should = " not" | ||
} else { | ||
infact = " not" | ||
} | ||
assert.EqualValues(t, kase.ExpectedMatch, pb.Match(kase.BranchName), | ||
fmt.Sprintf("%s should%s match %s but it is%s", kase.BranchName, should, kase.Rule, infact), | ||
) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -272,15 +272,17 @@ func IsOfficialReviewer(ctx context.Context, issue *Issue, reviewers ...*user_mo | |||||||||||||||||
if err != nil { | ||||||||||||||||||
return false, err | ||||||||||||||||||
} | ||||||||||||||||||
if err = pr.LoadProtectedBranchCtx(ctx); err != nil { | ||||||||||||||||||
|
||||||||||||||||||
rule, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
return false, err | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
+267
to
270
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
if pr.ProtectedBranch == nil { | ||||||||||||||||||
if rule == nil { | ||||||||||||||||||
return false, nil | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
+271
to
273
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
|
||||||||||||||||||
for _, reviewer := range reviewers { | ||||||||||||||||||
official, err := git_model.IsUserOfficialReviewerCtx(ctx, pr.ProtectedBranch, reviewer) | ||||||||||||||||||
official, err := git_model.IsUserOfficialReviewer(ctx, rule, reviewer) | ||||||||||||||||||
if official || err != nil { | ||||||||||||||||||
return official, err | ||||||||||||||||||
} | ||||||||||||||||||
|
@@ -295,18 +297,19 @@ func IsOfficialReviewerTeam(ctx context.Context, issue *Issue, team *organizatio | |||||||||||||||||
if err != nil { | ||||||||||||||||||
return false, err | ||||||||||||||||||
} | ||||||||||||||||||
if err = pr.LoadProtectedBranchCtx(ctx); err != nil { | ||||||||||||||||||
pb, err := git_model.GetFirstMatchProtectedBranchRule(ctx, pr.BaseRepoID, pr.BaseBranch) | ||||||||||||||||||
if err != nil { | ||||||||||||||||||
return false, err | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
+292
to
294
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
if pr.ProtectedBranch == nil { | ||||||||||||||||||
if pb == nil { | ||||||||||||||||||
return false, nil | ||||||||||||||||||
} | ||||||||||||||||||
Comment on lines
+295
to
297
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||
|
||||||||||||||||||
if !pr.ProtectedBranch.EnableApprovalsWhitelist { | ||||||||||||||||||
if !pb.EnableApprovalsWhitelist { | ||||||||||||||||||
return team.UnitAccessModeCtx(ctx, unit.TypeCode) >= perm.AccessModeWrite, nil | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
return base.Int64sContains(pr.ProtectedBranch.ApprovalsWhitelistTeamIDs, team.ID), nil | ||||||||||||||||||
return base.Int64sContains(pb.ApprovalsWhitelistTeamIDs, team.ID), nil | ||||||||||||||||||
} | ||||||||||||||||||
|
||||||||||||||||||
// CreateReview creates a new review based on opts | ||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,7 +53,7 @@ type BranchProtection struct { | |
|
||
// CreateBranchProtectionOption options for creating a branch protection | ||
type CreateBranchProtectionOption struct { | ||
BranchName string `json:"branch_name"` | ||
RuleName string `json:"branch_name"` // it now in fact stores rule name not only branch name | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there perhaps an option to create aliases? |
||
EnablePush bool `json:"enable_push"` | ||
EnablePushWhitelist bool `json:"enable_push_whitelist"` | ||
PushWhitelistUsernames []string `json:"push_whitelist_usernames"` | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.