Skip to content

Commit a6090fc

Browse files
Lukas Hettwergrogy
Lukas Hettwer
authored andcommitted
Extend by the Code Climate output format
The Code Climate format is used in the GitLab CI/CD to show errors in the merge request page. The simple JSON format is not sufficient for this.
1 parent 2c07745 commit a6090fc

File tree

7 files changed

+187
-0
lines changed

7 files changed

+187
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ It is strongly recommended for existing users of the (unmaintained)
7575
- `--no-progress` Disable progress in console output.
7676
- `--checkstyle` Output results as Checkstyle XML.
7777
- `--json` Output results as JSON string (requires PHP 5.4).
78+
- `--gitlab` Output results for the GitLab Code Quality widget (requires PHP 5.4), see more in [Code Quality](https://docs.gitlab.com/ee/user/project/merge_requests/code_quality.html) documentation.
7879
- `--blame` Try to show git blame for row with error.
7980
- `--git <git>` Path to Git executable to show blame message (default: 'git').
8081
- `--stdin` Load files and folder to test from standard input.

src/Application.php

+1
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ private function showOptions()
8787
--no-colors Disable colors in console output.
8888
--no-progress Disable progress in console output.
8989
--json Output results as JSON string.
90+
--gitlab Output results for the GitLab Code Quality Widget.
9091
--checkstyle Output results as Checkstyle XML.
9192
--blame Try to show git blame for row with error.
9293
--git <git> Path to Git executable to show blame message (default: 'git').

src/Manager.php

+2
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ protected function getDefaultOutput(Settings $settings)
8282
switch ($settings->format) {
8383
case Settings::FORMAT_JSON:
8484
return new JsonOutput($writer);
85+
case Settings::FORMAT_GITLAB:
86+
return new GitLabOutput($writer);
8587
case Settings::FORMAT_CHECKSTYLE:
8688
return new CheckstyleOutput($writer);
8789
}

src/Output.php

+75
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,81 @@ public function writeResult(Result $result, ErrorFormatter $errorFormatter, $ign
8585
}
8686
}
8787

88+
class GitLabOutput implements Output
89+
{
90+
/** @var IWriter */
91+
protected $writer;
92+
93+
/**
94+
* @param IWriter $writer
95+
*/
96+
public function __construct(IWriter $writer)
97+
{
98+
$this->writer = $writer;
99+
}
100+
101+
public function ok()
102+
{
103+
104+
}
105+
106+
public function skip()
107+
{
108+
109+
}
110+
111+
public function error()
112+
{
113+
114+
}
115+
116+
public function fail()
117+
{
118+
119+
}
120+
121+
public function setTotalFileCount($count)
122+
{
123+
124+
}
125+
126+
public function writeHeader($phpVersion, $parallelJobs, $hhvmVersion = null)
127+
{
128+
129+
}
130+
131+
public function writeResult(Result $result, ErrorFormatter $errorFormatter, $ignoreFails)
132+
{
133+
$errors = array();
134+
foreach ($result->getErrors() as $error) {
135+
$message = $error->getMessage();
136+
$line = 1;
137+
if ($error instanceof SyntaxError) {
138+
$line = $error->getLine();
139+
}
140+
$filePath = $error->getFilePath();
141+
$result = array(
142+
'type' => 'issue',
143+
'check_name' => 'Parse error',
144+
'description' => $message,
145+
'categories' => 'Style',
146+
'fingerprint' => md5($filePath . $message . $line),
147+
'severity' => 'minor',
148+
'location' => array(
149+
'path' => $filePath,
150+
'lines' => array(
151+
'begin' => $line,
152+
),
153+
),
154+
);
155+
array_push($errors, $result);
156+
}
157+
158+
$string = json_encode($errors) . PHP_EOL;
159+
$this->writer->write($string);
160+
}
161+
}
162+
88163
class TextOutput implements Output
89164
{
90165
const TYPE_DEFAULT = 'default',

src/Settings.php

+5
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ class Settings
1313

1414
const FORMAT_TEXT = 'text';
1515
const FORMAT_JSON = 'json';
16+
const FORMAT_GITLAB = 'gitlab';
1617
const FORMAT_CHECKSTYLE = 'checkstyle';
1718

1819
/**
@@ -179,6 +180,10 @@ public static function parseArguments(array $arguments)
179180
$settings->format = self::FORMAT_JSON;
180181
break;
181182

183+
case '--gitlab':
184+
$settings->format = self::FORMAT_GITLAB;
185+
break;
186+
182187
case '--checkstyle':
183188
$settings->format = self::FORMAT_CHECKSTYLE;
184189
break;

tests/Output.phpt

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
<?php
2+
3+
/**
4+
* @testCase
5+
*/
6+
7+
require __DIR__ . '/../vendor/autoload.php';
8+
9+
use JakubOnderka\PhpParallelLint\ErrorFormatter;
10+
use JakubOnderka\PhpParallelLint\GitLabOutput;
11+
use JakubOnderka\PhpParallelLint\IWriter;
12+
use JakubOnderka\PhpParallelLint\Result;
13+
use JakubOnderka\PhpParallelLint\SyntaxError;
14+
use Tester\Assert;
15+
16+
class OutputTest extends Tester\TestCase
17+
{
18+
/**
19+
* @dataProvider getGitLabOutputData
20+
*/
21+
public function testGitLabOutput($errors)
22+
{
23+
$result = new Result($errors, array(), array(), 0);
24+
$writer = new TestWriter();
25+
$output = new GitLabOutput($writer);
26+
27+
$output->writeResult($result, new ErrorFormatter(), true);
28+
29+
$result = (array) json_decode($writer->getLogs());
30+
31+
for ($i = 0; $i < count($result) && $i < count($errors); $i++) {
32+
$message = $errors[$i]->getMessage();
33+
$filePath = $errors[$i]->getFilePath();
34+
$line = 1;
35+
if ($errors[$i] instanceof SyntaxError) {
36+
$line = $errors[$i]->getLine();
37+
}
38+
Assert::equal($result[$i]->type, 'issue');
39+
Assert::equal($result[$i]->check_name, 'Parse error');
40+
Assert::equal($result[$i]->categories, 'Style');
41+
Assert::equal($result[$i]->severity, 'minor');
42+
Assert::equal($result[$i]->description, $message);
43+
Assert::equal($result[$i]->fingerprint, md5($filePath . $message . $line));
44+
Assert::equal($result[$i]->location->path, $filePath);
45+
Assert::equal($result[$i]->location->lines->begin, $line);
46+
}
47+
}
48+
49+
public function getGitLabOutputData()
50+
{
51+
return array(
52+
array(
53+
'errors' => array()
54+
),
55+
array(
56+
'errors' => array(
57+
new SyntaxError('foo/bar.php', "Parse error: syntax error, unexpected in foo/bar.php on line 52")
58+
)
59+
),
60+
array(
61+
'errors' => array(
62+
new JakubOnderka\PhpParallelLint\Error('foo/bar.php', "PHP Parse error: syntax error, unexpected ';'")
63+
)
64+
),
65+
array(
66+
'errors' => array(
67+
new SyntaxError('foo/bar.php', "Parse error: syntax error, unexpected in foo/bar.php on line 52"),
68+
new JakubOnderka\PhpParallelLint\Error('foo/bar.php', "PHP Parse error: syntax error, unexpected ';'")
69+
)
70+
),
71+
);
72+
}
73+
}
74+
75+
class TestWriter implements IWriter
76+
{
77+
/** @var string */
78+
protected $logs = "";
79+
80+
/**
81+
* @param string $string
82+
*/
83+
public function write($string)
84+
{
85+
$this->logs .= $string;
86+
}
87+
88+
public function getLogs()
89+
{
90+
return $this->logs;
91+
}
92+
}
93+
94+
$testCase = new OutputTest;
95+
$testCase->run();

tests/Settings.parseArguments.phpt

+8
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,14 @@ class SettingsParseArgumentsTest extends Tester\TestCase
103103
Assert::equal(Settings::FORMAT_JSON, $settings->format);
104104
}
105105

106+
public function testGitLabOutput()
107+
{
108+
$commandLine = './parallel-lint --gitlab .';
109+
$argv = explode(" ", $commandLine);
110+
$settings = Settings::parseArguments($argv);
111+
Assert::equal(Settings::FORMAT_GITLAB, $settings->format);
112+
}
113+
106114
public function testCheckstyleOutput()
107115
{
108116
$commandLine = './parallel-lint --checkstyle .';

0 commit comments

Comments
 (0)