github.com/autonomy/conform@v0.1.0-alpha.16/internal/summarizer/summarizer.go (about) 1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 package summarizer 6 7 import ( 8 "context" 9 "encoding/json" 10 "errors" 11 "fmt" 12 "io/ioutil" 13 "log" 14 "net/http" 15 "os" 16 "path" 17 "strings" 18 19 "github.com/autonomy/conform/internal/git" 20 "github.com/google/go-github/github" 21 ) 22 23 // Summarizer describes a hook for send summarized results to a remote API. 24 type Summarizer interface { 25 SetStatus(string, string, string, string) error 26 } 27 28 // GitHub is a summarizer that can be used with GitHub. 29 type GitHub struct { 30 token string 31 owner string 32 repo string 33 sha string 34 } 35 36 // Noop is a summarizer that does nothing. 37 type Noop struct { 38 } 39 40 // SetStatus is a noop func. 41 func (n *Noop) SetStatus(state, policy, check, message string) error { 42 return nil 43 } 44 45 // NewGitHubSummarizer returns a summarizer that posts policy checks as status 46 // checks on a pull request. 47 func NewGitHubSummarizer(token string) (*GitHub, error) { 48 eventPath, ok := os.LookupEnv("GITHUB_EVENT_PATH") 49 if !ok { 50 return nil, errors.New("GITHUB_EVENT_PATH is not set") 51 } 52 53 data, err := ioutil.ReadFile(eventPath) 54 if err != nil { 55 return nil, err 56 } 57 58 pullRequestEvent := &github.PullRequestEvent{} 59 if err = json.Unmarshal(data, pullRequestEvent); err != nil { 60 return nil, err 61 } 62 63 g, err := git.NewGit() 64 if err != nil { 65 return nil, err 66 } 67 68 if err = g.FetchPullRequest("origin", pullRequestEvent.GetNumber()); err != nil { 69 return nil, err 70 } 71 72 if err = g.CheckoutPullRequest(pullRequestEvent.GetNumber()); err != nil { 73 return nil, err 74 } 75 76 sha, err := g.SHA() 77 if err != nil { 78 log.Fatal(err) 79 } 80 81 gh := &GitHub{ 82 token: token, 83 owner: pullRequestEvent.GetRepo().GetOwner().GetLogin(), 84 repo: pullRequestEvent.GetRepo().GetName(), 85 sha: sha, 86 } 87 88 return gh, nil 89 } 90 91 // SetStatus sets the status of a GitHub check. 92 // Valid statuses are "error", "failure", "pending", "success" 93 func (gh *GitHub) SetStatus(state, policy, check, message string) error { 94 if gh.token == "" { 95 return errors.New("no token") 96 } 97 statusCheckContext := strings.ReplaceAll(strings.ToLower(path.Join("conform", policy, check)), " ", "-") 98 description := message 99 repoStatus := &github.RepoStatus{} 100 repoStatus.Context = &statusCheckContext 101 repoStatus.Description = &description 102 repoStatus.State = &state 103 104 http.DefaultClient.Transport = roundTripper{gh.token} 105 githubClient := github.NewClient(http.DefaultClient) 106 107 _, _, err := githubClient.Repositories.CreateStatus(context.Background(), gh.owner, gh.repo, gh.sha, repoStatus) 108 if err != nil { 109 return err 110 } 111 112 return nil 113 } 114 115 type roundTripper struct { 116 accessToken string 117 } 118 119 // RoundTrip implements the net/http.RoundTripper interface. 120 func (rt roundTripper) RoundTrip(r *http.Request) (*http.Response, error) { 121 r.Header.Set("Authorization", fmt.Sprintf("Bearer %s", rt.accessToken)) 122 return http.DefaultTransport.RoundTrip(r) 123 }