Skip to content

Commit

Permalink
Merge pull request #19813 from tidb-community-bots/rustin-patch-cherr…
Browse files Browse the repository at this point in the history
…ypicker

Add custom label prefix support for cherry picker
  • Loading branch information
k8s-ci-robot authored Nov 9, 2020
2 parents 43e3ada + da90429 commit 475170b
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 70 deletions.
5 changes: 4 additions & 1 deletion prow/external-plugins/cherrypicker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type options struct {
prowAssignments bool
allowAll bool
issueOnConflict bool
labelPrefix string
}

func (o *options) Validate() error {
Expand All @@ -68,6 +69,7 @@ func gatherOptions() options {
fs.BoolVar(&o.prowAssignments, "use-prow-assignments", true, "Use prow commands to assign cherrypicked PRs.")
fs.BoolVar(&o.allowAll, "allow-all", false, "Allow anybody to use automated cherrypicks by skipping GitHub organization membership checks.")
fs.BoolVar(&o.issueOnConflict, "create-issue-on-conflict", false, "Create a GitHub issue and assign it to the requestor on cherrypick conflict.")
fs.StringVar(&o.labelPrefix, "label-prefix", defaultLabelPrefix, "Set a custom label prefix.")
for _, group := range []flagutil.OptionGroup{&o.github} {
group.AddFlags(fs)
}
Expand All @@ -84,7 +86,7 @@ func main() {
logrus.SetFormatter(&logrus.JSONFormatter{})
// TODO: Use global option from the prow config.
logrus.SetLevel(logrus.DebugLevel)
log := logrus.StandardLogger().WithField("plugin", "cherrypick")
log := logrus.StandardLogger().WithField("plugin", pluginName)

secretAgent := &secret.Agent{}
if err := secretAgent.Start([]string{o.github.TokenPath, o.webhookSecretFile}); err != nil {
Expand Down Expand Up @@ -132,6 +134,7 @@ func main() {
prowAssignments: o.prowAssignments,
allowAll: o.allowAll,
issueOnConflict: o.issueOnConflict,
labelPrefix: o.labelPrefix,

bare: &http.Client{},
patchURL: "https://patch-diff.githubusercontent.com",
Expand Down
10 changes: 6 additions & 4 deletions prow/external-plugins/cherrypicker/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import (
)

const pluginName = "cherrypick"
const defaultLabelPrefix = "cherrypick/"

var cherryPickRe = regexp.MustCompile(`(?m)^(?:/cherrypick|/cherry-pick)\s+(.+)$`)
var releaseNoteRe = regexp.MustCompile(`(?s)(?:Release note\*\*:\s*(?:<!--[^<>]*-->\s*)?` + "```(?:release-note)?|```release-note)(.+?)```")
Expand All @@ -63,7 +64,7 @@ type githubClient interface {
// HelpProvider construct the pluginhelp.PluginHelp for this plugin.
func HelpProvider(_ []config.OrgRepo) (*pluginhelp.PluginHelp, error) {
pluginHelp := &pluginhelp.PluginHelp{
Description: `The cherrypick plugin is used for cherrypicking PRs across branches. For every successful cherrypick invocation a new PR is opened against the target branch and assigned to the requester. If the parent PR contains a release note, it is copied to the cherrypick PR.`,
Description: `The cherrypick plugin is used for cherrypicking PRs across branches. For every successful cherrypick invocation a new PR is opened against the target branch and assigned to the requestor. If the parent PR contains a release note, it is copied to the cherrypick PR.`,
}
pluginHelp.AddCommand(pluginhelp.Command{
Usage: "/cherrypick [branch]",
Expand Down Expand Up @@ -97,6 +98,8 @@ type Server struct {
allowAll bool
// Create an issue on cherrypick conflict.
issueOnConflict bool
// Set a custom label prefix.
labelPrefix string

bare *http.Client
patchURL string
Expand Down Expand Up @@ -298,10 +301,9 @@ func (s *Server) handlePullRequest(l *logrus.Entry, pre github.PullRequestEvent)
}

foundCherryPickLabels := false
labelPrefix := "cherrypick/"
for _, label := range labels {
if strings.HasPrefix(label.Name, labelPrefix) {
requestorToComments[pr.User.Login][label.Name[len(labelPrefix):]] = nil // leave this nil which indicates a label-initiated cherry-pick
if strings.HasPrefix(label.Name, s.labelPrefix) {
requestorToComments[pr.User.Login][label.Name[len(s.labelPrefix):]] = nil // leave this nil which indicates a label-initiated cherry-pick
foundCherryPickLabels = true
}
}
Expand Down
162 changes: 97 additions & 65 deletions prow/external-plugins/cherrypicker/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,88 +547,120 @@ func testCherryPickPRWithLabels(clients localgit.Clients, t *testing.T) {
return []byte("sha=abcdefg")
}

for _, evt := range events {
ghc := &fghc{
orgMembers: []github.TeamMember{
{
Login: "approver",
},
testCases := []struct {
name string
labelPrefix string
prLabels []github.Label
}{
{
name: "Default label prefix",
labelPrefix: defaultLabelPrefix,
prLabels: []github.Label{
{
Login: "merge-bot",
Name: "cherrypick/release-1.5",
},
{
Login: "developer",
Name: "cherrypick/release-1.6",
},
},
prComments: []github.IssueComment{
{
User: github.User{
Login: "developer",
},
Body: "a review comment",
Name: "cherrypick/release-1.7",
},
},
},
{
name: "Custom label prefix",
labelPrefix: "needs-cherry-pick-",
prLabels: []github.Label{
{
Name: "cherrypick/release-1.5",
Name: "needs-cherry-pick-release-1.5",
},
{
Name: "cherrypick/release-1.6",
Name: "needs-cherry-pick-release-1.6",
},
{
Name: "cherrypick/release-1.7",
Name: "needs-cherry-pick-release-1.7",
},
},
isMember: true,
patch: patch,
}

s := &Server{
botName: botName,
gc: c,
push: func(newBranch string, force bool) error { return nil },
ghc: ghc,
tokenGenerator: getSecret,
log: logrus.StandardLogger().WithField("client", "cherrypicker"),
repos: []github.Repo{{Fork: true, FullName: "ci-robot/bar"}},

labels: []string{"cla: yes"},
prowAssignments: false,
}

if err := s.handlePullRequest(logrus.NewEntry(logrus.StandardLogger()), pr(evt)); err != nil {
t.Fatalf("unexpected error: %v", err)
}

var expectedFn = func(branch string) string {
expectedTitle := fmt.Sprintf("[%s] This is a fix for Y", branch)
expectedBody := "This is an automated cherry-pick of #2"
expectedHead := fmt.Sprintf(botName+":"+cherryPickBranchFmt, 2, branch)
expectedLabels := s.labels
return fmt.Sprintf(expectedFmt, expectedTitle, expectedBody, expectedHead, branch, expectedLabels)
}

if len(ghc.prs) != 2 {
t.Fatalf("Expected %d PRs, got %d", 2, len(ghc.prs))
}
},
}

expectedBranches := []string{"release-1.5", "release-1.6"}
seenBranches := make(map[string]struct{})
for _, p := range ghc.prs {
pr := prToString(p)
if pr != expectedFn("release-1.5") && pr != expectedFn("release-1.6") {
t.Errorf("Unexpected PR:\n%s\nExpected to target one of the following branches: %v", pr, expectedBranches)
}
if pr == expectedFn("release-1.5") {
seenBranches["release-1.5"] = struct{}{}
}
if pr == expectedFn("release-1.6") {
seenBranches["release-1.6"] = struct{}{}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
for _, evt := range events {
ghc := &fghc{
orgMembers: []github.TeamMember{
{
Login: "approver",
},
{
Login: "merge-bot",
},
{
Login: "developer",
},
},
prComments: []github.IssueComment{
{
User: github.User{
Login: "developer",
},
Body: "a review comment",
},
},
prLabels: tc.prLabels,
isMember: true,
patch: patch,
}

s := &Server{
botName: botName,
gc: c,
push: func(newBranch string, force bool) error { return nil },
ghc: ghc,
tokenGenerator: getSecret,
log: logrus.StandardLogger().WithField("client", "cherrypicker"),
repos: []github.Repo{{Fork: true, FullName: "ci-robot/bar"}},

labels: []string{"cla: yes"},
prowAssignments: false,
labelPrefix: tc.labelPrefix,
}

if err := s.handlePullRequest(logrus.NewEntry(logrus.StandardLogger()), pr(evt)); err != nil {
t.Fatalf("unexpected error: %v", err)
}

var expectedFn = func(branch string) string {
expectedTitle := fmt.Sprintf("[%s] This is a fix for Y", branch)
expectedBody := "This is an automated cherry-pick of #2"
expectedHead := fmt.Sprintf(botName+":"+cherryPickBranchFmt, 2, branch)
expectedLabels := s.labels
return fmt.Sprintf(expectedFmt, expectedTitle, expectedBody, expectedHead, branch, expectedLabels)
}

if len(ghc.prs) != 2 {
t.Fatalf("Expected %d PRs, got %d", 2, len(ghc.prs))
}

expectedBranches := []string{"release-1.5", "release-1.6"}
seenBranches := make(map[string]struct{})
for _, p := range ghc.prs {
pr := prToString(p)
if pr != expectedFn("release-1.5") && pr != expectedFn("release-1.6") {
t.Errorf("Unexpected PR:\n%s\nExpected to target one of the following branches: %v", pr, expectedBranches)
}
if pr == expectedFn("release-1.5") {
seenBranches["release-1.5"] = struct{}{}
}
if pr == expectedFn("release-1.6") {
seenBranches["release-1.6"] = struct{}{}
}
}
if len(seenBranches) != 2 {
t.Fatalf("Expected to see PRs for %d branches, got %d (%v)", 2, len(seenBranches), seenBranches)
}
}
}
if len(seenBranches) != 2 {
t.Fatalf("Expected to see PRs for %d branches, got %d (%v)", 2, len(seenBranches), seenBranches)
}
})
}
}

Expand Down

0 comments on commit 475170b

Please sign in to comment.