github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/milestonestatus/milestonestatus.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // Package milestonestatus implements the `/status` command which allows members of the milestone 18 // maintainers team to specify a `status/*` label to be applied to an Issue or PR. 19 package milestonestatus 20 21 import ( 22 "fmt" 23 "regexp" 24 "strings" 25 26 "github.com/sirupsen/logrus" 27 28 "k8s.io/test-infra/prow/github" 29 "k8s.io/test-infra/prow/pluginhelp" 30 "k8s.io/test-infra/prow/plugins" 31 ) 32 33 const pluginName = "milestonestatus" 34 35 var ( 36 statusRegex = regexp.MustCompile(`(?m)^/status\s+(.+)$`) 37 mustBeSigLead = "You must be a member of the [%s/%s](https://github.com/orgs/%s/teams/%s/members) github team to add status labels." 38 milestoneTeamMsg = "The milestone maintainers team is the Github team with ID: %d." 39 statusMap = map[string]string{ 40 "approved-for-milestone": "status/approved-for-milestone", 41 "in-progress": "status/in-progress", 42 "in-review": "status/in-review", 43 } 44 ) 45 46 type githubClient interface { 47 CreateComment(owner, repo string, number int, comment string) error 48 AddLabel(owner, repo string, number int, label string) error 49 ListTeamMembers(id int, role string) ([]github.TeamMember, error) 50 } 51 52 func init() { 53 plugins.RegisterGenericCommentHandler(pluginName, handleGenericComment, helpProvider) 54 } 55 56 func helpProvider(config *plugins.Configuration, enabledRepos []string) (*pluginhelp.PluginHelp, error) { 57 pluginHelp := &pluginhelp.PluginHelp{ 58 Description: "The milestonestatus plugin allows members of the milestone maintainers Github team to specify the 'status/*' label that should apply to a pull request.", 59 Config: func() map[string]string { 60 configMap := make(map[string]string) 61 for _, repo := range enabledRepos { 62 team, exists := config.RepoMilestone[repo] 63 if exists { 64 configMap[repo] = fmt.Sprintf(milestoneTeamMsg, team) 65 } 66 } 67 configMap[""] = fmt.Sprintf(milestoneTeamMsg, config.RepoMilestone[""]) 68 return configMap 69 }(), 70 } 71 pluginHelp.AddCommand(pluginhelp.Command{ 72 Usage: "/status (approved-for-milestone|in-progress|in-review)", 73 Description: "Applies the 'status/' label to a PR.", 74 Featured: false, 75 WhoCanUse: "Members of the milestone maintainers Github team can use the '/status' command. This team is specified in the config by providing the Github team's ID.", 76 Examples: []string{"/status approved-for-milestone", "/status in-progress", "/status in-review"}, 77 }) 78 return pluginHelp, nil 79 } 80 81 func handleGenericComment(pc plugins.Agent, e github.GenericCommentEvent) error { 82 return handle(pc.GitHubClient, pc.Logger, &e, pc.PluginConfig.RepoMilestone) 83 } 84 85 func handle(gc githubClient, log *logrus.Entry, e *github.GenericCommentEvent, repoMilestone map[string]plugins.Milestone) error { 86 if e.Action != github.GenericCommentActionCreated { 87 return nil 88 } 89 90 statusMatches := statusRegex.FindAllStringSubmatch(e.Body, -1) 91 if len(statusMatches) == 0 { 92 return nil 93 } 94 95 org := e.Repo.Owner.Login 96 repo := e.Repo.Name 97 98 milestone, exists := repoMilestone[fmt.Sprintf("%s/%s", org, repo)] 99 if !exists { 100 // fallback default 101 milestone = repoMilestone[""] 102 } 103 104 milestoneMaintainers, err := gc.ListTeamMembers(milestone.MaintainersID, github.RoleAll) 105 if err != nil { 106 return err 107 } 108 found := false 109 for _, person := range milestoneMaintainers { 110 login := strings.ToLower(e.User.Login) 111 if strings.ToLower(person.Login) == login { 112 found = true 113 break 114 } 115 } 116 if !found { 117 // not in the milestone maintainers team 118 msg := fmt.Sprintf(mustBeSigLead, org, milestone.MaintainersTeam, org, milestone.MaintainersTeam) 119 return gc.CreateComment(org, repo, e.Number, msg) 120 } 121 122 for _, statusMatch := range statusMatches { 123 sLabel, validStatus := statusMap[strings.TrimSpace(statusMatch[1])] 124 if !validStatus { 125 continue 126 } 127 if err := gc.AddLabel(org, repo, e.Number, sLabel); err != nil { 128 log.WithError(err).Errorf("Error adding the label %q to %s/%s#%d.", sLabel, org, repo, e.Number) 129 } 130 } 131 return nil 132 }