Plugin Information |
---|
View Violation Comments to GitLab on the plugin site for more information. |
Older versions of this plugin may not be safe to use. Please review the following warnings before using an older version:
This is a Jenkins plugin for Violation Comments to GitLab Lib. This plugin will find report files from static code analysis and comment GitLab pull requests with the content.
Available in Jenkins here.
Example of supported reports are available here.
There is a complete running example available here: https://github.com/tomasbjerre/jenkins-configuration-as-code-sandbox
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 | |
CPPLint | CPPLINT | |
CSSLint | CSSLINT | |
Checkstyle | CHECKSTYLE | |
CodeClimate | CODECLIMATE | |
CodeNarc | CODENARC | |
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 | |
GoLint | GOLINT | |
GoVet | GOLINT | Same format as GoLint. |
GolangCI-Lint | CHECKSTYLE | With --out-format=checkstyle . |
GoogleErrorProne | GOOGLEERRORPRONE | |
IAR | IAR | With --no_wrap_diagnostics |
Infer | PMD | Facebook Infer. With --pmd-xml . |
JCReport | JCREPORT | |
JSHint | JSLINT | With --reporter=jslint or the CHECKSTYLE parser with --reporter=checkstyle |
JUnit | JUNIT | |
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. |
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 | |
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 |
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 |
XMLLint | XMLLINT | |
YAMLLint | YAMLLINT | With -f parsable |
ZPTLint | ZPTLINT |
Missing a format? Open an issue here!
There is also:
Notify Jenkins from GitLab
You may trigger with a webhook in GitLab. And consume it with Generic Webhook Trigger plugin to get the variables you need.
Or, trigger with GitLab plugin.
Or, trigger with GitLab Merge Request Builder Plugin.
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
Comments can be made on the diff with one comment per violation createSingleFileComments
.
Or one big comment can be made, createCommentWithAllSingleFileComments
.
Job DSL Plugin
This plugin can be used with the Job DSL Plugin. Here is an example using Generic Webhook Trigger plugin, HTTP Request Plugin and Conditional BuildStep Plugin.
job('GitLab_MR_Builder') { concurrentBuild() quietPeriod(0) parameters { stringParam('MERGE_REQUEST_TO_URL', '') stringParam('MERGE_REQUEST_FROM_URL', '') stringParam('MERGE_REQUEST_TO_BRANCH', '') stringParam('MERGE_REQUEST_FROM_BRANCH', '') } scm { git { remote { name('origin') url('$MERGE_REQUEST_TO_URL') } remote { name('upstream') url('$MERGE_REQUEST_FROM_URL') } branch('$MERGE_REQUEST_FROM_BRANCH') extensions { mergeOptions { remote('upstream') branch('$MERGE_REQUEST_TO_BRANCH') } } } } triggers { genericTrigger { genericVariables { genericVariable { key("MERGE_REQUEST_TO_URL") value("\$.object_attributes.target.git_http_url") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MERGE_REQUEST_FROM_URL") value("\$.object_attributes.source.git_http_url") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MERGE_REQUEST_TO_BRANCH") value("\$.object_attributes.target_branch") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MERGE_REQUEST_FROM_BRANCH") value("\$.object_attributes.source_branch") expressionType("JSONPath") regexpFilter("") } genericVariable { key("PROJECT_ID") value("\$.object_attributes.target_project_id") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MERGE_REQUST_ID") value("\$.object_attributes.id") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MR_OBJECT_KIND") value("\$.object_kind") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MR_OLD_REV") value("\$.object_attributes.oldrev") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MR_ACTION") value("\$.object_attributes.action") expressionType("JSONPath") regexpFilter("") } } regexpFilterText("\$MR_OBJECT_KIND \$MR_ACTION \$MR_OLD_REV") regexpFilterExpression("^merge_request\\s(update\\s.{40}\$|open.*)") } } steps { httpRequest { url("http://gitlab:880/api/v3/projects/\$PROJECT_ID/merge_requests/\$MERGE_REQUST_ID/notes?private_token=AvAkp6HtUvzpesPypXSk") consoleLogResponseBody(true) httpMode("POST") requestBody('body=Building... %20\$BUILD_URL') } shell('./gradlew build') conditionalBuilder { runCondition { statusCondition { worstResult('SUCCESS') bestResult('SUCCESS') } runner { runUnstable() } conditionalbuilders { httpRequest { url('http://gitlab:880/api/v3/projects/\$PROJECT_ID/merge_requests/\$MERGE_REQUST_ID/notes?private_token=AvAkp6HtUvzpesPypXSk') consoleLogResponseBody(true) httpMode('POST') requestBody('body=SUCCESS%20\$BUILD_URL') } } } } conditionalBuilder { runCondition { statusCondition { worstResult('FAILURE') bestResult('FAILURE') } runner { runUnstable() } conditionalbuilders { httpRequest { url('http://gitlab:880/api/v3/projects/\$PROJECT_ID/merge_requests/\$MERGE_REQUST_ID/notes?private_token=AvAkp6HtUvzpesPypXSk') consoleLogResponseBody(true) httpMode('POST') requestBody('body=FAIL%20\$BUILD_URL') } } } } } publishers { violationsToGitLabRecorder { config { gitLabUrl("http://gitlab:880/") projectId("\$PROJECT_ID") mergeRequestIid("\$MERGE_REQUST_IID") // Only specify proxy if you need it proxyUri('') proxyCredentialsId('') // A username/password credential commentOnlyChangedContent(true) commentOnlyChangedFiles(true) createSingleFileComments(true) createCommentWithAllSingleFileComments(true) minSeverity('INFO') maxNumberOfViolations(99999) //You may want this when troubleshooting things enableLogging(true) apiTokenCredentialsId("gitlabtoken") // A secret text credential apiTokenPrivate(true) authMethodHeader(true) ignoreCertificateErrors(true) 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
Here is an example pipeline that will merge, run unit tests, run static code analysis and finally report back to GitLab. It requires the GitLab Plugin.
pipelineJob("merge-request-pipeline") { concurrentBuild() quietPeriod(0) authenticationToken("thetoken") triggers { genericTrigger { genericVariables { genericVariable { key("MERGE_REQUEST_TO_URL") value("\$.object_attributes.target.git_http_url") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MERGE_REQUEST_FROM_URL") value("\$.object_attributes.source.git_http_url") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MERGE_REQUEST_TO_BRANCH") value("\$.object_attributes.target_branch") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MERGE_REQUEST_FROM_BRANCH") value("\$.object_attributes.source_branch") expressionType("JSONPath") regexpFilter("") } genericVariable { key("PROJECT_ID") value("\$.object_attributes.target_project_id") expressionType("JSONPath") regexpFilter("") } genericVariable { key("PROJECT_PATH") value("\$.object_attributes.target.path_with_namespace") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MERGE_REQUST_IID") value("\$.object_attributes.iid") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MR_OBJECT_KIND") value("\$.object_kind") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MR_OLD_REV") value("\$.object_attributes.oldrev") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MR_ACTION") value("\$.object_attributes.action") expressionType("JSONPath") regexpFilter("") } genericVariable { key("MR_TITLE") value("\$.object_attributes.title") expressionType("JSONPath") regexpFilter("") } } regexpFilterText("\$MR_OBJECT_KIND \$MR_ACTION \$MR_OLD_REV") regexpFilterExpression("^merge_request\\s(update\\s.{40}\$|open.*)") } } definition { cps { script(readFileFromWorkspace('merge_request_pipeline.pipeline')) sandbox() } } }
And the merge_request_pipeline.pipeline contains
def commentMr(projectId, mergeRequestIid, comment) { def body = comment .replaceAll(" ","%20") .replaceAll("/","%2F") def project = projectId .replaceAll("/","%2F") sh "curl http://gitlab:80/api/v4/projects/$project/merge_requests/$mergeRequestIid/notes -H 'PRIVATE-TOKEN: 6xRcmSzPzzEXeS2qqr7R' -X POST -d \"body="+body+"\"" } node { deleteDir() currentBuild.description = "$MR_TITLE from $MERGE_REQUEST_FROM_BRANCH to $MERGE_REQUEST_TO_BRANCH" commentMr(env.PROJECT_PATH,env.MERGE_REQUST_IID,"Verifierar $MERGE_REQUEST_FROM_BRANCH... ${env.BUILD_URL}") stage('Merge') { sh "git init" sh "git fetch --no-tags $MERGE_REQUEST_TO_URL +refs/heads/*:refs/remotes/origin/* --depth=200" sh "git checkout origin/${env.MERGE_REQUEST_TO_BRANCH}" sh "git config user.email 'je@nkins.domain'" sh "git config user.name 'jenkins'" sh "git merge origin/${env.MERGE_REQUEST_FROM_BRANCH}" sh "git log --graph --abbrev-commit --max-count=10" } stage('Compile') { sh "./gradlew assemble" } stage('Unit test') { sh "./gradlew test" commentMr(env.PROJECT_PATH,env.MERGE_REQUST_IID,"Test ok in $MERGE_REQUEST_FROM_BRANCH =) ${env.BUILD_URL}") } stage('Regression test') { sh "echo regtest" commentMr(env.PROJECT_PATH,env.MERGE_REQUST_IID,"Regression test ok in $MERGE_REQUEST_FROM_BRANCH =) ${env.BUILD_URL}") } stage('Static code analysis') { sh "./gradlew check" step([ $class: 'ViolationsToGitLabRecorder', config: [ gitLabUrl: 'http://gitlab:80/', projectId: env.PROJECT_PATH, mergeRequestIid: env.MERGE_REQUST_IID, commentOnlyChangedContent: true, commentOnlyChangedFiles: true, createSingleFileComments: true, createCommentWithAllSingleFileComments: true, minSeverity: 'INFO', maxNumberOfViolations: 99999, // You may want this when troubleshooting things enableLogging: true, // Only specify proxy if you need it proxyUri: '', proxyCredentialsId: '', // A username/password credential apiTokenCredentialsId: 'id', // A secret text credential apiTokenPrivate: true, authMethodHeader: true, ignoreCertificateErrors: true, keepOldComments: false, shouldSetWip: true, 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/.*\\.xml$', parser: 'CHECKSTYLE', reporter: 'Checkstyle' ], [ pattern: '.*/findbugs/.*\\.xml$', parser: 'FINDBUGS', reporter: 'Findbugs' ], [ pattern: '.*/pmd/.*\\.xml$', parser: 'PMD', reporter: 'PMD' ], ] ] ]) } }
Changelog
https://github.com/jenkinsci/violation-comments-to-gitlab-plugin/blob/master/CHANGELOG.md
Attachments:
Skärmbild från 2017-03-14 19-22-30.png (image/png)
mergerequest-onecomment.png (image/png)
violation-pipeline.PNG (image/png)
image2018-4-27 17:39:11.png (image/png)