[JAVA] J'ai écrit un fichier Jenkins avec Declarative Pipeline (Checkstyle, Findbugs, PMD, CPD, etc.)

J'ai écrit un fichier Jenkins à CI une application Gradle Java avec Jenkins. Lorsque je le transmets à Github, le travail de pipeline Jenkins sur AWS s'exécute, et si le test réussit, je l'utilise également comme un déploiement sur Tomcat sur AWS.

Declarative Pipeline En l'état, Jenkinsfile

node {
  ....
}

Il a été écrit comme, mais si vous regardez Site officiel de Jenkins Il s'agit de la notation Scripted Pipelines, À partir de la migration de la version 2.5 du plugin Pipeline

pipeline {
  ....  
}

Une notation appelée Declarative Pipeline a été introduite pour écrire comme C'était plus simple et plus facile à écrire, alors je l'ai réécrit. C'est certainement chouette (surtout le dernier email, déploiement) C'est bien de pouvoir mélanger les pipelines scriptés traditionnels en cas d'urgence.

Plug-in ajouté

Jenkins non inclus dans le plugin suggéré au moment de la configuration initiale

Jenkinsfile Exécutez une tâche de pipeline Jenkins avec un webhook de Github. Pour les travaux de pipeline, utilisez Jenkinsfile de Github. Le flux des travaux est le suivant. Déployez uniquement si l'analyse et les tests de code statique réussissent. image

Jenkinsfile


pipeline {
    agent any
    //Définir des constantes et des variables
    environment {
        reportDir = 'build/reports'
        javaDir = 'src/main/java'
        resourcesDir = 'src/main/resources'
        testReportDir = 'build/test-results/test'
        jacocoReportDir = 'build/jacoco' 
        javadocDir = 'build/docs/javadoc'
        libsDir = 'build/libs'
        appName = 'SampleApp'
        appVersion = '1.0.0'
    }
    
    //Définir une ou plusieurs étapes dans le bloc étapes
    stages {
        stage('Préparation préalable') {
            //Le traitement réel est défini dans le bloc étapes
            steps {
                deleteDir()

                //Découvrez le projet Github qui a déclenché ce Job
                checkout scm

                //Jenkinsfile et build pour enquêter sur la cause de l'échec du travail.gradle enregistrer d'abord
                archiveArtifacts "Jenkinsfile"
                archiveArtifacts "build.gradle"

                //Vous pouvez également utiliser la notation traditionnelle Scripted Pipelines avec le bloc de script
                script {
                    //Accordez la permission d'exécution pour ne pas vous fâcher avec la permission refusée
                    if(isUnix()) {
                        sh 'chmod +x gradlew'
                    }
                }
                gradlew 'clean'
            }
        }
        
        stage('compiler') {
            steps {
                gradlew 'classes testClasses'
            }
            
            //Le bloc post peut définir le processus à exécuter après le bloc étapes
            post {
                //Le bloc always est toujours exécuté, que le traitement du bloc d'étapes échoue ou réussisse.
                always {

                    //Si vous l'exécutez lors de la génération de JavaDoc, l'avertissement JavaDoc sera également inclus.
                    //Les avertissements de compilation Java sont collectés immédiatement après la compilation
                    step([

                        //La spécification de classe lors de l'exécution du plug-in ne doit pas nécessairement être un nom complet
                        $class: 'WarningsPublisher',

                        //ConsoleParsers, si vous souhaitez collecter des alertes de la console lors de l'exécution de Job
                        // pmd.Spécifiez les configurations de l'analyseur lors de la collecte à partir d'un fichier tel que xml.
                        //Dans le cas des configurations d'analyseur, en plus du nom de l'analyseur, modèle(Chemin du fichier à agréger)Doit également être spécifié
                        //Utilisez le nom de l'analyseur défini dans le fichier de propriétés ci-dessous.
                        // https://github.com/jenkinsci/warnings-plugin/blob/master/src/main/resources/hudson/plugins/warnings/parser/Messages.properties
                        consoleParsers: [
                            [parserName: 'Java Compiler (javac)'],
                        ],
                        canComputeNew: false,
                        canResolveRelativesPaths: false,
                        usePreviousBuildAsReference: true
                    ])
                }
            }
        }
        
        stage('Analyse de code statique') {
            steps {
                //Utilisez la méthode parallèle pour le traitement parallèle
                parallel(
                    'Analyse de code statique' : {
                        gradlew 'check -x test'

                        //Vous pouvez spécifier le répertoire actuel avec la méthode dir
                        dir(reportDir) {
                            step([
                                $class: 'CheckStylePublisher',
                                pattern: "checkstyle/*.xml"
                            ])
                            step([
                                $class: 'FindBugsPublisher',
                                pattern: "findbugs/*.xml"
                            ])
                            step([
                                $class: 'PmdPublisher',
                                pattern: "pmd/*.xml"
                            ])
                            step([
                                $class: 'DryPublisher',
                                pattern: "cpd/*.xml"
                            ])
                
                            archiveArtifacts "checkstyle/*.xml"
                            archiveArtifacts "findbugs/*.xml"
                            archiveArtifacts "pmd/*.xml"
                            archiveArtifacts "cpd/*.xml"
                        }
                    },
                    'Nombre de pas': {
                        //Création de rapports
                        //Si vous spécifiez outputFile et outputFormat, un fichier Excel sera également créé.
                        stepcounter outputFile: 'stepcount.xls', outputFormat: 'excel', settings: [
                            [key:'Java', filePattern: "${javaDir}/**/*.java"],
                            [key:'SQL', filePattern: "${resourcesDir}/**/*.sql"],
                            [key:'HTML', filePattern: "${resourcesDir}/**/*.html"],
                            [key:'JS', filePattern: "${resourcesDir}/**/*.js"],
                            [key:'CSS', filePattern: "${resourcesDir}/**/*.css"]
                        ]
                        //Pour le moment, enregistrez le fichier Excel en tant que livrable
                        archiveArtifacts "stepcount.xls"
                    },
                    'Analyse des tâches': {
                        step([
                            $class: 'TasksPublisher',
                            pattern: './**',
                            //Est-il sensible à la casse lors de la recherche de cibles d'agrégation?
                            ignoreCase: true,
                            //Les chaînes de caractères à agréger peuvent être spécifiées pour chaque priorité
                            //Si vous en spécifiez plusieurs, spécifiez une chaîne de caractères séparés par des virgules.
                            high: 'System.out.System.err',
                            normal: 'TODO,FIXME,XXX',
                        ])
                    },
                    'JavaDoc': {
                        gradlew 'javadoc -x classes'
                        step([
                            $class: 'JavadocArchiver',
                            //Index Javadoc.Spécifiez le chemin du dossier où se trouve html
                            javadocDir: "${javadocDir}",
                            keepAll: true
                        ])
                    }
                )
            }
            
            post {
                always {
                   //Collecter les avertissements JavaDoc
                    step([
                        $class: 'WarningsPublisher',
                        consoleParsers: [
                            [parserName: 'JavaDoc Tool']
                        ],
                        canComputeNew: false,
                        canResolveRelativesPaths: false,
                        usePreviousBuildAsReference: true
                    ])
                }
            }
        }
        

        stage('tester') {
            steps {
                gradlew 'test jacocoTestReport -x classes -x testClasses'
                
                junit "${testReportDir}/*.xml"
                archiveArtifacts "${testReportDir}/*.xml"

                //Générer un rapport de couverture (hors classe de test)
                step([
                    $class: 'JacocoPublisher',
                    execPattern: "${jacocoReportDir}/*.exec",
                    exclusionPattern: '**/*Test.class'
                ])
            }
        }
        
        stage('Déployer') {
            //Vous pouvez spécifier les conditions d'exécution de l'étape dans le bloc when
            when {
                //Ne pas déployer sur l'analyse de code statique et l'échec du test
                expression {currentBuild.currentResult == 'SUCCESS'}
            }
            
            steps {
                gradlew 'jar'
                archiveArtifacts "${libsDir}/${appName}-${appVersion}.jar"
                gradlew 'war'
                archiveArtifacts "${libsDir}/${appName}-${appVersion}.war"
                deploy warDir: libsDir, appName: appName, appVersion: appVersion
            }
        }
    }
    
    //Si vous définissez un bloc de poste au même niveau que le bloc d'étapes
    //Il est possible de définir le traitement une fois que toutes les étapes de traitement sont terminées.
    post {
        always {
            //Supprimez enfin le contenu de l'espace de travail
            deleteDir()
        }
        //Envoyez-vous un e-mail sauf si vous réussissez dans une rangée

        //Quand le résultat change depuis la dernière fois
        changed {
            sendMail("${currentBuild.previousBuild.result} => ${currentBuild.currentResult}")
        }
        //Quand ça échoue
        failure {
            sendMail(currentBuild.currentResult)
        }
        //Lorsqu'il est instable (principalement lorsque le test échoue)
        unstable {
            sendMail(currentBuild.currentResult)
        }
    }
}


//Exécutez la commande Gradlew
def gradlew(command) {
    if(isUnix()) {
        sh "./gradlew ${command} --stacktrace"
    } else {
        bat "./gradlew.bat ${command} --stacktrace"
    }
}

//Déployer
// args.répertoire de stockage warDir war
// args.appName Nom de l'application
// args.version de l'application appVersion
def deploy(Map args) {
    //Chemin de la clé privée * Puisque le fichier est transféré vers le serveur Tomcat, il est nécessaire de stocker la clé privée quelque part dans le serveur Jenkins à l'avance.
    def keyDir = '/var/lib/jenkins/.ssh/xxx'
    //Adresse du serveur Tomcat et nom d'utilisateur
    def webServerAddress = 'ecX-XX-XXX-X-X.xx-xxxx-x.xxxxxxxx'
    def webServerUser = 'hoge-user'
    def webServer = "${webServerUser}@${webServerAddress}"
    
    def srcWar = "${args.appName}-${args.appVersion}.war"
    def destWar = "${args.appName}.war"
    
    //Transférer des fichiers et placer la guerre dans les applications Web de Tomcat
    sh "sudo -S scp -i ${keyDir} ./${args.warDir}/${srcWar} ${webServer}:/home/ec2-user"
    sh "sudo -S ssh -i ${keyDir} ${webServer} \"sudo cp /home/ec2-user/${srcWar} /usr/share/tomcat8/webapps/${destWar}\""
}

//Envoyer des e-mails à Gmail
def sendMail(result) {
    mail to: "[email protected]",
        subject: "${env.JOB_NAME} #${env.BUILD_NUMBER} [${result}]",
        body: "Build URL: ${env.BUILD_URL}.\n\n"
}

Trébucher

image

image

image

image

build.gradle Jenkins lui-même exécute simplement les commandes Gradle et génère un rapport basé sur la sortie, donc L'application Java build.gradle de Gradle doit être capable d'exécuter les processus suivants. Créez également un wrapper Gradle pour ne pas avoir à installer Gradle avec Jenkins.

Par exemple,

build.gradle


apply plugin: 'java'
apply plugin: 'war'
apply plugin: 'checkstyle'
apply plugin: 'findbugs'
apply plugin: 'pmd'
apply plugin: 'jacoco'

ext {
    appVersion = '1.0.0'
    appName = 'SampleApp'
    javaVersion = 1.8
    defaultEncoding = 'UTF-8'
}

sourceCompatibility = javaVersion
targetCompatibility  = javaVersion
tasks.withType(AbstractCompile)*.options*.encoding = defaultEncoding
tasks.withType(GroovyCompile)*.groovyOptions*.encoding = defaultEncoding
mainClassName = 'jp.takumon.sapmleapp.App'

repositories {
    mavenCentral()
}

dependencies {
    //Liste des bibliothèques dépendantes

    compile group: 'junit', name: 'junit', version: '4.12'
}

jar {
    baseName = appName
    version =  appVersion
}

war {
    baseName = appName
    version =  appVersion
}

checkstyle {
    //Continuer le traitement ultérieur même s'il échoue
    ignoreFailures = true
    sourceSets = [sourceSets.main]
    toolVersion = '7.6.1'
}

findbugs {
    //Continuer le traitement ultérieur même s'il échoue
    ignoreFailures = true
    sourceSets = [sourceSets.main]
    toolVersion = "3.0.1"
}

pmd {
    //Continuer le traitement ultérieur même s'il échoue
    ignoreFailures = true
    sourceSets = [sourceSets.main]
}

tasks.withType(Pmd) {
    reports {
      xml.enabled = true
    }
}

//Ajout de CPD (traitement de vérification de code en double) pour vérifier la tâche
check.doLast {
    File outputDir = new File("$reportsDir/cpd/")
    outputDir.mkdirs()
  
    ant.taskdef(
        name: 'cpd', 
        classname: 'net.sourceforge.pmd.cpd.CPDTask',
        classpath: configurations.pmd.asPath)
  
    ant.cpd(
        minimumTokenCount: '100',
        format: 'xml',
        encoding: defaultEncoding,
        outputFile: new File(outputDir, 'cpd.xml')
    ) {
        fileset(dir: "src/main/java") {
            include(name: '**/*.java')
        }
    }
}

javadoc {
    failOnError = false
    //À votre niveau préféré
    options.memberLevel = JavadocMemberLevel.PRIVATE
}

test {
    //Continuer le traitement ultérieur même s'il échoue
    ignoreFailures = true
    reports {
        junitXml.enabled = true
    }
}

jacoco {
    toolVersion = '0.7.5.201505241946'
}

jacocoTestReport {
    reports {
      xml.enabled = true
    }
    
    //Exclure la classe de test du rapport de couverture
    afterEvaluate { 
        classDirectories = files(classDirectories.files.collect {
            fileTree(dir: it, exclude: ['**/*Test.class']) 
        })
    }
}

task wrapper (type: Wrapper) {
    gradleVersion = '3.4.1'
}

c'est tout.

Recommended Posts

J'ai écrit un fichier Jenkins avec Declarative Pipeline (Checkstyle, Findbugs, PMD, CPD, etc.)
J'ai créé Code Pipeline avec AWS CDK.
Je souhaite surveiller un fichier spécifique avec WatchService
J'ai essayé OCR de traiter un fichier PDF avec Java
J'ai écrit un test avec Spring Boot + JUnit 5 maintenant
J'ai écrit un test CRUD avec SpringBoot + MyBatis + DBUnit (Partie 1)
J'ai essayé OCR de traiter un fichier PDF avec Java part2