sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/plugins/milestoneapplier/milestoneapplier.go (about) 1 /* 2 Copyright 2019 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 milestoneapplier implements the plugin to automatically apply 18 // the configured milestone after a PR is merged. 19 package milestoneapplier 20 21 import ( 22 "fmt" 23 "strings" 24 25 "github.com/sirupsen/logrus" 26 27 "sigs.k8s.io/prow/pkg/config" 28 "sigs.k8s.io/prow/pkg/github" 29 "sigs.k8s.io/prow/pkg/pluginhelp" 30 "sigs.k8s.io/prow/pkg/plugins" 31 "sigs.k8s.io/prow/pkg/plugins/milestone" 32 ) 33 34 const pluginName = "milestoneapplier" 35 36 type githubClient interface { 37 SetMilestone(org, repo string, issueNum, milestoneNum int) error 38 ListMilestones(org, repo string) ([]github.Milestone, error) 39 } 40 41 func init() { 42 plugins.RegisterPullRequestHandler(pluginName, handlePullRequest, helpProvider) 43 } 44 45 func helpProvider(config *plugins.Configuration, enabledRepos []config.OrgRepo) (*pluginhelp.PluginHelp, error) { 46 configInfo := map[string]string{} 47 for _, repo := range enabledRepos { 48 var branchesToMilestone []string 49 for branch, milestone := range config.MilestoneApplier[repo.String()] { 50 branchesToMilestone = append(branchesToMilestone, fmt.Sprintf("- `%s`: `%s`", branch, milestone)) 51 } 52 configInfo[repo.String()] = fmt.Sprintf("The configured branches and milestones for this repo are:\n%s", strings.Join(branchesToMilestone, "\n")) 53 } 54 55 // The {WhoCanUse, Usage, Examples} fields are omitted because this plugin is not triggered with commands. 56 yamlSnippet, err := plugins.CommentMap.GenYaml(&plugins.Configuration{ 57 MilestoneApplier: map[string]plugins.BranchToMilestone{ 58 "kubernetes/kubernetes": { 59 "release-1.19": "v1.19", 60 "release-1.18": "v1.18", 61 }, 62 }, 63 }) 64 if err != nil { 65 logrus.WithError(err).Warnf("cannot generate comments for %s plugin", pluginName) 66 } 67 return &pluginhelp.PluginHelp{ 68 Description: "The milestoneapplier plugin automatically applies the configured milestone for the base branch after a PR is merged. If a PR targets a non-default branch, it also adds the milestone when the PR is opened.", 69 Config: configInfo, 70 Snippet: yamlSnippet, 71 }, nil 72 } 73 74 func handlePullRequest(pc plugins.Agent, pre github.PullRequestEvent) error { 75 org := pre.PullRequest.Base.Repo.Owner.Login 76 repo := pre.PullRequest.Base.Repo.Name 77 baseBranch := pre.PullRequest.Base.Ref 78 79 // if there are no branch to milestone mappings for this repo, return early 80 branchToMilestone, ok := pc.PluginConfig.MilestoneApplier[fmt.Sprintf("%s/%s", org, repo)] 81 if !ok { 82 return nil 83 } 84 // if the repo does not define milestones for this branch, return early 85 milestone, ok := branchToMilestone[baseBranch] 86 if !ok { 87 return nil 88 } 89 90 return handle(pc.GitHubClient, pc.Logger, milestone, pre) 91 } 92 93 func handle(gc githubClient, log *logrus.Entry, configuredMilestone string, pre github.PullRequestEvent) error { 94 pr := pre.PullRequest 95 96 // if the current milestone is equal to the configured milestone, return early 97 if pr.Milestone != nil && pr.Milestone.Title == configuredMilestone { 98 return nil 99 } 100 101 // if a PR targets a non-default branch, apply milestone when opened and on merge 102 // if a PR targets the default branch, apply the milestone only on merge 103 merged := pre.Action == github.PullRequestActionClosed && pr.Merged 104 if pr.Base.Repo.DefaultBranch != pr.Base.Ref { 105 if !merged && pre.Action != github.PullRequestActionOpened { 106 return nil 107 } 108 } else if !merged { 109 return nil 110 } 111 112 number := pre.Number 113 org := pr.Base.Repo.Owner.Login 114 repo := pr.Base.Repo.Name 115 116 milestones, err := gc.ListMilestones(org, repo) 117 if err != nil { 118 log.WithError(err).Errorf("Error listing the milestones in the %s/%s repo", org, repo) 119 return err 120 } 121 122 milestoneMap := milestone.BuildMilestoneMap(milestones) 123 configuredMilestoneNumber, ok := milestoneMap[configuredMilestone] 124 if !ok { 125 return fmt.Errorf("The configured milestone %s for %s branch does not exist in the %s/%s repo", configuredMilestone, pr.Base.Ref, org, repo) 126 } 127 128 if err := gc.SetMilestone(org, repo, number, configuredMilestoneNumber); err != nil { 129 log.WithError(err).Errorf("Error adding the milestone %s to %s/%s#%d.", configuredMilestone, org, repo, number) 130 return err 131 } 132 133 return nil 134 }