Visitar URL original
fix(actions): check every job page in get_job_logs failed_only · github/github-mcp-server@c48f118 · GitHub
Skip to content

Commit c48f118

Browse files
fix(actions): check every job page in get_job_logs failed_only
Only the first 30 jobs of a run were listed, so in larger matrix runs a failed job past that page was reported as "No failed jobs found".
1 parent 6330b02 commit c48f118

2 files changed

Lines changed: 72 additions & 9 deletions

File tree

‎pkg/github/actions.go‎

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,27 @@ const (
5050
// handleFailedJobLogs gets logs for all failed jobs in a workflow run
5151
func handleFailedJobLogs(ctx context.Context, client *github.Client, owner, repo string, runID int64, returnContent bool, tailLines int, contentWindowSize int) (*mcp.CallToolResult, *ActionsJobLogsOutput, error) {
5252
// First, get all jobs for the workflow run
53-
jobs, resp, err := client.Actions.ListWorkflowJobs(ctx, owner, repo, runID, &github.ListWorkflowJobsOptions{
54-
Filter: "latest",
55-
})
56-
if err != nil {
57-
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to list workflow jobs", resp, err), nil, nil
53+
opts := &github.ListWorkflowJobsOptions{
54+
Filter: "latest",
55+
ListOptions: github.ListOptions{PerPage: 100},
56+
}
57+
var allJobs []*github.WorkflowJob
58+
for {
59+
jobs, resp, err := client.Actions.ListWorkflowJobs(ctx, owner, repo, runID, opts)
60+
if err != nil {
61+
return ghErrors.NewGitHubAPIErrorResponse(ctx, "failed to list workflow jobs", resp, err), nil, nil
62+
}
63+
_ = resp.Body.Close()
64+
allJobs = append(allJobs, jobs.Jobs...)
65+
if resp.NextPage == 0 {
66+
break
67+
}
68+
opts.Page = resp.NextPage
5869
}
59-
defer func() { _ = resp.Body.Close() }()
6070

6171
// Filter for failed jobs
6272
var failedJobs []*github.WorkflowJob
63-
for _, job := range jobs.Jobs {
73+
for _, job := range allJobs {
6474
if job.GetConclusion() == "failure" {
6575
failedJobs = append(failedJobs, job)
6676
}
@@ -70,7 +80,7 @@ func handleFailedJobLogs(ctx context.Context, client *github.Client, owner, repo
7080
result := &ActionsFailedJobLogsOutput{
7181
Message: "No failed jobs found in this workflow run",
7282
RunID: runID,
73-
TotalJobs: len(jobs.Jobs),
83+
TotalJobs: len(allJobs),
7484
}
7585
r, _ := json.Marshal(result)
7686
return utils.NewToolResultText(string(r)), &ActionsJobLogsOutput{Failed: result}, nil
@@ -95,7 +105,7 @@ func handleFailedJobLogs(ctx context.Context, client *github.Client, owner, repo
95105
result := &ActionsFailedJobLogsOutput{
96106
Message: fmt.Sprintf("Retrieved logs for %d failed jobs", len(failedJobs)),
97107
RunID: runID,
98-
TotalJobs: len(jobs.Jobs),
108+
TotalJobs: len(allJobs),
99109
FailedJobs: len(failedJobs),
100110
Logs: &logResults,
101111
ReturnFormat: &ActionsLogsReturnFormat{Content: returnContent, URLs: !returnContent},

‎pkg/github/actions_test.go‎

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,4 +751,57 @@ func Test_ActionsGetJobLogs_FailedJobs(t *testing.T) {
751751
require.NoError(t, err)
752752
assert.Equal(t, "No failed jobs found in this workflow run", response["message"])
753753
})
754+
755+
t.Run("failed job on a later page", func(t *testing.T) {
756+
mockedClient := MockHTTPClientWithHandlers(map[string]http.HandlerFunc{
757+
GetReposActionsRunsJobsByOwnerByRepoByRunID: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
758+
jobs := &github.Jobs{TotalCount: new(31)}
759+
if r.URL.Query().Get("page") == "2" {
760+
jobs.Jobs = []*github.WorkflowJob{
761+
{ID: new(int64(31)), Name: new("test-job-31"), Conclusion: new("failure")},
762+
}
763+
} else {
764+
for i := int64(1); i <= 30; i++ {
765+
jobs.Jobs = append(jobs.Jobs, &github.WorkflowJob{
766+
ID: new(i),
767+
Name: new("passing-job"),
768+
Conclusion: new("success"),
769+
})
770+
}
771+
w.Header().Set("Link", `<https://api.github.com/repos/owner/repo/actions/runs/456/jobs?page=2>; rel="next"`)
772+
}
773+
w.WriteHeader(http.StatusOK)
774+
_ = json.NewEncoder(w).Encode(jobs)
775+
}),
776+
GetReposActionsJobsLogsByOwnerByRepoByJobID: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
777+
w.Header().Set("Location", "https://github.com/logs/job/31")
778+
w.WriteHeader(http.StatusFound)
779+
}),
780+
})
781+
782+
client := mustNewGHClient(t, mockedClient)
783+
deps := BaseDeps{
784+
Client: client,
785+
ContentWindowSize: 5000,
786+
}
787+
handler := toolDef.Handler(deps)
788+
789+
request := createMCPRequest(map[string]any{
790+
"owner": "owner",
791+
"repo": "repo",
792+
"run_id": float64(456),
793+
"failed_only": true,
794+
})
795+
result, err := handler(ContextWithDeps(context.Background(), deps), &request)
796+
797+
require.NoError(t, err)
798+
require.False(t, result.IsError)
799+
800+
textContent := getTextResult(t, result)
801+
var response map[string]any
802+
err = json.Unmarshal([]byte(textContent.Text), &response)
803+
require.NoError(t, err)
804+
assert.Equal(t, float64(1), response["failed_jobs"])
805+
assert.Equal(t, float64(31), response["total_jobs"])
806+
})
754807
}

0 commit comments

Comments
 (0)