github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/prow/plugins/cla/cla.go (about) 1 /* 2 Copyright 2016 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 cla 18 19 import ( 20 "fmt" 21 "time" 22 23 "github.com/sirupsen/logrus" 24 25 "k8s.io/test-infra/prow/github" 26 "k8s.io/test-infra/prow/plugins" 27 ) 28 29 const ( 30 pluginName = "cla" 31 claContextName = "cla/linuxfoundation" 32 claYesLabel = "cncf-cla: yes" 33 claNoLabel = "cncf-cla: no" 34 cncfclaNotFoundMessage = `Thanks for your pull request. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). 35 36 :memo: **Please follow instructions at <https://github.com/kubernetes/kubernetes/wiki/CLA-FAQ> to sign the CLA.** 37 38 It may take a couple minutes for the CLA signature to be fully registered; after that, please reply here with a new comment and we'll verify. Thanks. 39 40 --- 41 42 - If you've already signed a CLA, it's possible we don't have your GitHub username or you're using a different email address. Check your existing CLA data and verify that your [email is set on your git commits](https://help.github.com/articles/setting-your-email-in-git/). 43 - If you signed the CLA as a corporation, please sign in with your organization's credentials at <https://identity.linuxfoundation.org/projects/cncf> to be authorized. 44 - If you have done the above and are still having issues with the CLA being reported as unsigned, please email the CNCF helpdesk: helpdesk@rt.linuxfoundation.org 45 46 <!-- need_sender_cla --> 47 48 <details> 49 50 %s 51 </details> 52 ` 53 maxRetries = 5 54 ) 55 56 func init() { 57 plugins.RegisterStatusEventHandler(pluginName, handleStatusEvent) 58 } 59 60 type gitHubClient interface { 61 CreateComment(owner, repo string, number int, comment string) error 62 AddLabel(owner, repo string, number int, label string) error 63 RemoveLabel(owner, repo string, number int, label string) error 64 GetPullRequest(owner, repo string, number int) (*github.PullRequest, error) 65 FindIssues(query, sort string, asc bool) ([]github.Issue, error) 66 } 67 68 func handleStatusEvent(pc plugins.PluginClient, se github.StatusEvent) error { 69 return handle(pc.GitHubClient, pc.Logger, se) 70 } 71 72 // 1. Check that the status event received from the webhook is for the CNCF-CLA. 73 // 2. Use the github search API to search for the PRs which match the commit hash corresponding to the status event. 74 // 3. For each issue that matches, check that the PR's HEAD commit hash against the commit hash for which the status 75 // was received. This is because we only care about the status associated with the last (latest) commit in a PR. 76 // 4. Set the corresponding CLA label if needed. 77 func handle(gc gitHubClient, log *logrus.Entry, se github.StatusEvent) error { 78 if se.State == "" || se.Context == "" { 79 return fmt.Errorf("invalid status event delivered with empty state/context") 80 } 81 82 if se.Context != claContextName { 83 // Not the CNCF CLA context, do not process this. 84 return nil 85 } 86 87 if se.State == github.StatusPending { 88 // do nothing and wait for state to be updated. 89 return nil 90 } 91 92 org := se.Repo.Owner.Login 93 repo := se.Repo.Name 94 log.Info("Searching for PRs matching the commit.") 95 96 var issues []github.Issue 97 var err error 98 for i := 0; i < maxRetries; i++ { 99 issues, err = gc.FindIssues(fmt.Sprintf("%s repo:%s/%s type:pr state:open", se.SHA, org, repo), "", false) 100 if err != nil { 101 return fmt.Errorf("error searching for issues matching commit: %v", err) 102 } 103 if len(issues) > 0 { 104 break 105 } 106 time.Sleep(10 * time.Second) 107 } 108 log.Infof("Found %d PRs matching commit.", len(issues)) 109 110 for _, issue := range issues { 111 l := log.WithField("pr", issue.Number) 112 hasCncfYes := issue.HasLabel(claYesLabel) 113 hasCncfNo := issue.HasLabel(claNoLabel) 114 if hasCncfYes && se.State == github.StatusSuccess { 115 // Nothing to update. 116 l.Infof("PR has up-to-date %s label.", claYesLabel) 117 continue 118 } 119 120 if hasCncfNo && (se.State == github.StatusFailure || se.State == github.StatusError) { 121 // Nothing to update. 122 l.Infof("PR has up-to-date %s label.", claNoLabel) 123 continue 124 } 125 126 l.Info("PR labels may be out of date. Getting pull request info.") 127 pr, err := gc.GetPullRequest(org, repo, issue.Number) 128 if err != nil { 129 l.WithError(err).Warningf("Unable to fetch PR-%d from %s/%s.", issue.Number, org, repo) 130 continue 131 } 132 133 // Check if this is the latest commit in the PR. 134 if pr.Head.SHA != se.SHA { 135 l.Info("Event is not for PR HEAD, skipping.") 136 continue 137 } 138 139 number := pr.Number 140 if se.State == github.StatusSuccess { 141 if hasCncfNo { 142 if err := gc.RemoveLabel(org, repo, number, claNoLabel); err != nil { 143 l.WithError(err).Warningf("Could not remove %s label.", claNoLabel) 144 } 145 } 146 if err := gc.AddLabel(org, repo, number, claYesLabel); err != nil { 147 l.WithError(err).Warningf("Could not add %s label.", claYesLabel) 148 } 149 continue 150 } 151 152 // If we end up here, the status is a failure/error. 153 if hasCncfYes { 154 if err := gc.RemoveLabel(org, repo, number, claYesLabel); err != nil { 155 l.WithError(err).Warningf("Could not remove %s label.", claYesLabel) 156 } 157 } 158 if err := gc.CreateComment(org, repo, number, fmt.Sprintf(cncfclaNotFoundMessage, plugins.AboutThisBot)); err != nil { 159 l.WithError(err).Warning("Could not create CLA not found comment.") 160 } 161 if err := gc.AddLabel(org, repo, number, claNoLabel); err != nil { 162 l.WithError(err).Warningf("Could not add %s label.", claNoLabel) 163 } 164 } 165 return nil 166 }