Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
27df5ff
feat: add createCheckRun for Check Runs API
HarshMN2345 May 26, 2026
530dc9b
test: add testCreateCheckRun for GitHub Check Runs API
HarshMN2345 May 26, 2026
ef4d594
fix: add completed_at and remove stale from valid conclusions
HarshMN2345 May 26, 2026
0c40bfa
fix: declare createCheckRun on base Adapter with not-implemented default
HarshMN2345 May 26, 2026
83b83d6
fix: use gmdate for UTC timestamp in createCheckRun
HarshMN2345 May 26, 2026
f5fe277
feat: expand createCheckRun params, add getCheckRun, improve test ass…
HarshMN2345 May 26, 2026
d733ddf
refactor: replace if-empty blocks with array_filter in createCheckRun
HarshMN2345 May 26, 2026
7de3f13
feat: add annotations and images params to createCheckRun output
HarshMN2345 May 26, 2026
bd9164d
feat: add updateCheckRun method and test
HarshMN2345 May 26, 2026
3f51e43
refactor: extract buildCheckRunOutput helper, fix split docblock on c…
HarshMN2345 May 26, 2026
2f5683f
refactor: inline output block, remove unnecessary helper
HarshMN2345 May 26, 2026
49dc894
fix: enforce conclusion implies completed status and completed_at; re…
HarshMN2345 May 26, 2026
ccb0e8e
refactor: createCheckRun for initial status only, conclusion via upda…
HarshMN2345 May 26, 2026
460cd88
feat: add conclusion/completedAt to createCheckRun; expand tests
HarshMN2345 May 28, 2026
21015e9
fix: sync createCheckRun signature in base Adapter with GitHub implem…
HarshMN2345 May 28, 2026
6ba62a1
fix: throw exception on HTTP errors in createCheckRun and getCheckRun
HarshMN2345 May 28, 2026
29e943b
fix: throw on HTTP errors in updateCheckRun; add failure tests for up…
HarshMN2345 May 28, 2026
ef78fe2
fix: guard against status=completed without conclusion in createCheck…
HarshMN2345 May 28, 2026
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
72 changes: 72 additions & 0 deletions src/VCS/Adapter.php
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,78 @@ abstract public function listBranches(string $owner, string $repositoryName): ar
*/
abstract public function updateCommitStatus(string $repositoryName, string $SHA, string $owner, string $state, string $description = '', string $target_url = '', string $context = ''): void;

/**
* Creates a check run for a commit.
* status can be one of: queued, in_progress
* Use updateCheckRun() to set conclusion and mark the run as completed.
*
* @param array<mixed> $annotations
* @param array<mixed> $images
* @param array<mixed> $actions
* @return array<mixed>
*/
public function createCheckRun(
string $owner,
string $repositoryName,
string $headSha,
string $name,
string $status = 'queued',
string $conclusion = '',
string $title = '',
string $summary = '',
string $text = '',
array $annotations = [],
array $images = [],
array $actions = [],
string $detailsUrl = '',
string $externalId = '',
string $startedAt = '',
string $completedAt = '',
): array {
throw new \Exception('createCheckRun() is not implemented for ' . $this->getName());
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.

/**
* Gets a check run by ID.
*
* @return array<mixed>
*/
public function getCheckRun(string $owner, string $repositoryName, int $checkRunId): array
{
throw new \Exception('getCheckRun() is not implemented for ' . $this->getName());
}

/**
* Updates an existing check run.
* status can be one of: queued, in_progress, completed
* conclusion (required when status=completed) can be one of: action_required, cancelled, failure, neutral, success, skipped, timed_out
*
* @param array<mixed> $annotations
* @param array<mixed> $images
* @param array<mixed> $actions
* @return array<mixed>
*/
public function updateCheckRun(
string $owner,
string $repositoryName,
int $checkRunId,
string $name = '',
string $status = '',
string $conclusion = '',
string $title = '',
string $summary = '',
string $text = '',
array $annotations = [],
array $images = [],
array $actions = [],
string $detailsUrl = '',
string $externalId = '',
string $startedAt = '',
string $completedAt = '',
): array {
throw new \Exception('updateCheckRun() is not implemented for ' . $this->getName());
}

/**
* Get repository tree
*
Expand Down
179 changes: 179 additions & 0 deletions src/VCS/Adapter/Git/GitHub.php
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,185 @@ public function updateCommitStatus(string $repositoryName, string $commitHash, s
$this->call(self::METHOD_POST, $url, ['Authorization' => "Bearer $this->accessToken"], $body);
}

/**
* Creates a check run for a commit.
* status can be one of: queued, in_progress, completed
* conclusion (required when status=completed) can be one of: action_required, cancelled, failure, neutral, success, skipped, timed_out
*
* @param array<mixed> $annotations
* @param array<mixed> $images
* @param array<mixed> $actions
* @return array<mixed>
*/
public function createCheckRun(
string $owner,
string $repositoryName,
string $headSha,
string $name,
string $status = 'queued',
string $conclusion = '',
string $title = '',
string $summary = '',
string $text = '',
array $annotations = [],
array $images = [],
array $actions = [],
string $detailsUrl = '',
string $externalId = '',
string $startedAt = '',
string $completedAt = '',
): array {
$url = "/repos/$owner/$repositoryName/check-runs";
Comment thread
Meldiron marked this conversation as resolved.
Comment thread
greptile-apps[bot] marked this conversation as resolved.

if ($status === 'completed' && empty($conclusion)) {
throw new Exception("conclusion is required when status is 'completed'");
}

// Conclusion requires status=completed; auto-set completed_at if not provided.
if (!empty($conclusion)) {
Comment thread
greptile-apps[bot] marked this conversation as resolved.
$status = 'completed';
if (empty($completedAt)) {
$completedAt = gmdate('Y-m-d\TH:i:s\Z');
}
}

$body = array_merge(
[
'name' => $name,
'head_sha' => $headSha,
'status' => $status,
],
array_filter([
'conclusion' => $conclusion,
'completed_at' => $completedAt,
'details_url' => $detailsUrl,
'external_id' => $externalId,
'started_at' => $startedAt,
], fn ($value) => !empty($value))
);

// Output requires both title and summary.
if (!empty($title) && !empty($summary)) {
$output = array_filter(['title' => $title, 'summary' => $summary, 'text' => $text], fn ($value) => !empty($value));
if (!empty($annotations)) {
$output['annotations'] = $annotations;
}
if (!empty($images)) {
$output['images'] = $images;
}
$body['output'] = $output;
}

if (!empty($actions)) {
$body['actions'] = $actions;
}

$response = $this->call(self::METHOD_POST, $url, ['Authorization' => "Bearer $this->accessToken"], $body);

$responseHeadersStatusCode = $response['headers']['status-code'] ?? 0;
if ($responseHeadersStatusCode >= 400) {
throw new Exception("Failed to create check run: HTTP $responseHeadersStatusCode");
}

return $response['body'] ?? [];
}

/**
* Gets a check run by ID.
*
* @return array<mixed>
*/
public function getCheckRun(string $owner, string $repositoryName, int $checkRunId): array
{
$url = "/repos/$owner/$repositoryName/check-runs/$checkRunId";

$response = $this->call(self::METHOD_GET, $url, ['Authorization' => "Bearer $this->accessToken"]);

$responseHeadersStatusCode = $response['headers']['status-code'] ?? 0;
if ($responseHeadersStatusCode >= 400) {
throw new Exception("Failed to get check run $checkRunId: HTTP $responseHeadersStatusCode");
}

return $response['body'] ?? [];
}

/**
* Updates an existing check run.
* status can be one of: queued, in_progress, completed
* conclusion (required when status=completed) can be one of: action_required, cancelled, failure, neutral, success, skipped, timed_out
*
* @param array<mixed> $annotations
* @param array<mixed> $images
* @return array<mixed>
*/
public function updateCheckRun(
string $owner,
string $repositoryName,
int $checkRunId,
string $name = '',
string $status = '',
string $conclusion = '',
string $title = '',
string $summary = '',
string $text = '',
array $annotations = [],
array $images = [],
array $actions = [],
string $detailsUrl = '',
string $externalId = '',
string $startedAt = '',
string $completedAt = '',
): array {
$url = "/repos/$owner/$repositoryName/check-runs/$checkRunId";

if ($status === 'completed' && empty($conclusion)) {
throw new Exception("conclusion is required when status is 'completed'");
}

// Conclusion requires status=completed; auto-set completed_at if not provided.
if (!empty($conclusion)) {
$status = 'completed';
if (empty($completedAt)) {
$completedAt = gmdate('Y-m-d\TH:i:s\Z');
}
}

$body = array_filter([
'name' => $name,
'status' => $status,
'details_url' => $detailsUrl,
'external_id' => $externalId,
'started_at' => $startedAt,
'conclusion' => $conclusion,
'completed_at' => $completedAt,
], fn ($value) => !empty($value));

// Output requires both title and summary.
if (!empty($title) && !empty($summary)) {
$output = array_filter(['title' => $title, 'summary' => $summary, 'text' => $text], fn ($value) => !empty($value));
if (!empty($annotations)) {
$output['annotations'] = $annotations;
}
if (!empty($images)) {
$output['images'] = $images;
}
$body['output'] = $output;
}

if (!empty($actions)) {
$body['actions'] = $actions;
}

$response = $this->call(self::METHOD_PATCH, $url, ['Authorization' => "Bearer $this->accessToken"], $body);

$responseHeadersStatusCode = $response['headers']['status-code'] ?? 0;
if ($responseHeadersStatusCode >= 400) {
throw new Exception("Failed to update check run $checkRunId: HTTP $responseHeadersStatusCode");
}

return $response['body'] ?? [];
}
Comment thread
greptile-apps[bot] marked this conversation as resolved.

/**
* Generates a clone command using app access token
*/
Expand Down
Loading
Loading