Repository: palantir/policy-bot (go)

Issue:
has_successful_status may use incorrect check-run
It seems that there are situations where the github api can return multiple "latest" check-runs (for a given sha & check-run name) and policy-bot uses the least recent.

eg: I pushed this workflow:

```
on:
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - run: |
          if [ $PR_NUMBER == '1' ]; then
            echo "First PR, failing"
            exit 1
          else
            echo "Passing"
            exit 0
          fi
        env:
          PR_NUMBER: ${{ github.event.pull_request.number }}
```

and opened PR 1. The job failed then I closed the PR.

Then I opened PR 2 and the job succeeds.

Looking at the query that I think policy-bot makes, there are 2 runs for `test` and I think policy-bot uses the last (least recent) one. Strangely both runs reference PR 2.

```
curl -L \
  -H "Accept: application/vnd.github.antiope-preview+json" \
  -H "Authorization: Bearer $GITHUB_TOKEN" \
  -H "X-GitHub-Api-Version: 2022-11-28" \
  https://api.github.com/repos/<org>/<repo>/commits/c4bb5fb/check-runs?filter=latest \
    | jq '.check_runs[] | {name,completed_at,conclusion,pull_requests}'

{
  "name": "test",
  "completed_at": "2024-08-14T07:23:49Z",
  "conclusion": "success",
  "pull_requests": [
    {
      "number": 2,
      ...
    }
  ]
}
{
  "name": "test",
  "completed_at": "2024-08-14T07:22:49Z",
  "conclusion": "failure",
  "pull_requests": [
    {
      "number": 2,
      ...
    }
  ]
}
```

Choosing the first (most recent) run would fix this case but I guess there can be other scenarios where the same commit has multiple "latest" check-runs

Pull request:
Only use the most recent check runs on a commit
This fixes an issue where we would iterate over all check runs on a commit and keep the last one. Because GitHub returns check runs in reverse chronological order, this meant we used the oldest result for each run in the event of duplicates instead of the most recent.

Fixes #821.

Patch:
diff --git a/pull/github.go b/pull/github.go
index 70d45b5..9408906 100644
--- a/pull/github.go
+++ b/pull/github.go
@@ -819,9 +819,18 @@ func (ghc *GitHubContext) getCheckStatuses() (map[string]string, error) {
 		if err != nil {
 			return nil, errors.Wrapf(err, "failed to get check runs for page %d", opt.Page)
 		}
+
+		// Check runs are ordered from most to least recent. In some cases,
+		// like when a commit is included in multiple PRs or when users re-run
+		// checks, there may be multiple runs with the same name. We only want
+		// to keep the first (most recent) result for each name.
 		for _, checkRun := range checkRuns.CheckRuns {
-			statuses[checkRun.GetName()] = checkRun.GetConclusion()
+			name := checkRun.GetName()
+			if _, exists := statuses[name]; !exists {
+				statuses[name] = checkRun.GetConclusion()
+			}
 		}
+
 		if resp.NextPage == 0 {
 			break
 		}


Test patch:
diff --git a/pull/github_test.go b/pull/github_test.go
index 29a1d8d..bca2e1b 100644
--- a/pull/github_test.go
+++ b/pull/github_test.go
@@ -638,12 +638,36 @@ func TestLatestWorkflowRuns(t *testing.T) {
 	require.NoError(t, err)
 
 	assert.Len(t, runs, 3, "incorrect number of workflow runs")
-	assert.Equal(t, runs[".github/workflows/a.yml"], []string{"success", "skipped"}, "incorrect conclusion for workflow run a")
-	assert.Equal(t, runs[".github/workflows/b.yml"], []string{"failure"}, "incorrect conclusion for workflow run b")
-	assert.Equal(t, runs[".github/workflows/c.yml"], []string{"cancelled"}, "incorrect conclusion for workflow run c")
+	assert.ElementsMatch(t, runs[".github/workflows/a.yml"], []string{"success", "skipped"}, "incorrect conclusion for workflow run a")
+	assert.ElementsMatch(t, runs[".github/workflows/b.yml"], []string{"failure"}, "incorrect conclusion for workflow run b")
+	assert.ElementsMatch(t, runs[".github/workflows/c.yml"], []string{"cancelled"}, "incorrect conclusion for workflow run c")
 	assert.Equal(t, 2, runsRule.Count, "incorrect http request count")
 }
 
+func TestLatestStatuses(t *testing.T) {
+	pr := defaultTestPR()
+
+	rp := &ResponsePlayer{}
+	rp.AddRule(
+		ExactPathMatcher("/repos/testorg/testrepo/commits/"+pr.Head.GetSHA()+"/status"),
+		"testdata/responses/combined_status_for_ref.yml",
+	)
+	rp.AddRule(
+		ExactPathMatcher("/repos/testorg/testrepo/commits/"+pr.Head.GetSHA()+"/check-runs"),
+		"testdata/responses/check_runs_for_ref.yml",
+	)
+
+	ctx := makeContext(t, rp, pr, nil)
+	statuses, err := ctx.LatestStatuses()
+	require.NoError(t, err)
+
+	assert.Len(t, statuses, 4, "incorrect number of statuses")
+	assert.Equal(t, statuses["commit-status-a"], "success", "incorrect conclusion for 'commit-status-a' status")
+	assert.Equal(t, statuses["commit-status-b"], "pending", "incorrect conclusion for 'commit-status-a' status")
+	assert.Equal(t, statuses["check-run-a"], "success", "incorrect conclusion for 'check-run-a' status")
+	assert.Equal(t, statuses["check-run-b"], "failure", "incorrect conclusion for 'check-run-b' status")
+}
+
 func makeContext(t *testing.T, rp *ResponsePlayer, pr *github.PullRequest, gc GlobalCache) Context {
 	ctx := context.Background()
 	client := github.NewClient(&http.Client{Transport: rp})
diff --git a/pull/testdata/responses/check_runs_for_ref.yml b/pull/testdata/responses/check_runs_for_ref.yml
new file mode 100644
index 0000000..86ebf6d
--- /dev/null
+++ b/pull/testdata/responses/check_runs_for_ref.yml
@@ -0,0 +1,28 @@
+- status: 200
+  body: |
+    {
+      "total_count": 3,
+      "check_runs": [
+        {
+          "status": "completed",
+          "conclusion": "success",
+          "started_at": "2024-08-14T12:12:45Z",
+          "completed_at": "2024-08-14T12:13:14Z",
+          "name": "check-run-a"
+        },
+        {
+          "status": "completed",
+          "conclusion": "failure",
+          "started_at": "2024-08-14T12:10:00Z",
+          "completed_at": "2024-08-14T12:10:36Z",
+          "name": "check-run-b"
+        },
+        {
+          "status": "completed",
+          "conclusion": "failure",
+          "started_at": "2024-08-14T12:10:00Z",
+          "completed_at": "2024-08-14T12:10:21Z",
+          "name": "check-run-a"
+        }
+      ]
+    }
diff --git a/pull/testdata/responses/combined_status_for_ref.yml b/pull/testdata/responses/combined_status_for_ref.yml
new file mode 100644
index 0000000..05f153b
--- /dev/null
+++ b/pull/testdata/responses/combined_status_for_ref.yml
@@ -0,0 +1,19 @@
+- status: 200
+  body: |
+    {
+      "state": "pending",
+      "statuses": [
+        {
+          "state": "pending",
+          "context": "commit-status-b",
+          "created_at": "2024-08-14T12:13:14Z",
+          "updated_at": "2024-08-14T12:13:14Z"
+        },
+        {
+          "state": "success",
+          "context": "commit-status-a",
+          "created_at": "2024-08-14T12:12:00Z",
+          "updated_at": "2024-08-14T12:12:00Z"
+        }
+      ]
+    }