github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/api/checks/api.go (about) 1 // Copyright 2018 The WPT Dashboard Project. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 //go:generate mockgen -destination mock_checks/api_mock.go github.com/web-platform-tests/wpt.fyi/api/checks API 6 7 package checks 8 9 import ( 10 "context" 11 "fmt" 12 "net/url" 13 "time" 14 15 "github.com/google/go-github/v47/github" 16 "github.com/web-platform-tests/wpt.fyi/api/checks/summaries" 17 "github.com/web-platform-tests/wpt.fyi/shared" 18 ) 19 20 const ( 21 wptfyiCheckAppID = int64(23318) // https://github.com/apps/wpt-fyi-status-check 22 wptfyiStagingCheckAppID = int64(19965) // https://github.com/apps/staging-wpt-fyi-status-check 23 24 wptRepoInstallationID = int64(577173) 25 wptRepoStagingInstallationID = int64(449270) 26 27 wptRepoID = int64(3618133) 28 checksForAllUsersFeature = "checksAllUsers" 29 ) 30 31 // API abstracts all the API calls used externally. 32 type API interface { 33 shared.AppEngineAPI 34 35 ScheduleResultsProcessing(sha string, browser shared.ProductSpec) error 36 GetSuitesForSHA(sha string) ([]shared.CheckSuite, error) 37 IgnoreFailure(sender, owner, repo string, run *github.CheckRun, installation *github.Installation) error 38 CancelRun(sender, owner, repo string, run *github.CheckRun, installation *github.Installation) error 39 CreateWPTCheckSuite(appID, installationID int64, sha string, prNumbers ...int) (bool, error) 40 GetWPTRepoAppInstallationIDs() (appID, installationID int64) 41 } 42 43 type checksAPIImpl struct { 44 shared.AppEngineAPI 45 46 queue string 47 } 48 49 // NewAPI returns a real implementation of the API. 50 // nolint:ireturn // TODO: Fix ireturn lint error 51 func NewAPI(ctx context.Context) API { 52 return checksAPIImpl{ 53 AppEngineAPI: shared.NewAppEngineAPI(ctx), 54 queue: CheckProcessingQueue, 55 } 56 } 57 58 // ScheduleResultsProcessing adds a URL for callback to TaskQueue for the given sha and 59 // product, which will actually interpret the results and summarize the outcome. 60 func (s checksAPIImpl) ScheduleResultsProcessing(sha string, product shared.ProductSpec) error { 61 log := shared.GetLogger(s.Context()) 62 target := fmt.Sprintf("/api/checks/%s", sha) 63 q := url.Values{} 64 q.Set("product", product.String()) 65 _, err := s.ScheduleTask(s.queue, "", target, q) 66 if err != nil { 67 log.Warningf("Failed to queue %s @ %s: %s", product.String(), sha[:7], err.Error()) 68 } else { 69 log.Infof("Added %s @ %s to checks processing queue", product.String(), sha[:7]) 70 } 71 72 return err 73 } 74 75 // GetSuitesForSHA gets all existing check suites for the given Head SHA. 76 func (s checksAPIImpl) GetSuitesForSHA(sha string) ([]shared.CheckSuite, error) { 77 var suites []shared.CheckSuite 78 store := shared.NewAppEngineDatastore(s.Context(), false) 79 _, err := store.GetAll(store.NewQuery("CheckSuite").Filter("SHA =", sha), &suites) 80 81 return suites, err 82 } 83 84 // IgnoreFailure updates the given CheckRun's outcome to success, even if it failed. 85 func (s checksAPIImpl) IgnoreFailure( 86 sender, 87 owner, repo string, 88 run *github.CheckRun, 89 installation *github.Installation, 90 ) error { 91 client, err := getGitHubClient(s.Context(), run.GetApp().GetID(), installation.GetID()) 92 if err != nil { 93 return err 94 } 95 96 // Keep the previous output, if applicable, but prefix it with an indication that 97 // somebody ignored the failure. 98 output := run.GetOutput() 99 if output == nil { 100 output = &github.CheckRunOutput{} 101 } 102 prepend := fmt.Sprintf("This check was marked as a success by @%s via the _Ignore_ action.\n\n", sender) 103 summary := prepend + output.GetSummary() 104 output.Summary = &summary 105 106 success := "success" 107 // nolint:exhaustruct // WONTFIX: Name only required. 108 opts := github.UpdateCheckRunOptions{ 109 Name: run.GetName(), 110 Output: output, 111 Conclusion: &success, 112 CompletedAt: &github.Timestamp{Time: time.Now()}, 113 Actions: []*github.CheckRunAction{ 114 summaries.RecomputeAction(), 115 }, 116 } 117 _, _, err = client.Checks.UpdateCheckRun(s.Context(), owner, repo, run.GetID(), opts) 118 119 return err 120 } 121 122 // CancelRun updates the given CheckRun's outcome to cancelled, even if it failed. 123 func (s checksAPIImpl) CancelRun( 124 sender, 125 owner, 126 repo string, 127 run *github.CheckRun, 128 installation *github.Installation, 129 ) error { 130 client, err := getGitHubClient(s.Context(), run.GetApp().GetID(), installation.GetID()) 131 if err != nil { 132 return err 133 } 134 135 // Keep the previous output, if applicable, but prefix it with an indication that 136 // somebody ignored the failure. 137 summary := fmt.Sprintf("This check was cancelled by @%s via the _Cancel_ action.", sender) 138 title := run.GetOutput().GetTitle() 139 output := &github.CheckRunOutput{ 140 Title: &title, 141 Summary: &summary, 142 } 143 144 cancelled := "cancelled" 145 // nolint:exhaustruct // WONTFIX: Name only required. 146 opts := github.UpdateCheckRunOptions{ 147 Name: run.GetName(), 148 Output: output, 149 Conclusion: &cancelled, 150 CompletedAt: &github.Timestamp{Time: time.Now()}, 151 Actions: []*github.CheckRunAction{ 152 summaries.RecomputeAction(), 153 summaries.IgnoreAction(), 154 }, 155 } 156 _, _, err = client.Checks.UpdateCheckRun(s.Context(), owner, repo, run.GetID(), opts) 157 158 return err 159 } 160 161 // CreateWPTCheckSuite creates a check_suite on the main wpt repo for the given 162 // SHA. This is needed when a PR comes from a different fork of the repo. 163 func (s checksAPIImpl) CreateWPTCheckSuite(appID, installationID int64, sha string, prNumbers ...int) (bool, error) { 164 log := shared.GetLogger(s.Context()) 165 log.Debugf("Creating check_suite for web-platform-tests/wpt @ %s", sha) 166 167 client, err := getGitHubClient(s.Context(), appID, installationID) 168 if err != nil { 169 return false, err 170 } 171 172 // nolint:exhaustruct // WONTFIX: HeadSHA only required. 173 opts := github.CreateCheckSuiteOptions{ 174 HeadSHA: sha, 175 } 176 suite, _, err := client.Checks.CreateCheckSuite(s.Context(), shared.WPTRepoOwner, shared.WPTRepoName, opts) 177 if err != nil { 178 log.Errorf("Failed to create GitHub check suite: %s", err.Error()) 179 } else if suite != nil { 180 log.Infof("check_suite %v created", suite.GetID()) 181 _, err = getOrCreateCheckSuite( 182 s.Context(), 183 sha, 184 shared.WPTRepoOwner, 185 shared.WPTRepoName, 186 appID, 187 installationID, 188 prNumbers..., 189 ) 190 if err != nil { 191 log.Infof("Error while getting check suite: %s", err.Error()) 192 } 193 } 194 195 return suite != nil, err 196 } 197 198 func (s checksAPIImpl) GetWPTRepoAppInstallationIDs() (appID, installationID int64) { 199 // Production 200 if s.GetHostname() == "wpt.fyi" { 201 return wptfyiCheckAppID, wptRepoInstallationID 202 } 203 // Default to staging 204 return wptfyiStagingCheckAppID, wptRepoStagingInstallationID 205 }