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
8 changes: 8 additions & 0 deletions src/VCS/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,14 @@ public function createCheckRun(
throw new \Exception('createCheckRun() is not implemented for ' . $this->getName());
}

/**
* Returns the ID of the most recently created check run with the given name on a commit ref, or 0 if none found.
*/
public function getCheckRunByName(string $owner, string $repositoryName, string $ref, string $checkName): int
{
throw new \Exception('getCheckRunByName() is not implemented for ' . $this->getName());
}

/**
* Gets a check run by ID.
*
Expand Down
27 changes: 27 additions & 0 deletions src/VCS/Adapter/Git/GitHub.php
Original file line number Diff line number Diff line change
Expand Up @@ -955,6 +955,33 @@ public function createCheckRun(
return $response['body'] ?? [];
}

public function getCheckRunByName(string $owner, string $repositoryName, string $ref, string $checkName): int
{
$url = "/repos/$owner/$repositoryName/commits/$ref/check-runs";

$response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"], [
'check_name' => $checkName,
'filter' => 'all',
'per_page' => 100,
]);

$responseHeadersStatusCode = $response['headers']['status-code'] ?? 0;
if ($responseHeadersStatusCode === 404) {
return 0;
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.
if ($responseHeadersStatusCode >= 400) {
throw new Exception("Failed to get check run by name: HTTP $responseHeadersStatusCode");
}

$runs = $response['body']['check_runs'] ?? [];
if (empty($runs)) {
return 0;
}

// GitHub IDs are monotonically increasing; the highest ID is the most recently created run.
return (int) max(array_column($runs, 'id'));
}

/**
* Gets a check run by ID.
*
Expand Down
166 changes: 166 additions & 0 deletions tests/VCS/Adapter/GitHubTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -932,6 +932,171 @@ public function testUpdateCheckRunWithMissingConclusion(): void
}
}

public function testGetCheckRunByName(): void
{
$repositoryName = 'test-get-check-run-by-name-' . \uniqid();
$this->vcsAdapter->createRepository(static::$owner, $repositoryName, false);

try {
$this->vcsAdapter->createFile(static::$owner, $repositoryName, 'README.md', '# Test');
$commit = $this->vcsAdapter->getLatestCommit(static::$owner, $repositoryName, static::$defaultBranch);
$commitHash = $commit['commitHash'];

$checkRun = $this->vcsAdapter->createCheckRun(
owner: static::$owner,
repositoryName: $repositoryName,
headSha: $commitHash,
name: 'ci/build',
status: 'in_progress',
);

$foundId = $this->vcsAdapter->getCheckRunByName(
static::$owner,
$repositoryName,
$commitHash,
'ci/build'
);

$this->assertEquals($checkRun['id'], $foundId);
} finally {
$this->vcsAdapter->deleteRepository(static::$owner, $repositoryName);
}
}

public function testGetCheckRunByNameNoMatchReturnsZero(): void
{
// Verifies the check_name filter is actually applied:
// a run with a different name must not be returned.
$repositoryName = 'test-get-check-run-by-name-nomatch-' . \uniqid();
$this->vcsAdapter->createRepository(static::$owner, $repositoryName, false);

try {
$this->vcsAdapter->createFile(static::$owner, $repositoryName, 'README.md', '# Test');
$commit = $this->vcsAdapter->getLatestCommit(static::$owner, $repositoryName, static::$defaultBranch);
$commitHash = $commit['commitHash'];

$this->vcsAdapter->createCheckRun(
owner: static::$owner,
repositoryName: $repositoryName,
headSha: $commitHash,
name: 'ci/build',
status: 'in_progress',
);

$foundId = $this->vcsAdapter->getCheckRunByName(
static::$owner,
$repositoryName,
$commitHash,
'ci/lint' // different name
);

$this->assertEquals(0, $foundId);
} finally {
$this->vcsAdapter->deleteRepository(static::$owner, $repositoryName);
}
}

public function testGetCheckRunByNameNotFoundRepositoryReturnsZero(): void
{
$foundId = $this->vcsAdapter->getCheckRunByName(
static::$owner,
'non-existing-repository-' . \uniqid(),
str_repeat('a', 40),
'ci/build'
);

$this->assertEquals(0, $foundId);
}

public function testGetCheckRunByNameReturnsMostRecent(): void
{
// When a commit has multiple runs with the same name (e.g. retries),
// the most recently created one must be returned.
$repositoryName = 'test-get-check-run-by-name-recent-' . \uniqid();
$this->vcsAdapter->createRepository(static::$owner, $repositoryName, false);

try {
$this->vcsAdapter->createFile(static::$owner, $repositoryName, 'README.md', '# Test');
$commit = $this->vcsAdapter->getLatestCommit(static::$owner, $repositoryName, static::$defaultBranch);
$commitHash = $commit['commitHash'];

$first = $this->vcsAdapter->createCheckRun(
owner: static::$owner,
repositoryName: $repositoryName,
headSha: $commitHash,
name: 'ci/build',
status: 'in_progress',
);

$second = $this->vcsAdapter->createCheckRun(
owner: static::$owner,
repositoryName: $repositoryName,
headSha: $commitHash,
name: 'ci/build',
status: 'in_progress',
);

$this->assertGreaterThan($first['id'], $second['id']);
Comment thread
greptile-apps[bot] marked this conversation as resolved.

$foundId = $this->vcsAdapter->getCheckRunByName(
static::$owner,
$repositoryName,
$commitHash,
'ci/build'
);

$this->assertEquals($second['id'], $foundId);
} finally {
$this->vcsAdapter->deleteRepository(static::$owner, $repositoryName);
}
}

public function testGetCheckRunByNameThenUpdate(): void
{
// End-to-end: create as in_progress, look up by name, update to completed.
// This is the exact workflow the method was designed for — no stored ID needed.
$repositoryName = 'test-get-check-run-by-name-update-' . \uniqid();
$this->vcsAdapter->createRepository(static::$owner, $repositoryName, false);

try {
$this->vcsAdapter->createFile(static::$owner, $repositoryName, 'README.md', '# Test');
$commit = $this->vcsAdapter->getLatestCommit(static::$owner, $repositoryName, static::$defaultBranch);
$commitHash = $commit['commitHash'];

$this->vcsAdapter->createCheckRun(
owner: static::$owner,
repositoryName: $repositoryName,
headSha: $commitHash,
name: 'ci/build',
status: 'in_progress',
);

$checkRunId = $this->vcsAdapter->getCheckRunByName(
static::$owner,
$repositoryName,
$commitHash,
'ci/build'
);

$this->assertGreaterThan(0, $checkRunId);

$updated = $this->vcsAdapter->updateCheckRun(
owner: static::$owner,
repositoryName: $repositoryName,
checkRunId: $checkRunId,
conclusion: 'success',
title: 'Build succeeded.',
summary: 'All steps passed.',
);

$this->assertEquals($checkRunId, $updated['id']);
$this->assertEquals('completed', $updated['status']);
$this->assertEquals('success', $updated['conclusion']);
} finally {
$this->vcsAdapter->deleteRepository(static::$owner, $repositoryName);
}
}

public function testGenerateCloneCommand(): void
{
$repositoryName = 'test-clone-command-' . \uniqid();
Expand Down Expand Up @@ -1112,4 +1277,5 @@ public function testUpdateComment(): void
{
$this->markTestSkipped('Requires existing PR — createPullRequest not implemented in GitHub adapter');
}

}
Loading