Violation Comments to GitHub

Violation Comments to GitHub Jenkins Plugin

This is a Jenkins plugin for Violation Comments to GitHub Lib. This plugin will find report files from static code analysis and comment GitHub pull requests with the content.

It uses Violation Comments to GitHub Lib and supports the same formats as Violations Lib.

Example of supported reports are available here.

You can also do this with a command line tool.

A number of parsers have been implemented. Some parsers can parse output from several reporters.

Reporter Parser Notes
ARM-GCC CLANG
AndroidLint ANDROIDLINT
AnsibleLint FLAKE8 With -p
Bandit CLANG With bandit -r examples/ -f custom -o bandit.out --msg-template "{abspath}:{line}: {severity}: {test_id}: {msg}"
CLang CLANG
CPD CPD
CPPCheck CPPCHECK With cppcheck test.cpp --output-file=cppcheck.xml --xml
CPPLint CPPLINT
CSSLint CSSLINT
Checkstyle CHECKSTYLE
CloudFormation Linter JUNIT cfn-lint . -f junit --output-file report-junit.xml
CodeClimate CODECLIMATE
CodeNarc CODENARC
Dart MACHINE With dart analyze --format=machine
Detekt CHECKSTYLE With --output-format xml.
DocFX DOCFX
Doxygen CLANG
ERB CLANG With erb -P -x -T '-' "${it}" | ruby -c 2>&1 >/dev/null | grep '^-' | sed -E 's/^-([a-zA-Z0-9:]+)/${filename}\1 ERROR:/p' > erbfiles.out.
ESLint CHECKSTYLE With format: 'checkstyle'.
Findbugs FINDBUGS
Flake8 FLAKE8
FxCop FXCOP
GCC CLANG
Gendarme GENDARME
Generic reporter GENERIC Will create one single violation with all the content as message.
GoLint GOLINT
GoVet GOLINT Same format as GoLint.
GolangCI-Lint CHECKSTYLE With --out-format=checkstyle.
GoogleErrorProne GOOGLEERRORPRONE
HadoLint CHECKSTYLE With -f checkstyle
IAR IAR With --no_wrap_diagnostics
Infer PMD Facebook Infer. With --pmd-xml.
JACOCO JACOCO
JCReport JCREPORT
JSHint JSLINT With --reporter=jslint or the CHECKSTYLE parser with --reporter=checkstyle
JUnit JUNIT It only contains the failures.
KTLint CHECKSTYLE
Klocwork KLOCWORK
KotlinGradle KOTLINGRADLE Output from Kotlin Gradle Plugin.
KotlinMaven KOTLINMAVEN Output from Kotlin Maven Plugin.
Lint LINT A common XML format, used by different linters.
MSBuildLog MSBULDLOG With -fileLogger use .*msbuild\\.log$ as pattern or -fl -flp:logfile=MyProjectOutput.log;verbosity=diagnostic for a custom output filename
MSCpp MSCPP
Mccabe FLAKE8
MyPy MYPY
NullAway GOOGLEERRORPRONE Same format as Google Error Prone.
PCLint PCLINT PC-Lint using the same output format as the Jenkins warnings plugin, details here
PHPCS CHECKSTYLE With phpcs api.php --report=checkstyle.
PHPPMD PMD With phpmd api.php xml ruleset.xml.
PMD PMD
Pep8 FLAKE8
PerlCritic PERLCRITIC
PiTest PITEST
ProtoLint PROTOLINT
Puppet-Lint CLANG With -log-format %{fullpath}:%{line}:%{column}: %{kind}: %{message}
PyDocStyle PYDOCSTYLE
PyFlakes FLAKE8
PyLint PYLINT With pylint --output-format=parseable.
ReSharper RESHARPER
RubyCop CLANG With rubycop -f clang file.rb
SARIF SARIF
SbtScalac SBTSCALAC
Scalastyle CHECKSTYLE
Simian SIMIAN
Sonar SONAR With mvn sonar:sonar -Dsonar.analysis.mode=preview -Dsonar.report.export.path=sonar-report.json. Removed in 7.7, see SONAR-11670 but can be retrieved with: curl --silent 'http://sonar-server/api/issues/search?componentKeys=unique-key&resolved=false' | jq -f sonar-report-builder.jq > sonar-report.json.
Spotbugs FINDBUGS
StyleCop STYLECOP
SwiftLint CHECKSTYLE With --reporter checkstyle.
TSLint CHECKSTYLE With -t checkstyle
Valgrind VALGRIND With --xml=yes.
XMLLint XMLLINT
XUnit XUNIT It only contains the failures.
YAMLLint YAMLLINT With -f parsable
ZPTLint ZPTLINT

48 parsers and 74 reporters.

Missing a format? Open an issue here!

There is also:

Available in Jenkins here.

Notify Jenkins from GitHub

You will need to the pull request id for the pull request that was built.

Merge

You must perform the merge before build. If you don't perform the merge, the reported violations will refer to other lines then those in the pull request. The merge can be done with a shell script like this.

echo ---
echo --- Merging from $FROM in $FROMREPO to $TO in $TOREPO
echo ---
git clone $TOREPO
cd *
git reset --hard $TO
git status
git remote add from $FROMREPO
git fetch from
git merge $FROM
git --no-pager log --max-count=10 --graph --abbrev-commit

Your build command here!

Screenshots

When installed, a post build action will be available.

Post build action menu

The pull request will be commented like this.

Pull request comment

Job DSL Plugin

This plugin can be used with the Job DSL Plugin. In this example the GitHub Pull Request Builder Plugin is used to trigger, merge and provide environment variables needed.

job('GitHub_PR_Builder') {
 concurrentBuild()
 quietPeriod(0)
 scm {
  git {
   remote {
    github('tomasbjerre/violations-test')
    refspec('+refs/pull/*:refs/remotes/origin/pr/*')
   }
   branch('${sha1}')
  }
 }

 triggers {
  githubPullRequest {
   cron('* * * * *')
   permitAll()
   extensions {
    buildStatus {
     completedStatus('SUCCESS', 'There were no errors, go have a cup of coffee...')
     completedStatus('FAILURE', 'There were errors, for info, please see...')
     completedStatus('ERROR', 'There was an error in the infrastructure, please contact...')
    }
   }
  }
 }

 steps {
  shell('''
./gradlew build
  ''')
 }

 publishers {
  violationsToGitHubRecorder {
   config {
    gitHubUrl("https://api.github.com/")
    repositoryOwner("tomasbjerre")
    repositoryName("violations-test")
    pullRequestId("\$ghprbPullId")

    // Only specify one of these!
    oAuth2Token("")
    credentialsId("githubtoken")
    
    createSingleFileComments(true)
    createCommentWithAllSingleFileComments(true)
    commentOnlyChangedContent(true)
    commentOnlyChangedFiles(true)
    minSeverity('INFO')
    maxNumberOfViolations(99999)
    keepOldComments(false)
    
    commentTemplate("""
    **Reporter**: {{violation.reporter}}{{#violation.rule}}
    
    **Rule**: {{violation.rule}}{{/violation.rule}}
    **Severity**: {{violation.severity}}
    **File**: {{violation.file}} L{{violation.startLine}}{{#violation.source}}
    
    **Source**: {{violation.source}}{{/violation.source}}
    
    {{violation.message}}
    """)
    
    violationConfigs {
     violationConfig {
      parser("FINDBUGS")
      reporter("Findbugs")
      pattern(".*/findbugs/.*\\.xml\$")
     }
     violationConfig {
      parser("CHECKSTYLE")
      reporter("Checkstyle")
      pattern(".*/checkstyle/.*\\.xml\$")
     }
    }
   }
  }
 }
}

Here is another example using Generic Webhook Trigger plugin. You will need to add a webhook in GitHub and point it to http://JENKINS_URL/generic-webhook-trigger/invoke. You may want to combine this with HTTP Request Plugin to comment the pull requests with a link to the job. And also Conditional BuildStep Plugin to have different comments depending on build status.

job('GitHub_PR_Builder Generic') {
 concurrentBuild()
 quietPeriod(0)
 parameters {
  stringParam('PULL_REQUEST_HEAD_URL', '')
  stringParam('PULL_REQUEST_BASE_URL', '')
  stringParam('PULL_REQUEST_HEAD_REF', '')
  stringParam('PULL_REQUEST_BASE_REF', '')
 }
 scm {
  git {
   remote {
    name('origin')
    url('$PULL_REQUEST_BASE_URL')
   }
   remote {
    name('upstream')
    url('$PULL_REQUEST_HEAD_URL')
   }
   branch('$PULL_REQUEST_HEAD_REF')
   extensions {
    mergeOptions {
     remote('upstream')
     branch('$PULL_REQUEST_BASE_REF')
    }
   }
  }
 }
 triggers {
  genericTrigger {
   genericVariables {
    genericVariable {
     key("PULL_REQUEST_HEAD_URL")
     value("\$.pull_request.head.repo.clone_url")
     expressionType("JSONPath")
     regexpFilter("")
    }
    genericVariable {
     key("PULL_REQUEST_HEAD_REF")
     value("\$.pull_request.head.ref")
     expressionType("JSONPath")
     regexpFilter("")
    }
    genericVariable {
     key("PULL_REQUEST_BASE_URL")
     value("\$.pull_request.base.repo.clone_url")
     expressionType("JSONPath")
     regexpFilter("")
    }
    genericVariable {
     key("PULL_REQUEST_BASE_REF")
     value("\$.pull_request.base.ref")
     expressionType("JSONPath")
     regexpFilter("")
    }
    genericVariable {
     key("PULL_REQUEST_BASE_OWNER")
     value("\$.pull_request.base.repo.owner.login")
     expressionType("JSONPath")
     regexpFilter("")
    }
    genericVariable {
     key("PULL_REQUEST_BASE_REPO")
     value("\$.pull_request.base.repo.name")
     expressionType("JSONPath")
     regexpFilter("")
    }
    genericVariable {
     key("PULL_REQUEST_ID")
     value("\$.number")
     expressionType("JSONPath")
     regexpFilter("")
    }
    genericVariable {
     key("ACTION")
     value("\$.action")
     expressionType("JSONPath")
     regexpFilter("")
    }
   }
   regexpFilterText("\$ACTION")
   regexpFilterExpression("opened|reopened|synchronize")
  }
 }

 steps {
  shell('./gradlew build')
 }

 publishers {
  violationsToGitHubRecorder {
   config {
    gitHubUrl("https://api.github.com/")
    repositoryOwner("\$PULL_REQUEST_BASE_OWNER")
    repositoryName("\$PULL_REQUEST_BASE_REPO")
    pullRequestId("\$PULL_REQUEST_ID")

    // Only specify one of these!
    oAuth2Token("oh no!")
    credentialsId("githubtoken")

    createSingleFileComments(true)
    createCommentWithAllSingleFileComments(true)
    commentOnlyChangedContent(true)
    commentOnlyChangedFiles(true)
    minSeverity('INFO')
    maxNumberOfViolations(99999)
    keepOldComments(false)
    
    commentTemplate("""
    **Reporter**: {{violation.reporter}}{{#violation.rule}}
    
    **Rule**: {{violation.rule}}{{/violation.rule}}
    **Severity**: {{violation.severity}}
    **File**: {{violation.file}} L{{violation.startLine}}{{#violation.source}}
    
    **Source**: {{violation.source}}{{/violation.source}}
    
    {{violation.message}}
    """)
    
    violationConfigs {
     violationConfig {
      parser("FINDBUGS")
      reporter("Findbugs")
      pattern(".*/findbugs/.*\\.xml\$")
     }
     violationConfig {
      parser("CHECKSTYLE")
      reporter("Checkstyle")
      pattern(".*/checkstyle/.*\\.xml\$")
     }
    }
   }
  }
 }
}

Pipeline Plugin

This plugin can be used with the Pipeline Plugin:

node {
 def mvnHome = tool 'Maven 3.3.9'
 deleteDir()
 
 stage('Merge') {
  sh "git init"
  sh "git fetch --no-tags --progress git@git:group/reponame.git +refs/heads/*:refs/remotes/origin/* --depth=200"
  sh "git checkout origin/${env.targetBranch}"
  sh "git merge origin/${env.sourceBranch}"
  sh "git log --graph --abbrev-commit --max-count=10"
 }

 stage('Static code analysis') {
  sh "${mvnHome}/bin/mvn package -DskipTests -Dmaven.test.failure.ignore=false -Dsurefire.skip=true -Dmaven.compile.fork=true -Dmaven.javadoc.skip=true"

  step([
   $class: 'ViolationsToGitHubRecorder', 
   config: [
    gitHubUrl: 'https://api.github.com/', 
    repositoryOwner: 'tomasbjerre', 
    repositoryName: 'violations-test', 
    pullRequestId: '2', 

    // Only specify one of these!
    oAuth2Token: '',
    credentialsId: '',

    createCommentWithAllSingleFileComments: true, 
    createSingleFileComments: true, 
    commentOnlyChangedContent: true, 
    commentOnlyChangedFiles: true,
    minSeverity: 'INFO',
    maxNumberOfViolations: 99999,
    keepOldComments: false,
 
    commentTemplate: """
    **Reporter**: {{violation.reporter}}{{#violation.rule}}
    
    **Rule**: {{violation.rule}}{{/violation.rule}}
    **Severity**: {{violation.severity}}
    **File**: {{violation.file}} L{{violation.startLine}}{{#violation.source}}
    
    **Source**: {{violation.source}}{{/violation.source}}
    
    {{violation.message}}
    """,

    violationConfigs: [
     [ pattern: '.*/checkstyle-result\\.xml$', parser: 'CHECKSTYLE', reporter: 'Checkstyle' ], 
     [ pattern: '.*/findbugsXml\\.xml$', parser: 'FINDBUGS', reporter: 'Findbugs' ], 
     [ pattern: '.*/pmd\\.xml$', parser: 'PMD', reporter: 'PMD' ], 
    ]
   ]
  ])
 }
}

Plugin development

More details on Jenkins plugin development is available here.

There is a /build.sh that will perform a full build and test the plugin.

If you have release-permissions this is how you do a release:

mvn release:prepare release:perform