From 40989eb146a06df464b4b222c41dab8af0db7252 Mon Sep 17 00:00:00 2001 From: Ben Broderick Phillips Date: Thu, 29 Dec 2022 17:08:34 -0500 Subject: [PATCH] Revert "Revert "Support check enforcer with Github Actions (#7)" (#8)" (#9) --- comments/no_pipelines.txt | 2 +- github_client.go | 61 ++- main.go | 96 ++-- main_test.go | 410 +++++++++++++----- .../multiple_check_suite_response.json | 361 +++++++++++++++ ...tiple_with_empty_check_suite_response.json | 385 ++++++++++++++++ ...ingle_with_empty_check_suite_response.json | 177 ++++++++ testpayloads/workflow_run_event.json | 385 ++++++++++++++++ types.go | 78 ++-- 9 files changed, 1757 insertions(+), 198 deletions(-) create mode 100644 testpayloads/multiple_check_suite_response.json create mode 100644 testpayloads/multiple_with_empty_check_suite_response.json create mode 100644 testpayloads/single_with_empty_check_suite_response.json create mode 100644 testpayloads/workflow_run_event.json diff --git a/comments/no_pipelines.txt b/comments/no_pipelines.txt index 67148c8..1d5d167 100644 --- a/comments/no_pipelines.txt +++ b/comments/no_pipelines.txt @@ -1,4 +1,4 @@ -Check Enforcer evaluate was requested, but there are no Azure Pipelines configured to trigger for the changed files. +Check Enforcer evaluate was requested, but no Azure Pipelines or Github Actions have been triggered for the changed files. If you are initializing a new service, follow the [new service docs](https://aka.ms/azsdk/checkenforcer#onboarding-a-new-service). If no Azure Pipelines are desired, run `/check-enforcer override`. diff --git a/github_client.go b/github_client.go index 5a356d9..12bc22a 100644 --- a/github_client.go +++ b/github_client.go @@ -11,22 +11,22 @@ import ( ) type GithubClient struct { - client *http.Client - token string - BaseUrl url.URL - AppTarget string + client *http.Client + token string + BaseUrl url.URL + AppTargets []string } -func NewGithubClient(baseUrl string, token string, appTarget string) (*GithubClient, error) { +func NewGithubClient(baseUrl string, token string, appTargets ...string) (*GithubClient, error) { u, err := url.Parse(baseUrl) if err != nil { return nil, err } return &GithubClient{ - client: &http.Client{}, - BaseUrl: *u, - token: token, - AppTarget: appTarget, + client: &http.Client{}, + BaseUrl: *u, + token: token, + AppTargets: appTargets, }, nil } @@ -102,17 +102,37 @@ func (gh *GithubClient) GetPullRequest(pullsUrl string) (PullRequest, error) { return pr, nil } -func (gh *GithubClient) GetCheckSuiteStatus(pr PullRequest) (CheckSuiteStatus, CheckSuiteConclusion, error) { - csUrl := pr.GetCheckSuiteUrl() +func (gh *GithubClient) FilterCheckSuiteStatuses(checkSuites []CheckSuite) []CheckSuite { + filteredCheckSuites := []CheckSuite{} + + for _, cs := range checkSuites { + for _, target := range gh.AppTargets { + // Ignore auxiliary checks we don't control, e.g. Microsoft Policy Service. + // Github creates a check suite for each app with checks:write permissions, + // so also ignore any check suites with 0 check runs posted + // + // TODO: in the case where a check run isn't posted from azure pipelines due to invalid yaml, will this + // show up as 0 check runs? If so, how do we differentiate between the following so we don't submit a passing status: + // 1. Github Actions CI intended, Azure Pipelines CI NOT detected + // 2. Github Actions CI intended, Azure Pipelines CI intended, Azure Pipelines CI invalid yaml + if cs.App.Name == target && cs.LatestCheckRunCount > 0 { + filteredCheckSuites = append(filteredCheckSuites, cs) + } + } + } + + return filteredCheckSuites +} - target, err := gh.getUrl(csUrl) +func (gh *GithubClient) GetCheckSuiteStatuses(checkSuiteUrl string) ([]CheckSuite, error) { + target, err := gh.getUrl(checkSuiteUrl) if err != nil { - return "", "", err + return []CheckSuite{}, err } req, err := http.NewRequest("GET", target.String(), nil) if err != nil { - return "", "", err + return []CheckSuite{}, err } gh.setHeaders(req) @@ -120,21 +140,14 @@ func (gh *GithubClient) GetCheckSuiteStatus(pr PullRequest) (CheckSuiteStatus, C fmt.Println("GET to", target.String()) data, err := gh.request(req) if err != nil { - return "", "", err + return []CheckSuite{}, err } suites := CheckSuites{} if err = json.Unmarshal(data, &suites); err != nil { - return "", "", err - } - - for _, cs := range suites.CheckSuites { - if cs.App.Name != gh.AppTarget { - continue - } - return cs.Status, cs.Conclusion, nil + return []CheckSuite{}, err } - return "", "", nil + return gh.FilterCheckSuiteStatuses(suites.CheckSuites), nil } func (gh *GithubClient) CreateIssueComment(commentsUrl string, body string) error { diff --git a/main.go b/main.go index cd0f2e1..558b158 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,7 @@ import ( const GithubTokenKey = "GITHUB_TOKEN" const CommitStatusContext = "https://aka.ms/azsdk/checkenforcer" const AzurePipelinesAppName = "Azure Pipelines" +const GithubActionsAppName = "GitHub Actions" func newPendingBody() StatusBody { return StatusBody{ @@ -58,7 +59,7 @@ func main() { fmt.Println(fmt.Sprintf("WARNING: environment variable '%s' is not set", GithubTokenKey)) } - gh, err := NewGithubClient("https://api.github.com", github_token, AzurePipelinesAppName) + gh, err := NewGithubClient("https://api.github.com", github_token, AzurePipelinesAppName, GithubActionsAppName) handleError(err) err = handleEvent(gh, payload) @@ -73,19 +74,23 @@ func handleEvent(gh *GithubClient, payload []byte) error { fmt.Println() if ic := NewIssueCommentWebhook(payload); ic != nil { - fmt.Println("Handling issue comment event.") - err := handleComment(gh, ic) + err := handleIssueComment(gh, ic) handleError(err) return nil } if cs := NewCheckSuiteWebhook(payload); cs != nil { - fmt.Println("Handling check suite event.") err := handleCheckSuite(gh, cs) handleError(err) return nil } + if wr := NewWorkflowRunWebhook(payload); wr != nil { + err := handleWorkflowRun(gh, wr) + handleError(err) + return nil + } + return errors.New("Error: Invalid or unsupported payload body.") } @@ -143,7 +148,29 @@ func getCheckEnforcerCommand(comment string) string { } } -func handleComment(gh *GithubClient, ic *IssueCommentWebhook) error { +func setStatusForCheckSuiteConclusions(gh *GithubClient, checkSuites []CheckSuite, statusesUrl string) error { + successCount := 0 + + for _, suite := range checkSuites { + fmt.Println(fmt.Sprintf("Check suite conclusion for '%s' is '%s'.", suite.App.Name, suite.Conclusion)) + if IsCheckSuiteSucceeded(suite.Conclusion) { + successCount++ + } + } + + if successCount > 0 && successCount == len(checkSuites) { + return gh.SetStatus(statusesUrl, newSucceededBody()) + } + + // A pending status is redundant with the default status, but it allows us to + // add more details to the status check in the UI such as a link back to the + // check enforcer run that evaluated pending. + return gh.SetStatus(statusesUrl, newPendingBody()) +} + +func handleIssueComment(gh *GithubClient, ic *IssueCommentWebhook) error { + fmt.Println("Handling issue comment event.") + command := getCheckEnforcerCommand(ic.Comment.Body) if command == "" { @@ -159,25 +186,17 @@ func handleComment(gh *GithubClient, ic *IssueCommentWebhook) error { // request branch is from, which may be a fork. pr, err := gh.GetPullRequest(ic.GetPullsUrl()) handleError(err) - _, conclusion, err := gh.GetCheckSuiteStatus(pr) + checkSuites, err := gh.GetCheckSuiteStatuses(pr.GetCheckSuiteUrl()) handleError(err) - if IsCheckSuiteNoMatch(conclusion) { + if checkSuites == nil || len(checkSuites) == 0 { noPipelineText, err := ioutil.ReadFile("./comments/no_pipelines.txt") handleError(err) err = gh.CreateIssueComment(ic.GetCommentsUrl(), string(noPipelineText)) handleError(err) - err = gh.SetStatus(pr.StatusesUrl, newPendingBody()) - handleError(err) - } else if IsCheckSuiteSucceeded(conclusion) { - return gh.SetStatus(pr.StatusesUrl, newSucceededBody()) - } else if IsCheckSuiteFailed(conclusion) { - // Mark as pending with link to action run even on failure, to maintain - // consistency with the old check enforcer behavior and avoid confusion for now. - return gh.SetStatus(pr.StatusesUrl, newPendingBody()) - } else { - return gh.SetStatus(pr.StatusesUrl, newPendingBody()) } + + return setStatusForCheckSuiteConclusions(gh, checkSuites, pr.StatusesUrl) } else { helpText, err := ioutil.ReadFile("./comments/help.txt") handleError(err) @@ -189,27 +208,40 @@ func handleComment(gh *GithubClient, ic *IssueCommentWebhook) error { } func handleCheckSuite(gh *GithubClient, cs *CheckSuiteWebhook) error { - if cs.CheckSuite.App.Name != gh.AppTarget { - fmt.Println(fmt.Sprintf( - "Check Enforcer only handles check suites from the '%s' app. Found: '%s'", - gh.AppTarget, - cs.CheckSuite.App.Name)) - return nil - } else if cs.CheckSuite.HeadBranch == "main" { + fmt.Println("Handling check suite event.") + + if cs.CheckSuite.HeadBranch == "main" { fmt.Println("Skipping check suite for main branch.") return nil - } else if cs.IsSucceeded() { - return gh.SetStatus(cs.GetStatusesUrl(), newSucceededBody()) - } else if cs.IsFailed() { - // Mark as pending with link to action run even on failure, to maintain - // consistency with the old check enforcer behavior and avoid confusion for now. - return gh.SetStatus(cs.GetStatusesUrl(), newPendingBody()) + } + + if len(gh.AppTargets) > 1 { + checkSuites, err := gh.GetCheckSuiteStatuses(cs.GetCheckSuiteUrl()) + handleError(err) + return setStatusForCheckSuiteConclusions(gh, checkSuites, cs.GetStatusesUrl()) } else { - fmt.Println("Skipping check suite with conclusion: ", cs.CheckSuite.Conclusion) - return nil + checkSuites := gh.FilterCheckSuiteStatuses([]CheckSuite{cs.CheckSuite}) + return setStatusForCheckSuiteConclusions(gh, checkSuites, cs.GetStatusesUrl()) } } +func handleWorkflowRun(gh *GithubClient, webhook *WorkflowRunWebhook) error { + workflowRun := webhook.WorkflowRun + fmt.Println("Handling workflow run event.") + fmt.Println(fmt.Sprintf("Workflow run url: %s", workflowRun.HtmlUrl)) + fmt.Println(fmt.Sprintf("Workflow run commit: %s", workflowRun.HeadSha)) + + if workflowRun.Event != "pull_request" || workflowRun.PullRequests == nil || len(workflowRun.PullRequests) == 0 { + fmt.Println("Check enforcer only handles workflow_run events for pull requests. Skipping") + return gh.SetStatus(workflowRun.GetStatusesUrl(), newPendingBody()) + } + + checkSuites, err := gh.GetCheckSuiteStatuses(workflowRun.GetCheckSuiteUrl()) + handleError(err) + + return setStatusForCheckSuiteConclusions(gh, checkSuites, workflowRun.GetStatusesUrl()) +} + func help() { help := `Update pull request status checks based on github webhook events. diff --git a/main_test.go b/main_test.go index aab2581..ec3e354 100644 --- a/main_test.go +++ b/main_test.go @@ -12,17 +12,24 @@ import ( ) type Payloads struct { - CheckSuiteEvent []byte - IssueCommentEvent []byte - PullRequestResponse []byte - CheckSuiteResponse []byte - StatusResponse []byte - NewCommentResponse []byte + CheckSuiteEvent []byte + IssueCommentEvent []byte + WorkflowRunEvent []byte + PullRequestResponse []byte + CheckSuiteResponse []byte + MultipleCheckSuiteResponse []byte + MultipleWithEmptyCheckSuiteResponse []byte + SingleWithEmptyCheckSuiteResponse []byte + StatusResponse []byte + NewCommentResponse []byte + NoPipelinesComment []byte + HelpComment []byte } func getPayloads() (Payloads, error) { payloads := Payloads{} var err error + payloads.CheckSuiteEvent, err = ioutil.ReadFile("./testpayloads/check_suite_event.json") if err != nil { return Payloads{}, err @@ -31,6 +38,10 @@ func getPayloads() (Payloads, error) { if err != nil { return Payloads{}, err } + payloads.WorkflowRunEvent, err = ioutil.ReadFile("./testpayloads/workflow_run_event.json") + if err != nil { + return Payloads{}, err + } payloads.PullRequestResponse, err = ioutil.ReadFile("./testpayloads/pull_request_response.json") if err != nil { return Payloads{}, err @@ -39,6 +50,18 @@ func getPayloads() (Payloads, error) { if err != nil { return Payloads{}, err } + payloads.MultipleCheckSuiteResponse, err = ioutil.ReadFile("./testpayloads/multiple_check_suite_response.json") + if err != nil { + return Payloads{}, err + } + payloads.MultipleWithEmptyCheckSuiteResponse, err = ioutil.ReadFile("./testpayloads/multiple_with_empty_check_suite_response.json") + if err != nil { + return Payloads{}, err + } + payloads.SingleWithEmptyCheckSuiteResponse, err = ioutil.ReadFile("./testpayloads/single_with_empty_check_suite_response.json") + if err != nil { + return Payloads{}, err + } payloads.StatusResponse, err = ioutil.ReadFile("./testpayloads/status_response.json") if err != nil { return Payloads{}, err @@ -47,150 +70,309 @@ func getPayloads() (Payloads, error) { if err != nil { return Payloads{}, err } + payloads.NoPipelinesComment, err = ioutil.ReadFile("./comments/no_pipelines.txt") + if err != nil { + return Payloads{}, err + } + payloads.HelpComment, err = ioutil.ReadFile("./comments/help.txt") + if err != nil { + return Payloads{}, err + } + return payloads, nil } -func TestCheckSuite(t *testing.T) { - payloads, err := getPayloads() - assert.NoError(t, err) - cs := NewCheckSuiteWebhook(payloads.CheckSuiteEvent) - assert.NotEmpty(t, cs) +func getStatusBody(assert *assert.Assertions, req *http.Request) StatusBody { + body, err := ioutil.ReadAll(req.Body) + assert.NoError(err) + status := StatusBody{} + assert.NoError(json.Unmarshal(body, &status)) + return status +} - postedStatus := false +type TestCheckSuiteCase struct { + Description string + AppTargets []string + InjectConclusion1 CheckSuiteConclusion + InjectConclusion2 CheckSuiteConclusion + ShouldPostStatus bool + ExpectedState CommitState + Event []byte +} + +func NewCheckSuiteTestServer( + assert *assert.Assertions, + payloads Payloads, + conclusion1 CheckSuiteConclusion, + conclusion2 CheckSuiteConclusion, + postedState *CommitState, + postedStatus *bool, + description string, +) *httptest.Server { + checkSuite := NewCheckSuiteWebhook(payloads.CheckSuiteEvent) + assert.NotEmpty(checkSuite) fn := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - assert.Contains(t, cs.GetStatusesUrl(), req.URL.String()) - assert.Contains(t, req.URL.Path, cs.CheckSuite.HeadSha) + response := []byte{} - assert.Equal(t, "POST", req.Method) - status := getStatusBody(t, req) - assert.Equal(t, status.State, CommitStateSuccess) - postedStatus = true + if strings.Contains(checkSuite.GetCheckSuiteUrl(), req.URL.String()) && req.Method == "GET" { + assert.Contains(req.URL.Path, checkSuite.CheckSuite.HeadSha, description) + response = payloads.MultipleCheckSuiteResponse + response = []byte(strings.Replace(string(response), + `"conclusion": "neutral"`, fmt.Sprintf("\"conclusion\": \"%s\"", conclusion1), 1)) + if conclusion2 != "" { + response = []byte(strings.Replace(string(response), + `"conclusion": "neutral"`, fmt.Sprintf("\"conclusion\": \"%s\"", conclusion2), 1)) + } + } else if strings.Contains(checkSuite.GetStatusesUrl(), req.URL.String()) && req.Method == "POST" { + assert.Contains(req.URL.Path, checkSuite.CheckSuite.HeadSha) + status := getStatusBody(assert, req) + *postedState = status.State + *postedStatus = true + response = payloads.StatusResponse + } else { + assert.Fail("%s: Unexpected %s request to '%s'", description, req.Method, req.URL.String()) + } - w.Write(payloads.StatusResponse) + w.Write(response) }) - server := httptest.NewServer(fn) - defer server.Close() - - gh, err := NewGithubClient(server.URL, "", "octocoders-linter") - assert.NoError(t, err) - - err = handleEvent(gh, payloads.CheckSuiteEvent) - assert.NoError(t, err) - assert.True(t, postedStatus, "Should POST status") - - // Test skip check suite events for main branch - replaced := strings.ReplaceAll(string(payloads.CheckSuiteEvent), `"head_branch": "changes"`, `"head_branch": "main"`) - postedStatus = false - err = handleEvent(gh, []byte(replaced)) - assert.NoError(t, err) - assert.False(t, postedStatus, "Should POST status") + + return httptest.NewServer(fn) +} + +func TestCheckSuite(t *testing.T) { + assert := assert.New(t) + payloads, err := getPayloads() + assert.NoError(err) + var zeroCommitState CommitState + singleAppTarget := []string{"octocoders-linter"} + multiAppTarget := []string{"Octocat App", "Hexacat App"} + noMatchAppTarget := []string{"no-match"} + servers := []*httptest.Server{} + + for i, tc := range []TestCheckSuiteCase{ + {"POST pending for single suite unfinished", singleAppTarget, "", "", true, CommitStatePending, + []byte(strings.ReplaceAll(string(payloads.CheckSuiteEvent), `"conclusion": "success"`, fmt.Sprintf("\"conclusion\": \"%s\"", CommitStatePending)))}, + {"POST pending for single suite failure", singleAppTarget, "", "", true, CommitStatePending, + []byte(strings.ReplaceAll(string(payloads.CheckSuiteEvent), `"conclusion": "success"`, fmt.Sprintf("\"conclusion\": \"%s\"", CommitStateFailure)))}, + {"POST success for single suite", singleAppTarget, "", "", true, CommitStateSuccess, payloads.CheckSuiteEvent}, + {"POST pending for no match, single suite", noMatchAppTarget, "", "", true, CommitStatePending, payloads.CheckSuiteEvent}, + {"POST pending for multiple suites pending", multiAppTarget, CheckSuiteConclusionSuccess, CheckSuiteConclusionEmpty, + true, CommitStatePending, payloads.CheckSuiteEvent}, + {"POST pending for multiple suites pending 2", multiAppTarget, CheckSuiteConclusionEmpty, CheckSuiteConclusionSuccess, + true, CommitStatePending, payloads.CheckSuiteEvent}, + {"POST pending for multiple suites failure", multiAppTarget, CheckSuiteConclusionSuccess, CheckSuiteConclusionFailure, + true, CommitStatePending, payloads.CheckSuiteEvent}, + {"POST success for multiple suites", multiAppTarget, CheckSuiteConclusionSuccess, CheckSuiteConclusionSuccess, + true, CommitStateSuccess, payloads.CheckSuiteEvent}, + {"skip for main branch", singleAppTarget, CheckSuiteConclusionSuccess, "", false, zeroCommitState, + []byte(strings.ReplaceAll(string(payloads.CheckSuiteEvent), `"head_branch": "changes"`, `"head_branch": "main"`))}, + } { + var postedStatus bool + var postedState CommitState + server := NewCheckSuiteTestServer(assert, payloads, tc.InjectConclusion1, tc.InjectConclusion2, &postedState, &postedStatus, tc.Description) + gh, err := NewGithubClient(server.URL, "", tc.AppTargets...) + assert.NoError(err, tc.Description) + servers = append(servers, server) + defer servers[i].Close() + fmt.Println(fmt.Sprintf("\n\n========= %s =========", tc.Description)) + err = handleEvent(gh, tc.Event) + assert.NoError(err, tc.Description) + assert.Equal(tc.ShouldPostStatus, postedStatus, tc.Description) + assert.Equal(tc.ExpectedState, postedState, tc.Description) + } } -type testCommentCaseConfig struct { +type TestCommentCase struct { + Description string InputComment string InjectConclusion CheckSuiteConclusion ExpectedState CommitState ShouldPostStatus bool ShouldPostComment bool + ExpectedComment string + AppTargets []string +} + +func NewCommentTestServer( + assert *assert.Assertions, + payloads Payloads, + inputComment string, + injectConclusion CheckSuiteConclusion, + expectedState CommitState, + postedStatus *bool, + postedComment *bool, + expectedComment string, + description string, +) *httptest.Server { + issueCommentEvent := NewIssueCommentWebhook(payloads.IssueCommentEvent) + assert.NotEmpty(issueCommentEvent) + pullRequestResponse := NewPullRequest(payloads.PullRequestResponse) + assert.NotEmpty(pullRequestResponse) + + fn := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + response := []byte{} + if strings.Contains(issueCommentEvent.GetPullsUrl(), req.URL.String()) && req.Method == "GET" { + response = payloads.PullRequestResponse + } else if strings.Contains(pullRequestResponse.GetCheckSuiteUrl(), req.URL.String()) && req.Method == "GET" { + response = []byte(strings.ReplaceAll( + string(payloads.CheckSuiteResponse), + `"conclusion": "neutral"`, + fmt.Sprintf("\"conclusion\": \"%s\"", injectConclusion))) + } else if strings.Contains(pullRequestResponse.StatusesUrl, req.URL.String()) && req.Method == "POST" { + *postedStatus = true + response = payloads.StatusResponse + status := getStatusBody(assert, req) + assert.Equal(expectedState, status.State, description) + } else if strings.Contains(issueCommentEvent.GetCommentsUrl(), req.URL.String()) && req.Method == "POST" { + *postedComment = true + response = payloads.NewCommentResponse + body, err := ioutil.ReadAll(req.Body) + assert.NoError(err, description) + assert.Equal(expectedComment, string(body), "%s: Comment body for command '%s'", description, inputComment) + } else { + assert.Fail("%s: Unexpected %s request to '%s'", description, req.Method, req.URL.String()) + } + + w.Write(response) + }) + + return httptest.NewServer(fn) } func TestComments(t *testing.T) { + assert := assert.New(t) payloads, err := getPayloads() - assert.NoError(t, err) + assert.NoError(err) issueCommentEvent := NewIssueCommentWebhook(payloads.IssueCommentEvent) - assert.NotEmpty(t, issueCommentEvent) + assert.NotEmpty(issueCommentEvent) pullRequestResponse := NewPullRequest(payloads.PullRequestResponse) - assert.NotEmpty(t, pullRequestResponse) - - cases := []testCommentCaseConfig{ - {"/check-enforcer override", CheckSuiteConclusionSuccess, CommitStateSuccess, true, false}, - {"/check-enforcer override", CheckSuiteConclusionFailure, CommitStateSuccess, true, false}, - {" /check-enforcer override ", CheckSuiteConclusionFailure, CommitStateSuccess, true, false}, - {"/check-enforcer reset", CheckSuiteConclusionSuccess, CommitStateSuccess, true, false}, - {"/check-enforcer reset", CheckSuiteConclusionFailure, CommitStatePending, true, false}, - {"/check-enforcer evaluate", CheckSuiteConclusionSuccess, CommitStateSuccess, true, false}, - {"/check-enforcer evaluate", CheckSuiteConclusionFailure, CommitStatePending, true, false}, - {"/check-enforcer evaluate", CheckSuiteConclusionTimedOut, CommitStatePending, true, false}, - {"/check-enforcer evaluate", CheckSuiteConclusionNeutral, CommitStatePending, true, false}, - {"/check-enforcer evaluate", CheckSuiteConclusionStale, CommitStatePending, true, false}, - {"/check-enforcer evaluate", "", CommitStatePending, true, true}, - {"/check-enforcer reset", "", CommitStatePending, true, true}, - {"/check-enforcer help", "", "", false, true}, - {"/check-enforcerevaluate", "", "", false, true}, - {"/check-enforcer foobar", "", "", false, true}, - {"/check-enforcer foobar bar bar", "", "", false, true}, - {"/azp run", "", "", false, false}, - {";;;;;;;;;;;;;;;;;;;;;;;;;;;", "", "", false, false}, - } - for _, tc := range cases { - testCommentCase(t, tc, payloads, issueCommentEvent, pullRequestResponse) + assert.NotEmpty(pullRequestResponse) + noPipelinesComment, err := NewIssueCommentBody(string(payloads.NoPipelinesComment)) + assert.NoError(err) + helpComment, err := NewIssueCommentBody(string(payloads.HelpComment)) + assert.NoError(err) + + servers := []*httptest.Server{} + apps := []string{"Octocat App"} + noMatchAppTarget := []string{"no-match"} + + cases := []TestCommentCase{ + {"override+success", "/check-enforcer override", CheckSuiteConclusionSuccess, CommitStateSuccess, true, false, "", apps}, + {"override+failure", "/check-enforcer override", CheckSuiteConclusionFailure, CommitStateSuccess, true, false, "", apps}, + {"comment spaces", " /check-enforcer override ", CheckSuiteConclusionFailure, CommitStateSuccess, true, false, "", apps}, + {"reset+success", "/check-enforcer reset", CheckSuiteConclusionSuccess, CommitStateSuccess, true, false, "", apps}, + {"reset+failure", "/check-enforcer reset", CheckSuiteConclusionFailure, CommitStatePending, true, false, "", apps}, + {"evaluate+success", "/check-enforcer evaluate", CheckSuiteConclusionSuccess, CommitStateSuccess, true, false, "", apps}, + {"evaluate+failure", "/check-enforcer evaluate", CheckSuiteConclusionFailure, CommitStatePending, true, false, "", apps}, + {"evaluate+timeout", "/check-enforcer evaluate", CheckSuiteConclusionTimedOut, CommitStatePending, true, false, "", apps}, + {"evaluate+neutral", "/check-enforcer evaluate", CheckSuiteConclusionNeutral, CommitStatePending, true, false, "", apps}, + {"evaluate+stale", "/check-enforcer evaluate", CheckSuiteConclusionStale, CommitStatePending, true, false, "", apps}, + {"evaluate+nopipelinematches", "/check-enforcer evaluate", CheckSuiteConclusionSuccess, CommitStatePending, true, true, string(noPipelinesComment), noMatchAppTarget}, + {"reset+nopipelinematches", "/check-enforcer reset", CheckSuiteConclusionSuccess, CommitStatePending, true, true, string(noPipelinesComment), noMatchAppTarget}, + {"help", "/check-enforcer help", "", "", false, true, string(helpComment), apps}, + {"missing space", "/check-enforcerevaluate", "", "", false, true, string(helpComment), apps}, + {"invalid command", "/check-enforcer foobar", "", "", false, true, string(helpComment), apps}, + {"invalid command+args", "/check-enforcer foobar bar bar", "", "", false, true, string(helpComment), apps}, + {"different command", "/azp run", "", "", false, false, "", apps}, + {"semicolons", ";;;;;;;;;;;;;;;;;;;;;;;;;;;", "", "", false, false, "", apps}, + } + + for i, tc := range cases { + var postedStatus bool + var postedComment bool + + server := NewCommentTestServer(assert, payloads, tc.InputComment, tc.InjectConclusion, tc.ExpectedState, + &postedStatus, &postedComment, tc.ExpectedComment, tc.Description) + servers = append(servers, server) + defer servers[i].Close() + + gh, err := NewGithubClient(server.URL, "", tc.AppTargets...) + assert.NoError(err, tc.Description) + + replaced := strings.ReplaceAll(string(payloads.IssueCommentEvent), "You are totally right! I'll get this fixed right away.", tc.InputComment) + + err = handleEvent(gh, []byte(replaced)) + assert.NoError(err) + assert.Equal(tc.ShouldPostStatus, postedStatus, "%s: Should POST status for command '%s'", tc.Description, tc.InputComment) + assert.Equal(tc.ShouldPostComment, postedComment, "%s: Should POST comment for command '%s'", tc.Description, tc.InputComment) } } -func testCommentCase(t *testing.T, tc testCommentCaseConfig, payloads Payloads, issueCommentEvent *IssueCommentWebhook, pullRequestResponse *PullRequest) { - postedStatus := false - postedComment := false +type WorkflowRunCase struct { + Description string + Event []byte + CheckSuiteResponse []byte + ExpectedState CommitState + AppTargets []string +} - csResponse := strings.ReplaceAll( - string(payloads.CheckSuiteResponse), - `"conclusion": "neutral"`, - fmt.Sprintf("\"conclusion\": \"%s\"", tc.InjectConclusion)) +func NewWorkflowRunTestServer( + assert *assert.Assertions, + payloads Payloads, + checkSuiteResponse []byte, + postedState *CommitState, + description string, +) *httptest.Server { + workflowRun := NewWorkflowRunWebhook(payloads.WorkflowRunEvent) + assert.NotEmpty(workflowRun) fn := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { response := []byte{} - if strings.Contains(issueCommentEvent.GetPullsUrl(), req.URL.String()) { - response = payloads.PullRequestResponse - } else if strings.Contains(pullRequestResponse.GetCheckSuiteUrl(), req.URL.String()) { - response = []byte(csResponse) - } else if strings.Contains(pullRequestResponse.StatusesUrl, req.URL.String()) { + + if strings.Contains(workflowRun.WorkflowRun.GetCheckSuiteUrl(), req.URL.String()) && req.Method == "GET" { + response = checkSuiteResponse + } else if strings.Contains(workflowRun.WorkflowRun.GetStatusesUrl(), req.URL.String()) && req.Method == "POST" { + assert.Contains(req.URL.Path, workflowRun.WorkflowRun.HeadSha) + status := getStatusBody(assert, req) + *postedState = status.State response = payloads.StatusResponse - assert.Equal(t, "POST", req.Method, "Post new status") - status := getStatusBody(t, req) - assert.Equal(t, tc.ExpectedState, status.State, "TestCase %s", tc.InputComment) - postedStatus = true - } else if strings.Contains(issueCommentEvent.GetCommentsUrl(), req.URL.String()) { - response = payloads.NewCommentResponse - assert.Equal(t, "POST", req.Method, fmt.Sprintf("POST new comment for command '%s'", tc.InputComment)) - body, err := ioutil.ReadAll(req.Body) - assert.NoError(t, err) - if tc.InputComment == "/check-enforcer evaluate" || tc.InputComment == "/check-enforcer reset" { - noPipelineText, err := ioutil.ReadFile("./comments/no_pipelines.txt") - assert.NoError(t, err) - expectedBody, err := NewIssueCommentBody(string(noPipelineText)) - assert.NoError(t, err) - assert.Equal(t, string(expectedBody), string(body), fmt.Sprintf("Comment body for command '%s'", tc.InputComment)) - } else { - helpText, err := ioutil.ReadFile("./comments/help.txt") - assert.NoError(t, err) - expectedBody, err := NewIssueCommentBody(string(helpText)) - assert.NoError(t, err) - assert.Equal(t, string(expectedBody), string(body), fmt.Sprintf("Comment body for command '%s'", tc.InputComment)) - } - postedComment = true } else { - assert.Fail(t, "Unexpected request to "+req.URL.String()) + assert.Fail("%s: Unexpected %s request to '%s'", description, req.Method, req.URL.String()) } + w.Write(response) }) - server := httptest.NewServer(fn) - defer server.Close() - gh, err := NewGithubClient(server.URL, "", "Octocat App") - assert.NoError(t, err) + return httptest.NewServer(fn) +} - replaced := strings.ReplaceAll(string(payloads.IssueCommentEvent), "You are totally right! I'll get this fixed right away.", tc.InputComment) +func TestWorkflowRun(t *testing.T) { + assert := assert.New(t) + payloads, err := getPayloads() + assert.NoError(err) + servers := []*httptest.Server{} - err = handleEvent(gh, []byte(replaced)) - assert.NoError(t, err) - assert.Equal(t, tc.ShouldPostStatus, postedStatus, fmt.Sprintf("Should POST status for command '%s'", tc.InputComment)) - assert.Equal(t, tc.ShouldPostComment, postedComment, fmt.Sprintf("Should POST comment for command '%s'", tc.InputComment)) -} + singleCheckSuiteResponse := []byte(strings.ReplaceAll( + string(payloads.CheckSuiteResponse), `"conclusion": "neutral"`, fmt.Sprintf("\"conclusion\": \"%s\"", CommitStateSuccess))) + multipleCheckSuiteResponse := []byte(strings.ReplaceAll(string(payloads.MultipleCheckSuiteResponse), + `"conclusion": "neutral"`, fmt.Sprintf("\"conclusion\": \"%s\"", CommitStateSuccess))) + multipleCheckSuiteResponsePending := []byte(strings.Replace(string(multipleCheckSuiteResponse), + fmt.Sprintf("\"conclusion\": \"%s\"", CommitStateSuccess), fmt.Sprintf("\"conclusion\": \"%s\"", CommitStatePending), 1)) -func getStatusBody(t *testing.T, req *http.Request) StatusBody { - body, err := ioutil.ReadAll(req.Body) - assert.NoError(t, err) - status := StatusBody{} - assert.NoError(t, json.Unmarshal(body, &status)) - return status + for i, tc := range []WorkflowRunCase{ + {"Workflow run success", payloads.WorkflowRunEvent, singleCheckSuiteResponse, CommitStateSuccess, []string{"Octocat App"}}, + {"Workflow run multiple success", payloads.WorkflowRunEvent, multipleCheckSuiteResponse, CommitStateSuccess, []string{"Octocat App"}}, + {"Workflow run multiple pending", payloads.WorkflowRunEvent, multipleCheckSuiteResponsePending, CommitStatePending, []string{"Octocat App"}}, + {"Workflow run with empty check run suite", payloads.WorkflowRunEvent, payloads.MultipleWithEmptyCheckSuiteResponse, + CommitStateSuccess, []string{"GitHub Actions", "Azure Pipelines"}}, + {"Workflow run single pending", payloads.WorkflowRunEvent, payloads.SingleWithEmptyCheckSuiteResponse, + CommitStatePending, []string{"Azure Pipelines"}}, + {"Workflow run push event", + []byte(strings.ReplaceAll(string(payloads.WorkflowRunEvent), `"event": "pull_request"`, `"event": "push"`)), + singleCheckSuiteResponse, CommitStatePending, []string{"Octocat App"}}, + } { + var postedState CommitState + + server := NewWorkflowRunTestServer(assert, payloads, tc.CheckSuiteResponse, &postedState, tc.Description) + gh, err := NewGithubClient(server.URL, "", tc.AppTargets...) + assert.NoError(err) + servers = append(servers, server) + defer servers[i].Close() + + err = handleEvent(gh, tc.Event) + assert.NoError(err) + + assert.Equal(tc.ExpectedState, postedState, tc.Description) + } } diff --git a/testpayloads/multiple_check_suite_response.json b/testpayloads/multiple_check_suite_response.json new file mode 100644 index 0000000..61154a7 --- /dev/null +++ b/testpayloads/multiple_check_suite_response.json @@ -0,0 +1,361 @@ +{ + "total_count": 2, + "check_suites": [ + { + "id": 5, + "node_id": "MDEwOkNoZWNrU3VpdGU1", + "head_branch": "master", + "head_sha": "d6fde92930d4715a2b49857d24b940956b26d2d3", + "status": "completed", + "conclusion": "neutral", + "url": "https://api.github.com/repos/github/hello-world/check-suites/5", + "before": "146e867f55c26428e5f9fade55a9bbf5e95a7912", + "after": "d6fde92930d4715a2b49857d24b940956b26d2d3", + "pull_requests": [], + "app": { + "id": 1, + "slug": "octoapp", + "node_id": "MDExOkludGVncmF0aW9uMQ==", + "owner": { + "login": "github", + "id": 1, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE=", + "url": "https://api.github.com/orgs/github", + "repos_url": "https://api.github.com/orgs/github/repos", + "events_url": "https://api.github.com/orgs/github/events", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": true + }, + "name": "Octocat App", + "description": "", + "external_url": "https://example.com", + "html_url": "https://github.com/apps/octoapp", + "created_at": "2017-07-08T16:18:44-04:00", + "updated_at": "2017-07-08T16:18:44-04:00", + "permissions": { + "metadata": "read", + "contents": "read", + "issues": "write", + "single_file": "write" + }, + "events": [ + "push", + "pull_request" + ] + }, + "repository": { + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World", + "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}", + "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}", + "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}", + "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}", + "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors", + "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments", + "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads", + "events_url": "https://api.github.com/repos/octocat/Hello-World/events", + "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks", + "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World.git", + "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}", + "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}", + "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}", + "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}", + "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages", + "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges", + "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}", + "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription", + "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags", + "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams", + "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World.git", + "mirror_url": "git:git.example.com/octocat/Hello-World", + "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "homepage": "https://github.com", + "language": null, + "forks_count": 9, + "stargazers_count": 80, + "watchers_count": 80, + "size": 108, + "default_branch": "master", + "open_issues_count": 0, + "is_template": true, + "topics": [ + "octocat", + "atom", + "electron", + "api" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "disabled": false, + "visibility": "public", + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "admin": false, + "push": false, + "pull": true + }, + "temp_clone_token": "ABTLWHOULUVAXGTRYU7OC2876QJ2O", + "delete_branch_on_merge": true, + "subscribers_count": 42, + "network_count": 0 + }, + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "head_commit": { + "id": "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", + "tree_id": "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", + "message": "Merge pull request #6 from Spaceghost/patch-1\n\nNew line at end of file.", + "timestamp": "2016-10-10T00:00:00Z", + "author": { + "name": "The Octocat", + "email": "octocat@nowhere.com" + }, + "committer": { + "name": "The Octocat", + "email": "octocat@nowhere.com" + } + }, + "latest_check_runs_count": 1, + "check_runs_url": "https://api.github.com/repos/octocat/Hello-World/check-suites/5/check-runs" + }, + { + "id": 5, + "node_id": "MDEwOkNoZWNrU3VpdGU1", + "head_branch": "master", + "head_sha": "d6fde92930d4715a2b49857d24b940956b26d2d3", + "status": "completed", + "conclusion": "neutral", + "url": "https://api.github.com/repos/github/hello-world/check-suites/5", + "before": "146e867f55c26428e5f9fade55a9bbf5e95a7912", + "after": "d6fde92930d4715a2b49857d24b940956b26d2d3", + "pull_requests": [], + "app": { + "id": 1, + "slug": "hexaapp", + "node_id": "MDExOkludGVncmF0aW9uMQ==", + "owner": { + "login": "github", + "id": 1, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjE=", + "url": "https://api.github.com/orgs/github", + "repos_url": "https://api.github.com/orgs/github/repos", + "events_url": "https://api.github.com/orgs/github/events", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": true + }, + "name": "Hexacat App", + "description": "", + "external_url": "https://example.com", + "html_url": "https://github.com/apps/octoapp", + "created_at": "2017-07-08T16:18:44-04:00", + "updated_at": "2017-07-08T16:18:44-04:00", + "permissions": { + "metadata": "read", + "contents": "read", + "issues": "write", + "single_file": "write" + }, + "events": [ + "push", + "pull_request" + ] + }, + "repository": { + "id": 1296269, + "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5", + "name": "Hello-World", + "full_name": "octocat/Hello-World", + "owner": { + "login": "octocat", + "id": 1, + "node_id": "MDQ6VXNlcjE=", + "avatar_url": "https://github.com/images/error/octocat_happy.gif", + "gravatar_id": "", + "url": "https://api.github.com/users/octocat", + "html_url": "https://github.com/octocat", + "followers_url": "https://api.github.com/users/octocat/followers", + "following_url": "https://api.github.com/users/octocat/following{/other_user}", + "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}", + "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/octocat/subscriptions", + "organizations_url": "https://api.github.com/users/octocat/orgs", + "repos_url": "https://api.github.com/users/octocat/repos", + "events_url": "https://api.github.com/users/octocat/events{/privacy}", + "received_events_url": "https://api.github.com/users/octocat/received_events", + "type": "User", + "site_admin": false + }, + "private": false, + "html_url": "https://github.com/octocat/Hello-World", + "description": "This your first repo!", + "fork": false, + "url": "https://api.github.com/repos/octocat/Hello-World", + "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}", + "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}", + "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}", + "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}", + "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors", + "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments", + "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads", + "events_url": "https://api.github.com/repos/octocat/Hello-World/events", + "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks", + "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}", + "git_url": "git:github.com/octocat/Hello-World.git", + "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}", + "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}", + "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}", + "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}", + "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages", + "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges", + "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}", + "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}", + "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}", + "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}", + "ssh_url": "git@github.com:octocat/Hello-World.git", + "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers", + "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers", + "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription", + "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags", + "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams", + "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}", + "clone_url": "https://github.com/octocat/Hello-World.git", + "mirror_url": "git:git.example.com/octocat/Hello-World", + "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks", + "svn_url": "https://svn.github.com/octocat/Hello-World", + "homepage": "https://github.com", + "language": null, + "forks_count": 9, + "stargazers_count": 80, + "watchers_count": 80, + "size": 108, + "default_branch": "master", + "open_issues_count": 0, + "is_template": true, + "topics": [ + "octocat", + "atom", + "electron", + "api" + ], + "has_issues": true, + "has_projects": true, + "has_wiki": true, + "has_pages": false, + "has_downloads": true, + "archived": false, + "disabled": false, + "visibility": "public", + "pushed_at": "2011-01-26T19:06:43Z", + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "permissions": { + "admin": false, + "push": false, + "pull": true + }, + "temp_clone_token": "ABTLWHOULUVAXGTRYU7OC2876QJ2O", + "delete_branch_on_merge": true, + "subscribers_count": 42, + "network_count": 0 + }, + "created_at": "2011-01-26T19:01:12Z", + "updated_at": "2011-01-26T19:14:43Z", + "head_commit": { + "id": "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", + "tree_id": "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d", + "message": "Merge pull request #6 from Spaceghost/patch-1\n\nNew line at end of file.", + "timestamp": "2016-10-10T00:00:00Z", + "author": { + "name": "The Octocat", + "email": "octocat@nowhere.com" + }, + "committer": { + "name": "The Octocat", + "email": "octocat@nowhere.com" + } + }, + "latest_check_runs_count": 1, + "check_runs_url": "https://api.github.com/repos/octocat/Hello-World/check-suites/5/check-runs" + } + ] +} diff --git a/testpayloads/multiple_with_empty_check_suite_response.json b/testpayloads/multiple_with_empty_check_suite_response.json new file mode 100644 index 0000000..8e2fd87 --- /dev/null +++ b/testpayloads/multiple_with_empty_check_suite_response.json @@ -0,0 +1,385 @@ +{ + "total_count": 2, + "check_suites": [ + { + "id": 10092247692, + "node_id": "CS_kwDOFqc1sc8AAAACWYt6jA", + "head_branch": "benbp/ci-pr", + "head_sha": "1f3db168a3ae9d8aad1b7e65184565efb3018713", + "status": "queued", + "conclusion": null, + "url": "https://api.github.com/repos/benbp/azure-sdk-tools/check-suites/10092247692", + "before": "b22924091189779aeca71beec21d79f9d79dc6a2", + "after": "1f3db168a3ae9d8aad1b7e65184565efb3018713", + "pull_requests": [ + { + "url": "https://api.github.com/repos/benbp/azure-sdk-tools/pulls/26", + "id": 1174568198, + "number": 26, + "head": { + "ref": "benbp/ci-pr", + "sha": "1f3db168a3ae9d8aad1b7e65184565efb3018713", + "repo": { + "id": 380057009, + "url": "https://api.github.com/repos/benbp/azure-sdk-tools", + "name": "azure-sdk-tools" + } + }, + "base": { + "ref": "main", + "sha": "050e02490337f660730af4edbdf5a8b23d34759f", + "repo": { + "id": 380057009, + "url": "https://api.github.com/repos/benbp/azure-sdk-tools", + "name": "azure-sdk-tools" + } + } + } + ], + "app": { + "id": 9426, + "slug": "azure-pipelines", + "node_id": "MDM6QXBwOTQyNg==", + "owner": { + "login": "AzurePipelines", + "id": 39924718, + "node_id": "MDQ6VXNlcjM5OTI0NzE4", + "avatar_url": "https://avatars.githubusercontent.com/u/39924718?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/AzurePipelines", + "html_url": "https://github.com/AzurePipelines", + "followers_url": "https://api.github.com/users/AzurePipelines/followers", + "following_url": "https://api.github.com/users/AzurePipelines/following{/other_user}", + "gists_url": "https://api.github.com/users/AzurePipelines/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AzurePipelines/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AzurePipelines/subscriptions", + "organizations_url": "https://api.github.com/users/AzurePipelines/orgs", + "repos_url": "https://api.github.com/users/AzurePipelines/repos", + "events_url": "https://api.github.com/users/AzurePipelines/events{/privacy}", + "received_events_url": "https://api.github.com/users/AzurePipelines/received_events", + "type": "User", + "site_admin": false + }, + "name": "Azure Pipelines", + "description": "### Continuously build, test, and deploy to any platform and cloud\r\n\r\n[Azure Pipelines](https://azure.microsoft.com/services/devops/pipelines/) offers cloud-hosted pipelines for Linux, macOS, and Windows with 10 free parallel jobs and unlimited minutes for open source projects.\r\n\r\n:v: **Any language, platform, and cloud**\r\nBuild, test, and deploy Node.js, Python, 
Java, PHP, Ruby, Go, C/C++, C#, Android, and iOS apps. Run in parallel on Linux, macOS, and Windows. Deploy to cloud providers like Azure, AWS, and GCP. Distribute mobile apps through beta channels and app stores.\r\n\r\n:whale2: **Native container support**\r\nCreate new containers with ease and push them to any registry. Deploy containers to independent hosts or Kubernetes.\r\n\r\n:muscle: **Advanced workflows and features**\r\nEasy build chaining and multi-phased builds. Support for YAML, test integration, release gates, reporting, and more.\r\n\r\n:wrench: **Extensible**\r\nUse a range of build, test, and deployment tasks built by the community – hundreds of extensions from Slack to SonarCloud. You can even deploy from other CI systems, like Jenkins. Webhooks and REST APIs help you integrate.\r\n\r\n:blue_heart: **Free, to you from Azure Pipelines**\r\nFree cloud-hosted builds for public and private repositories.\r\n", + "external_url": "https://azure.microsoft.com/services/devops/pipelines/", + "html_url": "https://github.com/apps/azure-pipelines", + "created_at": "2018-02-23T17:14:32Z", + "updated_at": "2018-10-25T18:51:25Z", + "permissions": { + "checks": "write", + "contents": "write", + "deployments": "write", + "issues": "write", + "metadata": "read", + "pull_requests": "write", + "statuses": "write" + }, + "events": [ + "check_suite", + "create", + "issue_comment", + "pull_request", + "push", + "release", + "repository" + ] + }, + "created_at": "2022-12-28T23:52:49Z", + "updated_at": "2022-12-28T23:52:49Z", + "rerequestable": true, + "runs_rerequestable": true, + "latest_check_runs_count": 0, + "check_runs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/check-suites/10092247692/check-runs", + "head_commit": { + "id": "1f3db168a3ae9d8aad1b7e65184565efb3018713", + "tree_id": "d5c30619027c5dd2b2b7f86d452e46bf13353add", + "message": "9", + "timestamp": "2022-12-28T23:52:47Z", + "author": { + "name": "Ben Broderick Phillips", + "email": "bebroder@microsoft.com" + }, + "committer": { + "name": "Ben Broderick Phillips", + "email": "bebroder@microsoft.com" + } + }, + "repository": { + "id": 380057009, + "node_id": "MDEwOlJlcG9zaXRvcnkzODAwNTcwMDk=", + "name": "azure-sdk-tools", + "full_name": "benbp/azure-sdk-tools", + "private": false, + "owner": { + "login": "benbp", + "id": 1020379, + "node_id": "MDQ6VXNlcjEwMjAzNzk=", + "avatar_url": "https://avatars.githubusercontent.com/u/1020379?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/benbp", + "html_url": "https://github.com/benbp", + "followers_url": "https://api.github.com/users/benbp/followers", + "following_url": "https://api.github.com/users/benbp/following{/other_user}", + "gists_url": "https://api.github.com/users/benbp/gists{/gist_id}", + "starred_url": "https://api.github.com/users/benbp/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/benbp/subscriptions", + "organizations_url": "https://api.github.com/users/benbp/orgs", + "repos_url": "https://api.github.com/users/benbp/repos", + "events_url": "https://api.github.com/users/benbp/events{/privacy}", + "received_events_url": "https://api.github.com/users/benbp/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/benbp/azure-sdk-tools", + "description": "Tools repository leveraged by the Azure SDK team.", + "fork": true, + "url": "https://api.github.com/repos/benbp/azure-sdk-tools", + "forks_url": "https://api.github.com/repos/benbp/azure-sdk-tools/forks", + "keys_url": "https://api.github.com/repos/benbp/azure-sdk-tools/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/benbp/azure-sdk-tools/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/benbp/azure-sdk-tools/teams", + "hooks_url": "https://api.github.com/repos/benbp/azure-sdk-tools/hooks", + "issue_events_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues/events{/number}", + "events_url": "https://api.github.com/repos/benbp/azure-sdk-tools/events", + "assignees_url": "https://api.github.com/repos/benbp/azure-sdk-tools/assignees{/user}", + "branches_url": "https://api.github.com/repos/benbp/azure-sdk-tools/branches{/branch}", + "tags_url": "https://api.github.com/repos/benbp/azure-sdk-tools/tags", + "blobs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/benbp/azure-sdk-tools/statuses/{sha}", + "languages_url": "https://api.github.com/repos/benbp/azure-sdk-tools/languages", + "stargazers_url": "https://api.github.com/repos/benbp/azure-sdk-tools/stargazers", + "contributors_url": "https://api.github.com/repos/benbp/azure-sdk-tools/contributors", + "subscribers_url": "https://api.github.com/repos/benbp/azure-sdk-tools/subscribers", + "subscription_url": "https://api.github.com/repos/benbp/azure-sdk-tools/subscription", + "commits_url": "https://api.github.com/repos/benbp/azure-sdk-tools/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/benbp/azure-sdk-tools/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/benbp/azure-sdk-tools/contents/{+path}", + "compare_url": "https://api.github.com/repos/benbp/azure-sdk-tools/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/benbp/azure-sdk-tools/merges", + "archive_url": "https://api.github.com/repos/benbp/azure-sdk-tools/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/benbp/azure-sdk-tools/downloads", + "issues_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues{/number}", + "pulls_url": "https://api.github.com/repos/benbp/azure-sdk-tools/pulls{/number}", + "milestones_url": "https://api.github.com/repos/benbp/azure-sdk-tools/milestones{/number}", + "notifications_url": "https://api.github.com/repos/benbp/azure-sdk-tools/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/benbp/azure-sdk-tools/labels{/name}", + "releases_url": "https://api.github.com/repos/benbp/azure-sdk-tools/releases{/id}", + "deployments_url": "https://api.github.com/repos/benbp/azure-sdk-tools/deployments" + } + }, + { + "id": 10092247942, + "node_id": "CS_kwDOFqc1sc8AAAACWYt7hg", + "head_branch": "benbp/ci-pr", + "head_sha": "1f3db168a3ae9d8aad1b7e65184565efb3018713", + "status": "completed", + "conclusion": "success", + "url": "https://api.github.com/repos/benbp/azure-sdk-tools/check-suites/10092247942", + "before": "b22924091189779aeca71beec21d79f9d79dc6a2", + "after": "1f3db168a3ae9d8aad1b7e65184565efb3018713", + "pull_requests": [ + { + "url": "https://api.github.com/repos/benbp/azure-sdk-tools/pulls/26", + "id": 1174568198, + "number": 26, + "head": { + "ref": "benbp/ci-pr", + "sha": "1f3db168a3ae9d8aad1b7e65184565efb3018713", + "repo": { + "id": 380057009, + "url": "https://api.github.com/repos/benbp/azure-sdk-tools", + "name": "azure-sdk-tools" + } + }, + "base": { + "ref": "main", + "sha": "050e02490337f660730af4edbdf5a8b23d34759f", + "repo": { + "id": 380057009, + "url": "https://api.github.com/repos/benbp/azure-sdk-tools", + "name": "azure-sdk-tools" + } + } + } + ], + "app": { + "id": 15368, + "slug": "github-actions", + "node_id": "MDM6QXBwMTUzNjg=", + "owner": { + "login": "github", + "id": 9919, + "node_id": "MDEyOk9yZ2FuaXphdGlvbjk5MTk=", + "avatar_url": "https://avatars.githubusercontent.com/u/9919?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/github", + "html_url": "https://github.com/github", + "followers_url": "https://api.github.com/users/github/followers", + "following_url": "https://api.github.com/users/github/following{/other_user}", + "gists_url": "https://api.github.com/users/github/gists{/gist_id}", + "starred_url": "https://api.github.com/users/github/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/github/subscriptions", + "organizations_url": "https://api.github.com/users/github/orgs", + "repos_url": "https://api.github.com/users/github/repos", + "events_url": "https://api.github.com/users/github/events{/privacy}", + "received_events_url": "https://api.github.com/users/github/received_events", + "type": "Organization", + "site_admin": false + }, + "name": "GitHub Actions", + "description": "Automate your workflow from idea to production", + "external_url": "https://help.github.com/en/actions", + "html_url": "https://github.com/apps/github-actions", + "created_at": "2018-07-30T09:30:17Z", + "updated_at": "2019-12-10T19:04:12Z", + "permissions": { + "actions": "write", + "administration": "read", + "checks": "write", + "contents": "write", + "deployments": "write", + "discussions": "write", + "issues": "write", + "merge_queues": "write", + "metadata": "read", + "packages": "write", + "pages": "write", + "pull_requests": "write", + "repository_hooks": "write", + "repository_projects": "write", + "security_events": "write", + "statuses": "write", + "vulnerability_alerts": "read" + }, + "events": [ + "branch_protection_rule", + "check_run", + "check_suite", + "create", + "delete", + "deployment", + "deployment_status", + "discussion", + "discussion_comment", + "fork", + "gollum", + "issues", + "issue_comment", + "label", + "merge_group", + "milestone", + "page_build", + "project", + "project_card", + "project_column", + "public", + "pull_request", + "pull_request_review", + "pull_request_review_comment", + "push", + "registry_package", + "release", + "repository", + "repository_dispatch", + "status", + "watch", + "workflow_dispatch", + "workflow_run" + ] + }, + "created_at": "2022-12-28T23:52:52Z", + "updated_at": "2022-12-28T23:53:01Z", + "rerequestable": true, + "runs_rerequestable": false, + "latest_check_runs_count": 1, + "check_runs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/check-suites/10092247942/check-runs", + "head_commit": { + "id": "1f3db168a3ae9d8aad1b7e65184565efb3018713", + "tree_id": "d5c30619027c5dd2b2b7f86d452e46bf13353add", + "message": "9", + "timestamp": "2022-12-28T23:52:47Z", + "author": { + "name": "Ben Broderick Phillips", + "email": "bebroder@microsoft.com" + }, + "committer": { + "name": "Ben Broderick Phillips", + "email": "bebroder@microsoft.com" + } + }, + "repository": { + "id": 380057009, + "node_id": "MDEwOlJlcG9zaXRvcnkzODAwNTcwMDk=", + "name": "azure-sdk-tools", + "full_name": "benbp/azure-sdk-tools", + "private": false, + "owner": { + "login": "benbp", + "id": 1020379, + "node_id": "MDQ6VXNlcjEwMjAzNzk=", + "avatar_url": "https://avatars.githubusercontent.com/u/1020379?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/benbp", + "html_url": "https://github.com/benbp", + "followers_url": "https://api.github.com/users/benbp/followers", + "following_url": "https://api.github.com/users/benbp/following{/other_user}", + "gists_url": "https://api.github.com/users/benbp/gists{/gist_id}", + "starred_url": "https://api.github.com/users/benbp/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/benbp/subscriptions", + "organizations_url": "https://api.github.com/users/benbp/orgs", + "repos_url": "https://api.github.com/users/benbp/repos", + "events_url": "https://api.github.com/users/benbp/events{/privacy}", + "received_events_url": "https://api.github.com/users/benbp/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/benbp/azure-sdk-tools", + "description": "Tools repository leveraged by the Azure SDK team.", + "fork": true, + "url": "https://api.github.com/repos/benbp/azure-sdk-tools", + "forks_url": "https://api.github.com/repos/benbp/azure-sdk-tools/forks", + "keys_url": "https://api.github.com/repos/benbp/azure-sdk-tools/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/benbp/azure-sdk-tools/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/benbp/azure-sdk-tools/teams", + "hooks_url": "https://api.github.com/repos/benbp/azure-sdk-tools/hooks", + "issue_events_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues/events{/number}", + "events_url": "https://api.github.com/repos/benbp/azure-sdk-tools/events", + "assignees_url": "https://api.github.com/repos/benbp/azure-sdk-tools/assignees{/user}", + "branches_url": "https://api.github.com/repos/benbp/azure-sdk-tools/branches{/branch}", + "tags_url": "https://api.github.com/repos/benbp/azure-sdk-tools/tags", + "blobs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/benbp/azure-sdk-tools/statuses/{sha}", + "languages_url": "https://api.github.com/repos/benbp/azure-sdk-tools/languages", + "stargazers_url": "https://api.github.com/repos/benbp/azure-sdk-tools/stargazers", + "contributors_url": "https://api.github.com/repos/benbp/azure-sdk-tools/contributors", + "subscribers_url": "https://api.github.com/repos/benbp/azure-sdk-tools/subscribers", + "subscription_url": "https://api.github.com/repos/benbp/azure-sdk-tools/subscription", + "commits_url": "https://api.github.com/repos/benbp/azure-sdk-tools/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/benbp/azure-sdk-tools/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/benbp/azure-sdk-tools/contents/{+path}", + "compare_url": "https://api.github.com/repos/benbp/azure-sdk-tools/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/benbp/azure-sdk-tools/merges", + "archive_url": "https://api.github.com/repos/benbp/azure-sdk-tools/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/benbp/azure-sdk-tools/downloads", + "issues_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues{/number}", + "pulls_url": "https://api.github.com/repos/benbp/azure-sdk-tools/pulls{/number}", + "milestones_url": "https://api.github.com/repos/benbp/azure-sdk-tools/milestones{/number}", + "notifications_url": "https://api.github.com/repos/benbp/azure-sdk-tools/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/benbp/azure-sdk-tools/labels{/name}", + "releases_url": "https://api.github.com/repos/benbp/azure-sdk-tools/releases{/id}", + "deployments_url": "https://api.github.com/repos/benbp/azure-sdk-tools/deployments" + } + } + ] +} diff --git a/testpayloads/single_with_empty_check_suite_response.json b/testpayloads/single_with_empty_check_suite_response.json new file mode 100644 index 0000000..c1514b7 --- /dev/null +++ b/testpayloads/single_with_empty_check_suite_response.json @@ -0,0 +1,177 @@ +{ + "total_count": 1, + "check_suites": [ + { + "id": 10092247692, + "node_id": "CS_kwDOFqc1sc8AAAACWYt6jA", + "head_branch": "benbp/ci-pr", + "head_sha": "1f3db168a3ae9d8aad1b7e65184565efb3018713", + "status": "queued", + "conclusion": null, + "url": "https://api.github.com/repos/benbp/azure-sdk-tools/check-suites/10092247692", + "before": "b22924091189779aeca71beec21d79f9d79dc6a2", + "after": "1f3db168a3ae9d8aad1b7e65184565efb3018713", + "pull_requests": [ + { + "url": "https://api.github.com/repos/benbp/azure-sdk-tools/pulls/26", + "id": 1174568198, + "number": 26, + "head": { + "ref": "benbp/ci-pr", + "sha": "1f3db168a3ae9d8aad1b7e65184565efb3018713", + "repo": { + "id": 380057009, + "url": "https://api.github.com/repos/benbp/azure-sdk-tools", + "name": "azure-sdk-tools" + } + }, + "base": { + "ref": "main", + "sha": "050e02490337f660730af4edbdf5a8b23d34759f", + "repo": { + "id": 380057009, + "url": "https://api.github.com/repos/benbp/azure-sdk-tools", + "name": "azure-sdk-tools" + } + } + } + ], + "app": { + "id": 9426, + "slug": "azure-pipelines", + "node_id": "MDM6QXBwOTQyNg==", + "owner": { + "login": "AzurePipelines", + "id": 39924718, + "node_id": "MDQ6VXNlcjM5OTI0NzE4", + "avatar_url": "https://avatars.githubusercontent.com/u/39924718?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/AzurePipelines", + "html_url": "https://github.com/AzurePipelines", + "followers_url": "https://api.github.com/users/AzurePipelines/followers", + "following_url": "https://api.github.com/users/AzurePipelines/following{/other_user}", + "gists_url": "https://api.github.com/users/AzurePipelines/gists{/gist_id}", + "starred_url": "https://api.github.com/users/AzurePipelines/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/AzurePipelines/subscriptions", + "organizations_url": "https://api.github.com/users/AzurePipelines/orgs", + "repos_url": "https://api.github.com/users/AzurePipelines/repos", + "events_url": "https://api.github.com/users/AzurePipelines/events{/privacy}", + "received_events_url": "https://api.github.com/users/AzurePipelines/received_events", + "type": "User", + "site_admin": false + }, + "name": "Azure Pipelines", + "description": "### Continuously build, test, and deploy to any platform and cloud\r\n\r\n[Azure Pipelines](https://azure.microsoft.com/services/devops/pipelines/) offers cloud-hosted pipelines for Linux, macOS, and Windows with 10 free parallel jobs and unlimited minutes for open source projects.\r\n\r\n:v: **Any language, platform, and cloud**\r\nBuild, test, and deploy Node.js, Python, 
Java, PHP, Ruby, Go, C/C++, C#, Android, and iOS apps. Run in parallel on Linux, macOS, and Windows. Deploy to cloud providers like Azure, AWS, and GCP. Distribute mobile apps through beta channels and app stores.\r\n\r\n:whale2: **Native container support**\r\nCreate new containers with ease and push them to any registry. Deploy containers to independent hosts or Kubernetes.\r\n\r\n:muscle: **Advanced workflows and features**\r\nEasy build chaining and multi-phased builds. Support for YAML, test integration, release gates, reporting, and more.\r\n\r\n:wrench: **Extensible**\r\nUse a range of build, test, and deployment tasks built by the community – hundreds of extensions from Slack to SonarCloud. You can even deploy from other CI systems, like Jenkins. Webhooks and REST APIs help you integrate.\r\n\r\n:blue_heart: **Free, to you from Azure Pipelines**\r\nFree cloud-hosted builds for public and private repositories.\r\n", + "external_url": "https://azure.microsoft.com/services/devops/pipelines/", + "html_url": "https://github.com/apps/azure-pipelines", + "created_at": "2018-02-23T17:14:32Z", + "updated_at": "2018-10-25T18:51:25Z", + "permissions": { + "checks": "write", + "contents": "write", + "deployments": "write", + "issues": "write", + "metadata": "read", + "pull_requests": "write", + "statuses": "write" + }, + "events": [ + "check_suite", + "create", + "issue_comment", + "pull_request", + "push", + "release", + "repository" + ] + }, + "created_at": "2022-12-28T23:52:49Z", + "updated_at": "2022-12-28T23:52:49Z", + "rerequestable": true, + "runs_rerequestable": true, + "latest_check_runs_count": 0, + "check_runs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/check-suites/10092247692/check-runs", + "head_commit": { + "id": "1f3db168a3ae9d8aad1b7e65184565efb3018713", + "tree_id": "d5c30619027c5dd2b2b7f86d452e46bf13353add", + "message": "9", + "timestamp": "2022-12-28T23:52:47Z", + "author": { + "name": "Ben Broderick Phillips", + "email": "bebroder@microsoft.com" + }, + "committer": { + "name": "Ben Broderick Phillips", + "email": "bebroder@microsoft.com" + } + }, + "repository": { + "id": 380057009, + "node_id": "MDEwOlJlcG9zaXRvcnkzODAwNTcwMDk=", + "name": "azure-sdk-tools", + "full_name": "benbp/azure-sdk-tools", + "private": false, + "owner": { + "login": "benbp", + "id": 1020379, + "node_id": "MDQ6VXNlcjEwMjAzNzk=", + "avatar_url": "https://avatars.githubusercontent.com/u/1020379?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/benbp", + "html_url": "https://github.com/benbp", + "followers_url": "https://api.github.com/users/benbp/followers", + "following_url": "https://api.github.com/users/benbp/following{/other_user}", + "gists_url": "https://api.github.com/users/benbp/gists{/gist_id}", + "starred_url": "https://api.github.com/users/benbp/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/benbp/subscriptions", + "organizations_url": "https://api.github.com/users/benbp/orgs", + "repos_url": "https://api.github.com/users/benbp/repos", + "events_url": "https://api.github.com/users/benbp/events{/privacy}", + "received_events_url": "https://api.github.com/users/benbp/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/benbp/azure-sdk-tools", + "description": "Tools repository leveraged by the Azure SDK team.", + "fork": true, + "url": "https://api.github.com/repos/benbp/azure-sdk-tools", + "forks_url": "https://api.github.com/repos/benbp/azure-sdk-tools/forks", + "keys_url": "https://api.github.com/repos/benbp/azure-sdk-tools/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/benbp/azure-sdk-tools/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/benbp/azure-sdk-tools/teams", + "hooks_url": "https://api.github.com/repos/benbp/azure-sdk-tools/hooks", + "issue_events_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues/events{/number}", + "events_url": "https://api.github.com/repos/benbp/azure-sdk-tools/events", + "assignees_url": "https://api.github.com/repos/benbp/azure-sdk-tools/assignees{/user}", + "branches_url": "https://api.github.com/repos/benbp/azure-sdk-tools/branches{/branch}", + "tags_url": "https://api.github.com/repos/benbp/azure-sdk-tools/tags", + "blobs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/benbp/azure-sdk-tools/statuses/{sha}", + "languages_url": "https://api.github.com/repos/benbp/azure-sdk-tools/languages", + "stargazers_url": "https://api.github.com/repos/benbp/azure-sdk-tools/stargazers", + "contributors_url": "https://api.github.com/repos/benbp/azure-sdk-tools/contributors", + "subscribers_url": "https://api.github.com/repos/benbp/azure-sdk-tools/subscribers", + "subscription_url": "https://api.github.com/repos/benbp/azure-sdk-tools/subscription", + "commits_url": "https://api.github.com/repos/benbp/azure-sdk-tools/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/benbp/azure-sdk-tools/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/benbp/azure-sdk-tools/contents/{+path}", + "compare_url": "https://api.github.com/repos/benbp/azure-sdk-tools/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/benbp/azure-sdk-tools/merges", + "archive_url": "https://api.github.com/repos/benbp/azure-sdk-tools/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/benbp/azure-sdk-tools/downloads", + "issues_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues{/number}", + "pulls_url": "https://api.github.com/repos/benbp/azure-sdk-tools/pulls{/number}", + "milestones_url": "https://api.github.com/repos/benbp/azure-sdk-tools/milestones{/number}", + "notifications_url": "https://api.github.com/repos/benbp/azure-sdk-tools/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/benbp/azure-sdk-tools/labels{/name}", + "releases_url": "https://api.github.com/repos/benbp/azure-sdk-tools/releases{/id}", + "deployments_url": "https://api.github.com/repos/benbp/azure-sdk-tools/deployments" + } + } + ] +} diff --git a/testpayloads/workflow_run_event.json b/testpayloads/workflow_run_event.json new file mode 100644 index 0000000..d3a667b --- /dev/null +++ b/testpayloads/workflow_run_event.json @@ -0,0 +1,385 @@ +{ + "action": "completed", + "repository": { + "allow_forking": true, + "archive_url": "https://api.github.com/repos/benbp/azure-sdk-tools/{archive_format}{/ref}", + "archived": false, + "assignees_url": "https://api.github.com/repos/benbp/azure-sdk-tools/assignees{/user}", + "blobs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/benbp/azure-sdk-tools/branches{/branch}", + "clone_url": "https://github.com/benbp/azure-sdk-tools.git", + "collaborators_url": "https://api.github.com/repos/benbp/azure-sdk-tools/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/benbp/azure-sdk-tools/comments{/number}", + "commits_url": "https://api.github.com/repos/benbp/azure-sdk-tools/commits{/sha}", + "compare_url": "https://api.github.com/repos/benbp/azure-sdk-tools/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/benbp/azure-sdk-tools/contents/{+path}", + "contributors_url": "https://api.github.com/repos/benbp/azure-sdk-tools/contributors", + "created_at": "2021-06-24T21:43:31Z", + "default_branch": "main", + "deployments_url": "https://api.github.com/repos/benbp/azure-sdk-tools/deployments", + "description": "Tools repository leveraged by the Azure SDK team.", + "disabled": false, + "downloads_url": "https://api.github.com/repos/benbp/azure-sdk-tools/downloads", + "events_url": "https://api.github.com/repos/benbp/azure-sdk-tools/events", + "fork": true, + "forks": 0, + "forks_count": 0, + "forks_url": "https://api.github.com/repos/benbp/azure-sdk-tools/forks", + "full_name": "benbp/azure-sdk-tools", + "git_commits_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/tags{/sha}", + "git_url": "git://github.com/benbp/azure-sdk-tools.git", + "has_discussions": false, + "has_downloads": true, + "has_issues": true, + "has_pages": false, + "has_projects": true, + "has_wiki": true, + "homepage": null, + "hooks_url": "https://api.github.com/repos/benbp/azure-sdk-tools/hooks", + "html_url": "https://github.com/benbp/azure-sdk-tools", + "id": 380057009, + "is_template": false, + "issue_comment_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues/events{/number}", + "issues_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues{/number}", + "keys_url": "https://api.github.com/repos/benbp/azure-sdk-tools/keys{/key_id}", + "labels_url": "https://api.github.com/repos/benbp/azure-sdk-tools/labels{/name}", + "language": "C#", + "languages_url": "https://api.github.com/repos/benbp/azure-sdk-tools/languages", + "license": { + "key": "mit", + "name": "MIT License", + "node_id": "MDc6TGljZW5zZTEz", + "spdx_id": "MIT", + "url": "https://api.github.com/licenses/mit" + }, + "merges_url": "https://api.github.com/repos/benbp/azure-sdk-tools/merges", + "milestones_url": "https://api.github.com/repos/benbp/azure-sdk-tools/milestones{/number}", + "mirror_url": null, + "name": "azure-sdk-tools", + "node_id": "MDEwOlJlcG9zaXRvcnkzODAwNTcwMDk=", + "notifications_url": "https://api.github.com/repos/benbp/azure-sdk-tools/notifications{?since,all,participating}", + "open_issues": 2, + "open_issues_count": 2, + "owner": { + "avatar_url": "https://avatars.githubusercontent.com/u/1020379?v=4", + "events_url": "https://api.github.com/users/benbp/events{/privacy}", + "followers_url": "https://api.github.com/users/benbp/followers", + "following_url": "https://api.github.com/users/benbp/following{/other_user}", + "gists_url": "https://api.github.com/users/benbp/gists{/gist_id}", + "gravatar_id": "", + "html_url": "https://github.com/benbp", + "id": 1020379, + "login": "benbp", + "node_id": "MDQ6VXNlcjEwMjAzNzk=", + "organizations_url": "https://api.github.com/users/benbp/orgs", + "received_events_url": "https://api.github.com/users/benbp/received_events", + "repos_url": "https://api.github.com/users/benbp/repos", + "site_admin": false, + "starred_url": "https://api.github.com/users/benbp/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/benbp/subscriptions", + "type": "User", + "url": "https://api.github.com/users/benbp" + }, + "private": false, + "pulls_url": "https://api.github.com/repos/benbp/azure-sdk-tools/pulls{/number}", + "pushed_at": "2022-12-28T23:31:11Z", + "releases_url": "https://api.github.com/repos/benbp/azure-sdk-tools/releases{/id}", + "size": 46440, + "ssh_url": "git@github.com:benbp/azure-sdk-tools.git", + "stargazers_count": 0, + "stargazers_url": "https://api.github.com/repos/benbp/azure-sdk-tools/stargazers", + "statuses_url": "https://api.github.com/repos/benbp/azure-sdk-tools/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/benbp/azure-sdk-tools/subscribers", + "subscription_url": "https://api.github.com/repos/benbp/azure-sdk-tools/subscription", + "svn_url": "https://github.com/benbp/azure-sdk-tools", + "tags_url": "https://api.github.com/repos/benbp/azure-sdk-tools/tags", + "teams_url": "https://api.github.com/repos/benbp/azure-sdk-tools/teams", + "topics": [], + "trees_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/trees{/sha}", + "updated_at": "2022-10-21T19:20:04Z", + "url": "https://api.github.com/repos/benbp/azure-sdk-tools", + "visibility": "public", + "watchers": 0, + "watchers_count": 0, + "web_commit_signoff_required": false + }, + "sender": { + "avatar_url": "https://avatars.githubusercontent.com/u/1020379?v=4", + "events_url": "https://api.github.com/users/benbp/events{/privacy}", + "followers_url": "https://api.github.com/users/benbp/followers", + "following_url": "https://api.github.com/users/benbp/following{/other_user}", + "gists_url": "https://api.github.com/users/benbp/gists{/gist_id}", + "gravatar_id": "", + "html_url": "https://github.com/benbp", + "id": 1020379, + "login": "benbp", + "node_id": "MDQ6VXNlcjEwMjAzNzk=", + "organizations_url": "https://api.github.com/users/benbp/orgs", + "received_events_url": "https://api.github.com/users/benbp/received_events", + "repos_url": "https://api.github.com/users/benbp/repos", + "site_admin": false, + "starred_url": "https://api.github.com/users/benbp/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/benbp/subscriptions", + "type": "User", + "url": "https://api.github.com/users/benbp" + }, + "workflow": { + "badge_url": "https://github.com/benbp/azure-sdk-tools/workflows/cli-ci/badge.svg", + "created_at": "2022-12-21T17:48:30.000Z", + "html_url": "https://github.com/benbp/azure-sdk-tools/blob/main/.github/workflows/cli-ci.yml", + "id": 43680723, + "name": "cli-ci", + "node_id": "W_kwDOFqc1sc4CmoPT", + "path": ".github/workflows/cli-ci.yml", + "state": "active", + "updated_at": "2022-12-21T17:48:30.000Z", + "url": "https://api.github.com/repos/benbp/azure-sdk-tools/actions/workflows/43680723" + }, + "workflow_run": { + "actor": { + "avatar_url": "https://avatars.githubusercontent.com/u/1020379?v=4", + "events_url": "https://api.github.com/users/benbp/events{/privacy}", + "followers_url": "https://api.github.com/users/benbp/followers", + "following_url": "https://api.github.com/users/benbp/following{/other_user}", + "gists_url": "https://api.github.com/users/benbp/gists{/gist_id}", + "gravatar_id": "", + "html_url": "https://github.com/benbp", + "id": 1020379, + "login": "benbp", + "node_id": "MDQ6VXNlcjEwMjAzNzk=", + "organizations_url": "https://api.github.com/users/benbp/orgs", + "received_events_url": "https://api.github.com/users/benbp/received_events", + "repos_url": "https://api.github.com/users/benbp/repos", + "site_admin": false, + "starred_url": "https://api.github.com/users/benbp/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/benbp/subscriptions", + "type": "User", + "url": "https://api.github.com/users/benbp" + }, + "artifacts_url": "https://api.github.com/repos/benbp/azure-sdk-tools/actions/runs/3797196322/artifacts", + "cancel_url": "https://api.github.com/repos/benbp/azure-sdk-tools/actions/runs/3797196322/cancel", + "check_suite_id": 10092106576, + "check_suite_node_id": "CS_kwDOFqc1sc8AAAACWYlTUA", + "check_suite_url": "https://api.github.com/repos/benbp/azure-sdk-tools/check-suites/10092106576", + "conclusion": "failure", + "created_at": "2022-12-28T23:31:15Z", + "display_title": "TESTING ACTION", + "event": "pull_request", + "head_branch": "benbp/ci-pr", + "head_commit": { + "author": { + "email": "bebroder@microsoft.com", + "name": "Ben Broderick Phillips" + }, + "committer": { + "email": "bebroder@microsoft.com", + "name": "Ben Broderick Phillips" + }, + "id": "336a6bcd5e8b572f7102e370785d168379ee04bb", + "message": "7", + "timestamp": "2022-12-28T23:31:07Z", + "tree_id": "14cd1f1b5159b8cbd5e5e10f5f4a403cd8babb6f" + }, + "head_repository": { + "archive_url": "https://api.github.com/repos/benbp/azure-sdk-tools/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/benbp/azure-sdk-tools/assignees{/user}", + "blobs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/benbp/azure-sdk-tools/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/benbp/azure-sdk-tools/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/benbp/azure-sdk-tools/comments{/number}", + "commits_url": "https://api.github.com/repos/benbp/azure-sdk-tools/commits{/sha}", + "compare_url": "https://api.github.com/repos/benbp/azure-sdk-tools/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/benbp/azure-sdk-tools/contents/{+path}", + "contributors_url": "https://api.github.com/repos/benbp/azure-sdk-tools/contributors", + "deployments_url": "https://api.github.com/repos/benbp/azure-sdk-tools/deployments", + "description": "Tools repository leveraged by the Azure SDK team.", + "downloads_url": "https://api.github.com/repos/benbp/azure-sdk-tools/downloads", + "events_url": "https://api.github.com/repos/benbp/azure-sdk-tools/events", + "fork": true, + "forks_url": "https://api.github.com/repos/benbp/azure-sdk-tools/forks", + "full_name": "benbp/azure-sdk-tools", + "git_commits_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/tags{/sha}", + "hooks_url": "https://api.github.com/repos/benbp/azure-sdk-tools/hooks", + "html_url": "https://github.com/benbp/azure-sdk-tools", + "id": 380057009, + "issue_comment_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues/events{/number}", + "issues_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues{/number}", + "keys_url": "https://api.github.com/repos/benbp/azure-sdk-tools/keys{/key_id}", + "labels_url": "https://api.github.com/repos/benbp/azure-sdk-tools/labels{/name}", + "languages_url": "https://api.github.com/repos/benbp/azure-sdk-tools/languages", + "merges_url": "https://api.github.com/repos/benbp/azure-sdk-tools/merges", + "milestones_url": "https://api.github.com/repos/benbp/azure-sdk-tools/milestones{/number}", + "name": "azure-sdk-tools", + "node_id": "MDEwOlJlcG9zaXRvcnkzODAwNTcwMDk=", + "notifications_url": "https://api.github.com/repos/benbp/azure-sdk-tools/notifications{?since,all,participating}", + "owner": { + "avatar_url": "https://avatars.githubusercontent.com/u/1020379?v=4", + "events_url": "https://api.github.com/users/benbp/events{/privacy}", + "followers_url": "https://api.github.com/users/benbp/followers", + "following_url": "https://api.github.com/users/benbp/following{/other_user}", + "gists_url": "https://api.github.com/users/benbp/gists{/gist_id}", + "gravatar_id": "", + "html_url": "https://github.com/benbp", + "id": 1020379, + "login": "benbp", + "node_id": "MDQ6VXNlcjEwMjAzNzk=", + "organizations_url": "https://api.github.com/users/benbp/orgs", + "received_events_url": "https://api.github.com/users/benbp/received_events", + "repos_url": "https://api.github.com/users/benbp/repos", + "site_admin": false, + "starred_url": "https://api.github.com/users/benbp/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/benbp/subscriptions", + "type": "User", + "url": "https://api.github.com/users/benbp" + }, + "private": false, + "pulls_url": "https://api.github.com/repos/benbp/azure-sdk-tools/pulls{/number}", + "releases_url": "https://api.github.com/repos/benbp/azure-sdk-tools/releases{/id}", + "stargazers_url": "https://api.github.com/repos/benbp/azure-sdk-tools/stargazers", + "statuses_url": "https://api.github.com/repos/benbp/azure-sdk-tools/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/benbp/azure-sdk-tools/subscribers", + "subscription_url": "https://api.github.com/repos/benbp/azure-sdk-tools/subscription", + "tags_url": "https://api.github.com/repos/benbp/azure-sdk-tools/tags", + "teams_url": "https://api.github.com/repos/benbp/azure-sdk-tools/teams", + "trees_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/trees{/sha}", + "url": "https://api.github.com/repos/benbp/azure-sdk-tools" + }, + "head_sha": "336a6bcd5e8b572f7102e370785d168379ee04bb", + "html_url": "https://github.com/benbp/azure-sdk-tools/actions/runs/3797196322", + "id": 3797196322, + "jobs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/actions/runs/3797196322/jobs", + "logs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/actions/runs/3797196322/logs", + "name": "cli-ci", + "node_id": "WFR_kwLOFqc1sc7iVJ4i", + "path": ".github/workflows/cli-ci.yml", + "previous_attempt_url": null, + "pull_requests": [ + { + "base": { + "ref": "main", + "repo": { + "id": 380057009, + "name": "azure-sdk-tools", + "url": "https://api.github.com/repos/benbp/azure-sdk-tools" + }, + "sha": "3a00a1b8b10f27ce697079c5449ebc6ee48857e2" + }, + "head": { + "ref": "benbp/ci-pr", + "repo": { + "id": 380057009, + "name": "azure-sdk-tools", + "url": "https://api.github.com/repos/benbp/azure-sdk-tools" + }, + "sha": "336a6bcd5e8b572f7102e370785d168379ee04bb" + }, + "id": 1174568198, + "number": 26, + "url": "https://api.github.com/repos/benbp/azure-sdk-tools/pulls/26" + } + ], + "referenced_workflows": [], + "repository": { + "archive_url": "https://api.github.com/repos/benbp/azure-sdk-tools/{archive_format}{/ref}", + "assignees_url": "https://api.github.com/repos/benbp/azure-sdk-tools/assignees{/user}", + "blobs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/blobs{/sha}", + "branches_url": "https://api.github.com/repos/benbp/azure-sdk-tools/branches{/branch}", + "collaborators_url": "https://api.github.com/repos/benbp/azure-sdk-tools/collaborators{/collaborator}", + "comments_url": "https://api.github.com/repos/benbp/azure-sdk-tools/comments{/number}", + "commits_url": "https://api.github.com/repos/benbp/azure-sdk-tools/commits{/sha}", + "compare_url": "https://api.github.com/repos/benbp/azure-sdk-tools/compare/{base}...{head}", + "contents_url": "https://api.github.com/repos/benbp/azure-sdk-tools/contents/{+path}", + "contributors_url": "https://api.github.com/repos/benbp/azure-sdk-tools/contributors", + "deployments_url": "https://api.github.com/repos/benbp/azure-sdk-tools/deployments", + "description": "Tools repository leveraged by the Azure SDK team.", + "downloads_url": "https://api.github.com/repos/benbp/azure-sdk-tools/downloads", + "events_url": "https://api.github.com/repos/benbp/azure-sdk-tools/events", + "fork": true, + "forks_url": "https://api.github.com/repos/benbp/azure-sdk-tools/forks", + "full_name": "benbp/azure-sdk-tools", + "git_commits_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/commits{/sha}", + "git_refs_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/refs{/sha}", + "git_tags_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/tags{/sha}", + "hooks_url": "https://api.github.com/repos/benbp/azure-sdk-tools/hooks", + "html_url": "https://github.com/benbp/azure-sdk-tools", + "id": 380057009, + "issue_comment_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues/comments{/number}", + "issue_events_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues/events{/number}", + "issues_url": "https://api.github.com/repos/benbp/azure-sdk-tools/issues{/number}", + "keys_url": "https://api.github.com/repos/benbp/azure-sdk-tools/keys{/key_id}", + "labels_url": "https://api.github.com/repos/benbp/azure-sdk-tools/labels{/name}", + "languages_url": "https://api.github.com/repos/benbp/azure-sdk-tools/languages", + "merges_url": "https://api.github.com/repos/benbp/azure-sdk-tools/merges", + "milestones_url": "https://api.github.com/repos/benbp/azure-sdk-tools/milestones{/number}", + "name": "azure-sdk-tools", + "node_id": "MDEwOlJlcG9zaXRvcnkzODAwNTcwMDk=", + "notifications_url": "https://api.github.com/repos/benbp/azure-sdk-tools/notifications{?since,all,participating}", + "owner": { + "avatar_url": "https://avatars.githubusercontent.com/u/1020379?v=4", + "events_url": "https://api.github.com/users/benbp/events{/privacy}", + "followers_url": "https://api.github.com/users/benbp/followers", + "following_url": "https://api.github.com/users/benbp/following{/other_user}", + "gists_url": "https://api.github.com/users/benbp/gists{/gist_id}", + "gravatar_id": "", + "html_url": "https://github.com/benbp", + "id": 1020379, + "login": "benbp", + "node_id": "MDQ6VXNlcjEwMjAzNzk=", + "organizations_url": "https://api.github.com/users/benbp/orgs", + "received_events_url": "https://api.github.com/users/benbp/received_events", + "repos_url": "https://api.github.com/users/benbp/repos", + "site_admin": false, + "starred_url": "https://api.github.com/users/benbp/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/benbp/subscriptions", + "type": "User", + "url": "https://api.github.com/users/benbp" + }, + "private": false, + "pulls_url": "https://api.github.com/repos/benbp/azure-sdk-tools/pulls{/number}", + "releases_url": "https://api.github.com/repos/benbp/azure-sdk-tools/releases{/id}", + "stargazers_url": "https://api.github.com/repos/benbp/azure-sdk-tools/stargazers", + "statuses_url": "https://api.github.com/repos/benbp/azure-sdk-tools/statuses/{sha}", + "subscribers_url": "https://api.github.com/repos/benbp/azure-sdk-tools/subscribers", + "subscription_url": "https://api.github.com/repos/benbp/azure-sdk-tools/subscription", + "tags_url": "https://api.github.com/repos/benbp/azure-sdk-tools/tags", + "teams_url": "https://api.github.com/repos/benbp/azure-sdk-tools/teams", + "trees_url": "https://api.github.com/repos/benbp/azure-sdk-tools/git/trees{/sha}", + "url": "https://api.github.com/repos/benbp/azure-sdk-tools" + }, + "rerun_url": "https://api.github.com/repos/benbp/azure-sdk-tools/actions/runs/3797196322/rerun", + "run_attempt": 1, + "run_number": 11, + "run_started_at": "2022-12-28T23:31:15Z", + "status": "completed", + "triggering_actor": { + "avatar_url": "https://avatars.githubusercontent.com/u/1020379?v=4", + "events_url": "https://api.github.com/users/benbp/events{/privacy}", + "followers_url": "https://api.github.com/users/benbp/followers", + "following_url": "https://api.github.com/users/benbp/following{/other_user}", + "gists_url": "https://api.github.com/users/benbp/gists{/gist_id}", + "gravatar_id": "", + "html_url": "https://github.com/benbp", + "id": 1020379, + "login": "benbp", + "node_id": "MDQ6VXNlcjEwMjAzNzk=", + "organizations_url": "https://api.github.com/users/benbp/orgs", + "received_events_url": "https://api.github.com/users/benbp/received_events", + "repos_url": "https://api.github.com/users/benbp/repos", + "site_admin": false, + "starred_url": "https://api.github.com/users/benbp/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/benbp/subscriptions", + "type": "User", + "url": "https://api.github.com/users/benbp" + }, + "updated_at": "2022-12-28T23:31:25Z", + "url": "https://api.github.com/repos/benbp/azure-sdk-tools/actions/runs/3797196322", + "workflow_id": 43680723, + "workflow_url": "https://api.github.com/repos/benbp/azure-sdk-tools/actions/workflows/43680723" + } +} diff --git a/types.go b/types.go index a19c6db..dd6dc2a 100644 --- a/types.go +++ b/types.go @@ -16,7 +16,7 @@ const ( IssueCommentActionCreated ActionType = "created" - CheckSuiteStatusRequested CheckSuiteStatus = "requested" + CheckSuiteStatusQueued CheckSuiteStatus = "queued" CheckSuiteStatusInProgress CheckSuiteStatus = "in_progress" CheckSuiteStatusCompleted CheckSuiteStatus = "completed" @@ -27,6 +27,7 @@ const ( CheckSuiteConclusionTimedOut CheckSuiteConclusion = "timed_out" CheckSuiteConclusionActionRequired CheckSuiteConclusion = "action_required" CheckSuiteConclusionStale CheckSuiteConclusion = "stale" + CheckSuiteConclusionEmpty CheckSuiteConclusion = "" ) type ActionType string @@ -75,14 +76,15 @@ type CheckSuites struct { } type CheckSuite struct { - Id int `json:"id"` - HeadBranch string `json:"head_branch"` - HeadSha string `json:"head_sha"` - Status CheckSuiteStatus `json:"status"` - Conclusion CheckSuiteConclusion `json:"conclusion"` - Url string `json:"url"` - CheckRunsUrl string `json:"check_runs_url"` - App App `json:"app"` + Id int `json:"id"` + HeadBranch string `json:"head_branch"` + HeadSha string `json:"head_sha"` + Status CheckSuiteStatus `json:"status"` + Conclusion CheckSuiteConclusion `json:"conclusion"` + Url string `json:"url"` + CheckRunsUrl string `json:"check_runs_url"` + LatestCheckRunCount int `json:"latest_check_runs_count"` + App App `json:"app"` } type App struct { @@ -114,10 +116,6 @@ type CheckSuiteWebhook struct { Repo Repo `json:"repository"` } -func IsCheckSuiteNoMatch(conclusion CheckSuiteConclusion) bool { - return conclusion == "" -} - func IsCheckSuiteSucceeded(conclusion CheckSuiteConclusion) bool { return conclusion == CheckSuiteConclusionSuccess } @@ -126,20 +124,8 @@ func IsCheckSuiteFailed(conclusion CheckSuiteConclusion) bool { return conclusion == CheckSuiteConclusionFailure || conclusion == CheckSuiteConclusionTimedOut } -func (cs *CheckSuite) IsSucceeded() bool { - return IsCheckSuiteSucceeded(cs.Conclusion) -} - -func (cs *CheckSuite) IsFailed() bool { - return IsCheckSuiteFailed(cs.Conclusion) -} - -func (csw *CheckSuiteWebhook) IsSucceeded() bool { - return csw.CheckSuite.IsSucceeded() -} - -func (csw *CheckSuiteWebhook) IsFailed() bool { - return csw.CheckSuite.IsFailed() +func (csw *CheckSuiteWebhook) GetCheckSuiteUrl() string { + return strings.ReplaceAll(csw.Repo.CommitsUrl, "{/sha}", fmt.Sprintf("/%s", csw.CheckSuite.HeadSha)) + "/check-suites" } func (csw *CheckSuiteWebhook) GetStatusesUrl() string { @@ -165,6 +151,44 @@ func (ic *IssueCommentWebhook) GetCommentsUrl() string { return ic.Issue.CommentsUrl } +type WorkflowRunPullRequestData struct { + Url string `json:"url"` + Id int `json:"id"` + Number int `json:"number"` +} + +type WorkflowRun struct { + HtmlUrl string `json:"html_url"` + HeadSha string `json:"head_sha"` + Event string `json:"event"` + HeadRepo Repo `json:"head_repository"` + PullRequests []WorkflowRunPullRequestData `json:"pull_requests"` +} + +type WorkflowRunWebhook struct { + Action string `json:"action"` + WorkflowRun WorkflowRun `json:"workflow_run"` +} + +func (wr *WorkflowRun) GetStatusesUrl() string { + return strings.ReplaceAll(wr.HeadRepo.StatusesUrl, "{sha}", wr.HeadSha) +} + +func (wr *WorkflowRun) GetCheckSuiteUrl() string { + return strings.ReplaceAll(wr.HeadRepo.CommitsUrl, "{/sha}", fmt.Sprintf("/%s", wr.HeadSha)) + "/check-suites" +} + +func NewWorkflowRunWebhook(payload []byte) *WorkflowRunWebhook { + var wr WorkflowRunWebhook + if err := json.Unmarshal(payload, &wr); err != nil { + return nil + } + if wr.WorkflowRun.HtmlUrl == "" && wr.WorkflowRun.HeadSha == "" { + return nil + } + return &wr +} + func NewIssueCommentBody(body string) ([]byte, error) { jsonBody, err := json.Marshal(IssueCommentBody{body}) if err != nil {