Skip to content

Commit

Permalink
Let code sniffer do a review
Browse files Browse the repository at this point in the history
Github API changes forces us to place comments on pull request instead of commits which we did previously.
So converted code sniffer to place a review when it fails and place comments on Pull Request lines when they are inside the diff otherwise we just place it in main comment.
  • Loading branch information
GeoffreyDijkstra committed Feb 7, 2022
1 parent fba15ae commit fd3d0b4
Show file tree
Hide file tree
Showing 3 changed files with 183 additions and 25 deletions.
3 changes: 2 additions & 1 deletion src/Commands/CodeOwner.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ protected function execute(InputInterface $input, OutputInterface $output): int

$this->apiCommands->addPullRequestReview(
'Pull request automatically approved, pull request is done by a code owner.',
'APPROVE'
'APPROVE',
$this->config->sha()
);

return 0;
Expand Down
132 changes: 121 additions & 11 deletions src/Commands/CodeSniffer.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Services\GithubActionConfig;
use App\Services\GithubApiCommands;
use RuntimeException;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
Expand Down Expand Up @@ -59,43 +60,152 @@ protected function execute(InputInterface $input, OutputInterface $output): int
{
$path = getcwd() . '/vendor/bin/phpcs';
if (!file_exists($path)) {
$output->writeln('PHPCS not found.');

return 1;
}

exec($path . ' --report-json', $json, $code);
if ($code !== 0 && empty($json)) {
$output->writeln('No json generated');
$output->writeln('No json generated.');

return 1;
}

$json = json_decode(implode('', $json), true);
if (json_last_error() !== JSON_ERROR_NONE) {
$output->writeln('Json Error:' . json_last_error_msg());
$output->writeln('Json Error: ' . json_last_error_msg());

return 1;
}

$code = 0;
$diff = $this->getDiff();

$comments = [];
$body = '';
foreach ($json['files'] as $file => $results) {
if (empty($results['messages'])) {
continue;
}

$code = 1;
$file = ltrim(str_replace(getcwd(), '', $file), '/');

foreach ($results['messages'] as $message) {
$this->apiCommands->placeCommitComment(
$this->config->sha(),
$message['message'],
$file,
$message['line']
);
$output->writeln('File: ' . $file);
$output->writeln('Line: ' . $message['line']);
$output->writeln('Message: ' . $message['message']);
$output->writeln('');

if (isset($diff[$file][$message['line']])) {
$comments[] = [
'path' => $file,
'position' => $diff[$file][$message['line']],
'body' => $message['message']
];
} else {
if (empty($body)) {
$body = "Problematic files found outside Pull Request diff:\n\n";
}

$body .= sprintf(
"%s in [%s on line %s](%s)\n\n",
$message['message'],
$file,
$message['line'],
sprintf(
'https://github.com/%s/blob/%s/%s#L%s',
$this->config->repository(),
$this->config->headRef(),
$file,
$message['line']
)
);
}
}
}

return $code;
if (!empty($comments) || !empty($body)) {
$this->apiCommands->addPullRequestReview(
"CodeSniffer found some issues, please check your code.\n\n$body",
'REQUEST_CHANGES',
$this->config->sha(),
$comments
);

return 1;
}

return 0;
}

/**
* @return array
*/
protected function getDiff(): array
{
$output = $this->apiCommands->getPullRequestDiff();
$output = explode("\n", $output);

$files = [];
$lastFile = '';
foreach ($output as $line) {
if (strpos($line, 'diff --git') !== false) {
$lastFile = explode(' b/', $line);
$lastFile = array_pop($lastFile);
continue;
}

$files[$lastFile][] = $line;
}

// Remove deleted files from diff.
$files = array_filter($files, function (array $diff) {
return strpos($diff[0], 'deleted file') === false;
});

return array_map(
function (array $diff): array {
$results = [];

$index = preg_grep('/^@@[-+,0-9 ]+@@/', $diff);
for ($i = 0; $i < key($index); $i++) {
unset($diff[$i]);
}

$position = 0;
$currentLine = 0;
foreach ($diff as $line) {
if (1 === preg_match('/^@@ [^\s]+ \+([0-9]+),[0-9]+ @@/', $line, $matches)) {
$currentLine = (int)$matches[1];
$position++;
continue;
}

switch (substr($line, 0, 1)) {
case ' ':
case '+':
$results[$currentLine] = $position;
$currentLine++;
$position++;
break;
case '-':
$position++;
break;
case '':
// Do nothing.
break;
default:
throw new RuntimeException('Something funky happend...');
}
}

if (empty($results)) {
throw new RuntimeException('Something funky happend...');
}

return $results;
},
$files
);
}
}
73 changes: 60 additions & 13 deletions src/Services/GithubApiCommands.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,18 +101,22 @@ public function getCommitComments(string $commitHash): array
}

/**
* @param string $commitHash
* @param string $comment
* @param string|null $path
* @param int|null $line
* @param string $commitHash Full commit hash (40 characters).
* @param string $comment Comment you want to place, use \n for multi line and markup is possible.
* @param string|null $path Leave empty for a global commit comment, otherwise place it on file in given commit.
* @param int $position Git diff position.
*/
public function placeCommitComment(string $commitHash, string $comment, string $path = null, int $line = null): void
{
$data = ['body' => $comment];
if (empty($path)) {
$data['position'] = 0;
} else {
$data['line'] = $line;
public function placeCommitComment(
string $commitHash,
string $comment,
string $path = null,
int $position = 0
): void {
$data = [
'body' => $comment,
'position' => $position
];
if (!empty($path)) {
$data['path'] = $path;
}

Expand Down Expand Up @@ -330,14 +334,21 @@ public function setAssignee(string ...$assignee): void
/**
* @param string $message
* @param string $event APPROVE, REQUEST_CHANGES or COMMENT
* @param string $commitHash
* @param array $comments
*/
public function addPullRequestReview(string $message, string $event): void
public function addPullRequestReview(string $message, string $event, string $commitHash, array $comments = []): void
{
$event = strtoupper($event);
if (!in_array($event, ['APPROVE', 'REQUEST_CHANGES', 'COMMENT'], true)) {
throw new InvalidArgumentException('Wrong event type.');
}

$data = ['body' => $message, 'event' => $event, 'commit_id' => $commitHash];
if (!empty($comments)) {
$data['comments'] = $comments;
}

$curl = curl_init(sprintf(
'%s/repos/%s/pulls/%s/reviews',
$this->config->apiUrl(),
Expand All @@ -349,7 +360,7 @@ public function addPullRequestReview(string $message, string $event): void
[
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => json_encode(['body' => $message, 'event' => $event]),
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => [
'Accept: application/vnd.github.v3+json',
'Content-Type: application/json',
Expand All @@ -361,4 +372,40 @@ public function addPullRequestReview(string $message, string $event): void
curl_exec($curl);
curl_close($curl);
}

/**
* @return string
*/
public function getPullRequestDiff(): string
{
$curl = curl_init(sprintf(
'%s/repos/%s/pulls/%s',
$this->config->apiUrl(),
$this->config->repository(),
$this->config->pullRequestNumber()
));
curl_setopt_array(
$curl,
[
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_MAXREDIRS => 3,
CURLOPT_HTTPHEADER => [
'Accept: application/vnd.github.v3.diff',
'Authorization: Token ' . $this->config->token(),
'User-Agent: ' . $this->config->actor()
]
]
);
$response = curl_exec($curl);
$info = curl_getinfo($curl);
curl_close($curl);

if ($info['http_code'] !== 200) {
throw new RuntimeException('Could not get pull request diff.');
}

return $response;
}
}

0 comments on commit fd3d0b4

Please sign in to comment.