Gradle Checkstyle plugin

Jan 8, 2019 06:50 · 846 words · 4 minute read gradle CI jenkins

Возникла необходимость настроить статический анализ кода (checkstyle) для проекта на языке Java. Давайте разберемся!

Из особенностей - проверка кода должна запускаться только при подаче пулл реквеста, а сама задача должна быть легко масштабируема (можно быстро включить checkstyle еще для 50 или 100 других проектов). Итак, приступим.

Для решения задачи будем использовать плагин Checkstyle. Пример схемы (файл checkstyle.xml) для данного плагина можно взять здесь и, при необходимости, подредактировать под свои нужды.

В предыдущей статье мы уже рассматривали использование инит-скриптов гредла, и, так как нам нужна масштабируемость, настройку статического анализа кода мы тоже сделаем через инит-скрипт. Создаем файл checkstyle.gradle со следующим содержимым:

allprojects {
    afterEvaluate { project ->
        project.plugins.withId('checkstyle') {
            try {
                def _toolVersion = ext.has('toolVersion') ? ext['toolVersion'] : '8.15'
                if (System.getenv('BUILD_ENVIRONMENT') ?: 'DEVELOPER' == 'CI') {
                    println "'checkstyle' plugin common config"
                    println " |- use toolVersion version: $_toolVersion"
                    checkstyle {
                        toolVersion "$_toolVersion"

                        configFile = file(System.getenv('GRADLE_HOME') + "/checkstyle.xml")
                        println " |- use checkstyle.xml: " + configFile
                        println ''
                        reportsDir = file("${buildDir}/checkstyleReports")
                    }
                    checkstyleMain {
                        source ='src/main/java'
                    }
                    checkstyleTest {
                        source ='src/test/java'
                    }
                } else {
                    println "'checkstyle' plugin common config"
                    println " |- use toolVersion version: $_toolVersion"
                    checkstyle {
                        toolVersion "$_toolVersion"

                        configFile = file(System.getenv('HOME') + "/.gradle/checkstyle.xml")
                        println " |- use checkstyle.xml: " + configFile
                        println ''
                        reportsDir = file("${project.buildDir}/checkstyleReports")
                    }
                    checkstyleMain {
                        source ='src/main/java'
                    }
                    checkstyleTest {
                        source ='src/test/java'
                    }
                }
            } catch (UnknownPluginException) {
                println UnknownPluginException
            }
        }
    }
}

Схема для плагина на машине разработчика должна находиться по пути USER_HOME/.gradle/checkstyle.xml, а в docker-контейнере, который запускается на CI-сервере, - в GRADLE_HOME/checkstyle.xml. Добиться этого несложно - в предыдущей статье мы рассматривали как это можно сделать.

Теперь, чтобы в проекте появились задачи для статического анализа кода, достаточно в конфигурации сборки (build.gradle) добавить соответствующий плагин:

plugins {
    id 'checkstyle'
}

После добавления плагина станут доступны задачи checkstyleMain, checkstyleTest, checkstyleSourceSet и, самое главное, check, которая зависит (включает в себя) все перечисленные ранее.

Для запуска проверки кода на CI-сервере с помощью docker-контейнера достаточно выполнить примерно такую команду:

	docker run \
		--rm \
		-v $(pwd):/project \
		-w /project \
	myorganization/gradle:5.0-jdk11 gradle clean check

Для описания пайплайна (Pipeline) с задачей checkstyle будем использовать уже хорошо известные нам shared libraries - помним о возможности масштабирования и о принципе DRY (Don’t Repeat Yourself). Расширим базовые требования - добавим в описание пайплайна также отправку комментариев к пулл реквесту с результатами анализа кода, формирование отчета (для доступа по ссылке) и уведомление в slack-канал.

Примечание. Для отправки комментариев в пулл реквесту нам понадобится jenkins-плагин Violation Comments to GitHub. Установить его можно руками или с помощью скрипта 00-install-plugins.groovy как описано здесь.

Pipeline (файл adsCheckstylePipeline) в нашем случае будет выглядеть так:

def call(body) {
    def pipelineParams = [:]
    body.resolveStrategy = Closure.DELEGATE_FIRST
    body.delegate = pipelineParams
    body()

    pipeline {
        agent any
        stages {
            stage('Checkstyle') {
                steps {
                    script {
                        env.REPO_NAME = "${pipelineParams.repoName}"
                        sh '''
                            docker run \
                                --rm \
                                -v $(pwd):/project \
                                -w /project \
                            myorganization/gradle:5.0-jdk11 gradle clean check
                        '''
                        env.BUILD_STATUS = "${currentBuild.currentResult}".toLowerCase()
                    }
                    step([$class: 'ViolationsToGitHubRecorder',
                        config: [
                            gitHubUrl: 'https://api.github.com/',
                            repositoryOwner: "${GITHUB_PR_SOURCE_REPO_OWNER}",
                            repositoryName: "${REPO_NAME}",
                            pullRequestId: "${GITHUB_PR_NUMBER}",

                            credentialsId: 'token-pr',

                            createCommentWithAllSingleFileComments: true,
                            createSingleFileComments: true,
                            commentOnlyChangedContent: false,
                            minSeverity: 'WARN',
                            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: ".*/checkstyleReports/.*\\.xml\$",
                                  parser: 'CHECKSTYLE',
                                  reporter: 'Checkstyle'
                                ]
                            ]
                        ]
                    ])
                    }
                post {
                    always {
                        publishHTML([
                            allowMissing: false,
                            alwaysLinkToLastBuild: true,
                            keepAll: true,
                            reportDir: 'build/checkstyleReports',
                            reportFiles: 'main.html',
                            reportName: 'HTML Report',
                            reportTitles: ''
                        ])
                        slackSend(channel: "${pipelineParams.slackChannel}", \
                              message: "Build '${env.JOB_NAME}' is *${BUILD_STATUS}* \n (${env.BUILD_URL}) \n\n" +
                            "See report on '${JOB_URL}HTML_20Report/'")
                    }
                }
            }
        }
    }
}

Находящийся в основном проекте Jenkinsfile для запуска checkstyle будет таким:

@Library('jenkins-shared-libraries@master') _

adsCheckstylePipeline {
    slackChannel = '@ealebed' // for sending notification in slack
    repoName = 'my-test-repo' // GitHub repo name, need for pushing comment to PR
}

Для публикации комментариев с результатами анализа кода плагину необходимо авторизоваться на GitHub. Это делается с помощью строки credentialsId: 'token-pr' - конечно же, соответствующие креденшелы нужно создать. Можно это сделать вручную - в Интернете множество примеров с картинками, как это сделать, а можно немного “доработать” уже существующие groovy-скрипты по настройке Jenkins (см. цикл статей “Jenkins as a code” - 1, 2, 3, 4).

Я выбрал второй вариант, поэтому мне пришлось добавить в скрипт 01-global-settings.groovy следующие строки:

...

println("--- Configuring credentials")
Credentials secretText = (Credentials) new StringCredentialsImpl(
    CredentialsScope.GLOBAL,
    "token-pr", // id token
    "GitHub token for PR", // description for this token
    Secret.fromString("bw4aeqkmejay.....xip4ln727u0wg7q") // token generated on github: https://github.com/settings/tokens
)
SystemCredentialsProvider.getInstance().getStore().addCredentials(Domain.global(), secretText)

println("--- Configuring GitHubServer")
GitHubServerConfig server = new GitHubServerConfig('token-pr')
server.setManageHooks(true)
server.setApiUrl('https://api.github.com')
server.setClientCacheSize(20)
GitHubPlugin.configuration().getConfigs().add(server)

Осталось создать задачу в Jenkins, где в качестве триггера указать “GitHub Pull Requests”. Trigger Mode в данном случае будет “Cron with Persisted Data”, а Trigger Events - “Pull Request Opened” (конечно, вы можете выбрать другие значения в зависимости от ваших требований). Также в поле Branches to build нужно указать “origin/pull/${GITHUB_PR_NUMBER}/merge”, а в расширенных настройках репозитория в Refspec вписать “+refs/pull/${GITHUB_PR_NUMBER}/merge:refs/remotes/origin/pull/${GITHUB_PR_NUMBER}/merge”. Опять же, для 1-2 задач это быстрее сделать руками, но если нужно настроить несколько сотен таких задач, то проще доработать groovy-скрипт 05-create-jobs.groovy из этой статьи.

Больше полезной информации о настройке плагинов Checkstyle (анализ кода), Violation Comments to GitHub (комментарии с результатами чекстайла) и GitHub integration plugin (триггер “GitHub Pull Requests”) можно найти здесь, здесь и здесь.

tweet Share