Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

### Fixed

- `pr approve-align-files`: also evaluate GitHub Actions check_runs when the legacy commit status (e.g. CircleCI) is `success`, so a failing Actions check such as `pre-commit` no longer slips through and gets approved.

## [7.40.0] - 2026-04-23

### Added
Expand Down
36 changes: 20 additions & 16 deletions cmd/pr/approvealign/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,24 +203,28 @@ func (r *runner) processPR(ctx context.Context, githubClient *github.Client, ps
}
}

// Check individual check runs (GitHub Actions checks)
// Check individual check runs (GitHub Actions checks).
//
// combinedStatus only reflects legacy commit statuses (e.g. CircleCI's
// "ci/circleci: ..."). GitHub Actions report via the check_runs API
// instead, so a "success" combinedStatus does NOT imply that all
// GitHub Actions checks have passed. Always evaluate check_runs so a
// failing Actions check (e.g. "pre-commit") is not silently ignored
// when CircleCI is green.
if checkRuns != nil && len(checkRuns.CheckRuns) > 0 && !hasFailedChecks {
// Only check runs if combinedStatus didn't already give us a definitive answer
if combinedStatus == nil || (combinedStatus.GetState() != "success" && combinedStatus.GetState() != "failure") {
for _, run := range checkRuns.CheckRuns {
conclusion := run.GetConclusion()
status := run.GetStatus()

if status == "completed" {
if conclusion == "failure" || conclusion == "cancelled" || conclusion == "timed_out" {
hasFailedChecks = true
break
}
// success, neutral, skipped are OK
} else {
// Check is not completed (queued, in_progress)
checksPending = true
for _, run := range checkRuns.CheckRuns {
conclusion := run.GetConclusion()
status := run.GetStatus()

if status == "completed" {
if conclusion == "failure" || conclusion == "cancelled" || conclusion == "timed_out" {
hasFailedChecks = true
break
}
// success, neutral, skipped are OK
} else {
// Check is not completed (queued, in_progress)
checksPending = true
}
}
}
Expand Down