Summary
Implement check runs and status API for querying CI/CD pipeline results and PR status checks.
Acceptance Criteria
Check Run Query Interface
namespace ConduitUI\Pr\Contracts;
interface CheckRunQueryInterface
{
public function get(): Collection;
public function wherePassing(): Collection;
public function whereFailing(): Collection;
public function wherePending(): Collection;
public function whereNeutral(): Collection;
public function whereSkipped(): Collection;
public function latest(): ?CheckRun;
public function byName(string $name): ?CheckRun;
public function summary(): CheckSummary;
}
Check Run Query Implementation
namespace ConduitUI\Pr\Services;
use ConduitUI\Pr\Contracts\CheckRunQueryInterface;
use ConduitUI\Pr\Data\CheckRun;
use ConduitUI\Pr\Data\CheckSummary;
final class CheckRunQuery implements CheckRunQueryInterface
{
public function __construct(
protected GitHub $github,
protected string $fullName,
protected string $ref, // SHA or branch name
) {}
public function get(): Collection
{
$response = $this->github->get(
"/repos/{$this->fullName}/commits/{$this->ref}/check-runs"
);
return collect($response->json('check_runs'))
->map(fn($check) => CheckRun::fromArray($check));
}
public function wherePassing(): Collection
{
return $this->get()
->where('conclusion', 'success');
}
public function whereFailing(): Collection
{
return $this->get()
->whereIn('conclusion', ['failure', 'timed_out', 'action_required']);
}
public function wherePending(): Collection
{
return $this->get()
->whereNull('conclusion')
->where('status', 'in_progress');
}
public function whereNeutral(): Collection
{
return $this->get()
->where('conclusion', 'neutral');
}
public function whereSkipped(): Collection
{
return $this->get()
->where('conclusion', 'skipped');
}
public function latest(): ?CheckRun
{
return $this->get()
->sortByDesc('completed_at')
->first();
}
public function byName(string $name): ?CheckRun
{
return $this->get()
->firstWhere('name', $name);
}
public function summary(): CheckSummary
{
$checks = $this->get();
return new CheckSummary(
total: $checks->count(),
passing: $checks->where('conclusion', 'success')->count(),
failing: $checks->whereIn('conclusion', ['failure', 'timed_out'])->count(),
pending: $checks->whereNull('conclusion')->count(),
neutral: $checks->where('conclusion', 'neutral')->count(),
skipped: $checks->where('conclusion', 'skipped')->count(),
);
}
}
Check Run DTO
namespace ConduitUI\Pr\Data;
final readonly class CheckRun
{
public function __construct(
public int $id,
public string $name,
public string $status, // queued | in_progress | completed
public ?string $conclusion, // success | failure | neutral | cancelled | skipped | timed_out | action_required
public string $headSha,
public ?string $detailsUrl,
public ?string $htmlUrl,
public ?Carbon $startedAt,
public ?Carbon $completedAt,
) {}
public static function fromArray(array $data): self;
public function isPassing(): bool
{
return $this->conclusion === 'success';
}
public function isFailing(): bool
{
return in_array($this->conclusion, ['failure', 'timed_out', 'action_required']);
}
public function isPending(): bool
{
return $this->status === 'in_progress' && $this->conclusion === null;
}
public function isCompleted(): bool
{
return $this->status === 'completed';
}
}
Check Summary DTO
namespace ConduitUI\Pr\Data;
final readonly class CheckSummary
{
public function __construct(
public int $total,
public int $passing,
public int $failing,
public int $pending,
public int $neutral,
public int $skipped,
) {}
public function allPassing(): bool
{
return $this->total > 0
&& $this->failing === 0
&& $this->pending === 0;
}
public function anyFailing(): bool
{
return $this->failing > 0;
}
public function isComplete(): bool
{
return $this->pending === 0;
}
public function passRate(): float
{
if ($this->total === 0) {
return 0;
}
return round(($this->passing / $this->total) * 100, 2);
}
}
Integration with PullRequest DTO
// Add to PullRequest DTO
public function checks(): CheckRunQuery
{
return new CheckRunQuery(
$this->github,
$this->fullName,
$this->headSha
);
}
public function checksPass(): bool
{
return $this->checks()->summary()->allPassing();
}
public function checksFail(): bool
{
return $this->checks()->summary()->anyFailing();
}
public function checksPending(): bool
{
return !$this->checks()->summary()->isComplete();
}
Usage Examples
Query All Checks
$pr = PullRequests::find('owner/repo', 123);
// Get all checks
$checks = $pr->checks()->get();
// Filter checks
$passing = $pr->checks()->wherePassing();
$failing = $pr->checks()->whereFailing();
$pending = $pr->checks()->wherePending();
// Get specific check
$ciCheck = $pr->checks()->byName('CI');
Check Summary
$summary = $pr->checks()->summary();
echo "Total: {$summary->total}";
echo "Passing: {$summary->passing}";
echo "Failing: {$summary->failing}";
echo "Pass Rate: {$summary->passRate()}%";
if ($summary->allPassing()) {
echo "All checks passing!";
}
Conditional Operations Based on Checks
$pr = PullRequests::find('owner/repo', 123);
if ($pr->checksPass()) {
$pr->approve('Checks passing, approving')->submit();
}
if ($pr->checksFail()) {
$failing = $pr->checks()->whereFailing();
$message = "Failing checks:\n";
foreach ($failing as $check) {
$message .= "- {$check->name}\n";
}
$pr->comment($message);
}
Wait for Checks to Complete
use Illuminate\Support\Sleep;
$pr = PullRequests::find('owner/repo', 123);
while ($pr->checksPending()) {
Sleep::for(30)->seconds();
$pr = $pr->fresh(); // Refresh PR data
}
if ($pr->checksPass()) {
$pr->merge();
}
Auto-merge When Checks Pass
PullRequests::forRepo('owner/repo')
->whereOpen()
->whereLabel('auto-merge')
->get()
->filter(fn($pr) => $pr->checksPass() && $pr->isApproved())
->each(fn($pr) => $pr->merge());
Testing Requirements
it('gets all check runs')
->expect(fn() =>
PullRequests::find('test/repo', 1)->checks()->get()
)->toBeInstanceOf(Collection::class);
it('filters passing checks')
->expect(fn() =>
PullRequests::find('test/repo', 1)->checks()->wherePassing()
)->toBeInstanceOf(Collection::class);
it('generates check summary')
->expect(fn() =>
PullRequests::find('test/repo', 1)->checks()->summary()
)->toBeInstanceOf(CheckSummary::class);
it('determines if all checks pass')
->expect(fn() =>
PullRequests::find('test/repo', 1)->checksPass()
)->toBeBool();
Dependencies
References
Labels
Summary
Implement check runs and status API for querying CI/CD pipeline results and PR status checks.
Acceptance Criteria
Check Run Query Interface
Check Run Query Implementation
Check Run DTO
Check Summary DTO
Integration with PullRequest DTO
Usage Examples
Query All Checks
Check Summary
Conditional Operations Based on Checks
Wait for Checks to Complete
Auto-merge When Checks Pass
Testing Requirements
Dependencies
References
Labels