Migrating PHPMD to use XML report format and add a UI plugin to display warning information. See #305

This commit is contained in:
Dan Cryer 2014-04-25 11:28:27 +00:00
parent 71d86bd346
commit 66bfcea8ed
8 changed files with 184 additions and 14 deletions

View file

@ -258,6 +258,11 @@ class Builder implements LoggerAwareInterface
return $this->commandExecutor->getLastOutput();
}
public function logExecOutput($enableLog = true)
{
$this->commandExecutor->logExecOutput = $enableLog;
}
/**
* Find a binary required by a plugin.
* @param $binary

View file

@ -25,6 +25,9 @@ class CommandExecutor
protected $lastOutput;
public $logExecOutput = true;
/**
* The path which findBinary will look in.
* @var string
@ -80,7 +83,7 @@ class CommandExecutor
$lastOutput = trim($lastOutput, '"');
}
if (!empty($this->lastOutput) && ($this->verbose|| $status != 0)) {
if ($this->logExecOutput && !empty($this->lastOutput) && ($this->verbose|| $status != 0)) {
$this->logger->log($this->lastOutput);
}

View file

@ -157,4 +157,9 @@ class Build extends BuildBase
return $config;
}
public function getFileLink($file, $line = null)
{
return null;
}
}

View file

@ -93,4 +93,24 @@ class GithubBuild extends RemoteGitBuild
return 'https://github.com/' . $this->getProject()->getReference() . '.git';
}
}
public function getCommitMessage()
{
$rtn = $this->data['commit_message'];
$rtn = preg_replace('/\#([0-9]+)/', '<a target="_blank" href="https://github.com/' . $this->getProject()->getReference() . '/issues/$1">#$1</a>', $rtn);
$rtn = preg_replace('/\@([a-zA-Z0-9_]+)/', '<a target="_blank" href="https://github.com/$1">@$1</a>', $rtn);
return $rtn;
}
public function getFileLinkTemplate()
{
$link = 'https://github.com/' . $this->getProject()->getReference() . '/';
$link .= 'blob/' . $this->getBranch() . '/';
$link .= '{FILE}';
$link .= '#L{LINE}';
return $link;
}
}

View file

@ -124,7 +124,12 @@ class PhpMessDetector implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
$path = $this->path;
}
$cmd = $phpmd . ' "%s" text %s %s %s';
$cmd = $phpmd . ' "%s" xml %s %s %s';
// Disable exec output logging, as we don't want the XML report in the log:
$this->phpci->logExecOutput(false);
// Run PHPMD:
$this->phpci->executeCommand(
$cmd,
$path,
@ -133,9 +138,14 @@ class PhpMessDetector implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
$suffixes
);
// Re-enable exec output logging:
$this->phpci->logExecOutput(true);
$success = true;
$errors = count(array_filter(explode(PHP_EOL, trim($this->phpci->getLastOutput()))));
list($errors, $data) = $this->processReport(trim($this->phpci->getLastOutput()));
$this->build->storeMeta('phpmd-warnings', $errors);
$this->build->storeMeta('phpmd-data', $data);
if ($this->allowed_warnings != -1 && $errors > $this->allowed_warnings) {
$success = false;
@ -150,4 +160,38 @@ class PhpMessDetector implements PHPCI\Plugin, PHPCI\ZeroConfigPlugin
$this->{$key} = $options[$key];
}
}
protected function processReport($xml)
{
$xml = simplexml_load_string($xml);
if ($xml === false) {
throw new \Exception('Could not process PHPMD report XML.');
}
$warnings = 0;
$data = array();
foreach ($xml->file as $file) {
$fileName = (string)$file['name'];
$fileName = str_replace($this->phpci->buildPath, '', $fileName);
foreach ($file->violation as $violation) {
$warnings++;
$warning = array(
'file' => $fileName,
'line_start' => (int)$violation['beginline'],
'line_end' => (int)$violation['endline'],
'rule' => (string)$violation['rule'],
'ruleset' => (string)$violation['ruleset'],
'priority' => (int)$violation['priority'],
'message' => (string)$violation,
);
$data[] = $warning;
}
}
return array($warnings, $data);
}
}

View file

@ -57,11 +57,16 @@ td .label {
#title
{
border-bottom: 1px solid #ccc;
margin: -10px -10px 15px -10px;
padding: 10px;
}
#title img {
border: 1px solid #fff;
border-radius: 50%;
box-shadow: 2px 2px 2px rgba(0,0,0,0.1);
margin-bottom: 15px;
margin-right: 15px;
}
#title h1
{
font-size: 2em;
@ -69,6 +74,23 @@ td .label {
padding: 0;
}
#title h1 span {
font-weight: lighter;
margin-left: 15px;
}
#title h1 label {
margin-top: -6px;
}
#build-info {
margin-left: 100px;
}
.commit-message {
font-size: 1.2em;
margin-bottom: 10px;
}
h2 {
color: #246;
font-size: 1.8em;
@ -135,8 +157,13 @@ td .label {
.ui-sortable-placeholder * { visibility: hidden; }
.ui-plugin { padding-top: 15px; }
.ui-plugin .panel-title {
cursor: move;
}
.panel-body table {
margin-bottom: 0;
}
#loading {
font-family: Roboto, Arial, Sans-Serif;

View file

@ -0,0 +1,69 @@
var phpmdPlugin = PHPCI.UiPlugin.extend({
id: 'build-phpmd-warnings',
css: 'col-lg-12 col-md-12 col-sm-12 col-xs-12',
title: 'PHP Mess Detector',
lastData: null,
displayOnUpdate: false,
register: function() {
var self = this;
var query = PHPCI.registerQuery('phpmd-data', -1, {key: 'phpmd-data'})
$(window).on('phpmd-data', function(data) {
self.onUpdate(data);
});
$(window).on('build-updated', function(data) {
if (data.queryData.status > 1) {
self.displayOnUpdate = true;
query();
}
});
},
render: function() {
return $('<table class="table table-striped" id="phpmd-data">' +
'<thead>' +
'<tr>' +
' <th>File</th>' +
' <th>Start</th>' +
' <th>End</th>' +
' <th>Message</th>' +
'</tr>' +
'</thead><tbody></tbody></table>');
},
onUpdate: function(e) {
if (this.lastData && this.lastData[0]) {
return;
}
this.lastData = e.queryData;
var errors = this.lastData[0].meta_value;
var tbody = $('#phpmd-data tbody');
tbody.empty();
for (var i in errors) {
var file = errors[i].file;
if (PHPCI.fileLinkTemplate) {
var fileLink = PHPCI.fileLinkTemplate.replace('{FILE}', file);
fileLink = fileLink.replace('{LINE}', errors[i].line_start);
file = '<a target="_blank" href="'+fileLink+'">' + file + '</a>';
}
var row = $('<tr>' +
'<td>'+file+'</td>' +
'<td>'+errors[i].line_start+'</td>' +
'<td>'+errors[i].line_end+'</td>' +
'<td>'+errors[i].message+'</td></tr>');
tbody.append(row);
}
}
});
PHPCI.registerPlugin(new phpmdPlugin());

View file

@ -382,7 +382,7 @@ var PHPCIObject = Class.extend({
}
$('#plugins').sortable({
handle: '.title',
handle: '.panel-title',
connectWith: '#plugins',
update: self.storePluginOrder
});
@ -391,16 +391,13 @@ var PHPCIObject = Class.extend({
},
renderPlugin: function(plugin) {
var output = $('<div></div>').addClass('box-content').append(plugin.render());
var output = $('<div></div>').addClass('panel-body').append(plugin.render());
var container = $('<div></div>').addClass('ui-plugin ' + plugin.css);
var content = $('<div></div>').attr('id', plugin.id).append(output);
if (plugin.box) {
content.addClass('box');
}
content.addClass('panel');
if (plugin.title) {
content.prepend('<h3 class="title">'+plugin.title+'</h3>');
content.prepend('<div class="panel-heading"><h3 class="panel-title">'+plugin.title+'</h3></div>');
}
content.append(output);