Jenkinsfile - 파라미터를 받는 실질적인 빌드 Job

2021. 3. 14. 02:59버전관리 및 빌드/Jenkins

파라미터를 입력값으로 받는 Job 인 경우에는 "General" 섹션에서 다음을 체크하여야 한다.

 

[v] This project is parameterized

 

옵션 바로 밑의 "Add Parameters ▼" 를 클릭하면 파라미터를 추가할 수 있는 form fields 가 나오는데

본 기사에서는 파이프라인으로 다룰 것이기 때문에 그냥 넘어가도 된다. (Job 실행 시 나중에 파라미터 사용내역이 이력으로 남음)

 

 

 

스크립트 소스 :

pipeline {
    agent any
    tools {
        maven 'Maven'
        jdk 'JDK'
    }
    parameters {
        string(defaultValue: 'dev',                            name: 'BUILD_TYPE',                description: 'name of sub directory, for workspace')
        string(defaultValue: 'master',                         name: 'GIT_BRANCH',                description: 'git branch to pull down')
        string(defaultValue: 'myServDev.war',                  name: 'FILE_NAME',                 description: 'name of .war file, ex: myApp.war')
        string(defaultValue: 'Building for Dev application',   name: 'DIST_SSH_SVR_TITLE',        description: 'title to print on console log, ex: My Dev build')
        string(defaultValue: '127.0.0.1',                      name: 'DIST_SSH_SVR_IP',           description: 'specify the ip of ssh server to distribute ".war" file')
        string(defaultValue: '/home/dev/test',                 name: 'DIST_SSH_PUT_LOCATION',     description: 'canonical path of the directory to transfer ".war" file')
    }
    environment {
        BUILD_TYPE = "${params.BUILD_TYPE}"
        WORKSPACE = "${env.WORKSPACE}/${BUILD_TYPE}"
        
        GIT_BRANCH = "${params.GIT_BRANCH}"
        GIT_REMOTE_BRANCH = "refs/remotes/origin/${env.GIT_BRANCH}"
        GIT_URL = 'http://localhost/git/myProject'

        TARGET_WAR = "${env.WORKSPACE}/target/${params.FILE_NAME}"
        DIST_SSH_SVR_TITLE = "${params.DIST_SSH_SVR_TITLE}"
        DIST_SSH_SVR_IP = "${params.DIST_SSH_SVR_IP}"
        DIST_SSH_PUT_LOCATION = "${params.DIST_SSH_PUT_LOCATION}"

        LOCAL_FIRST_CHECK = 'true'
        REMOTE_FIRST_CHECK = 'true'
        ERROR_MESG = 'variable_not_set'

        EMAIL_RECEPIENTS = 'staff@hey-yo.com, staff-2@hey-yo.com'
        EMAIL_SENDER = 'notice@myServApps.com'
    }

    stages {
        stage('빌드 대상 플랫폼') {
            steps {
                echo "${DIST_SSH_SVR_TITLE}"
            }
        }
        stage('.git 검사') {
            steps {
                script {
                    ws("${WORKSPACE}") {
                        if (fileExists('.git')) {
                            echo 'found .git'
                        }
                        else {
                            git branch: "${GIT_BRANCH}", credentialsId: 'cc_ssh', url: "${GIT_URL}"
                            env.IS_GIT_INIT = "true"
                        }
                    }
                }
            }
        }
        stage('깃 변경여부 검사') {
            steps {
                checkBranchUsed()
                getCommitLocal()
                getCommitRemote()
                compareLocalAndRemote()
            }
        }
        stage('메이븐으로 컴파일') {
            steps {
                script {
                    if (GIT_CHANGED == "true") {// THIS IS TRUE
                        ws("${WORKSPACE}") {
                            try {
                                sh 'mvn clean'
                                sh 'mvn package'
                            }
                            catch (err) {
                                echo "Compile Failed"
                                error ${err}
                            }
                        }
                    }
                }
            }
        }
        stage('war 파일 생성여부 검사') {
            steps {
                script {
                    if (GIT_CHANGED == "true") {// THIS IS TRUE
                        if (fileExists(env.TARGET_WAR)) {
                            echo 'fileExists -- Yes!'
                        }
                        else {
                            env.ERROR_MESG = "${env.TARGET_WAR} NOT FOUND!"
                            error "${env.ERROR_MESG}"
                        }
                    }
                }
            }
        }
        stage('SSH 로 war 배포 단계') {
            steps {
                script {
                    if (GIT_CHANGED == "true") {// THIS IS TRUE
                        // distLocal()
                        distSSH()
                    }
                }
            }
        }
    }
    post {
        success {
            script {
                if (env.GIT_CHANGED == "true") {// THIS IS TRUE
                    env.SUCCESS_TITLE = 'from Jenkins -- myServ 앱 배포 성공'
                    env.SUCCESS_MESG = "(기존 ${GIT_BRANCH} 브랜치)\n " + "${CURRENT_LOCAL_COMMIT}\n" + "${GIT_LOCAL_COMMIT_DETAIL}\n\n" + "(가져온 ${GIT_REMOTE_BRANCH} 브랜치)\n " + "${CURRENT_REMOTE_COMMIT}\n" + "${GIT_REMOTE_COMMIT_DETAIL}\n\n" + "배포일시 : ${BUILD_TIMESTAMP}"

                    mail(subject: env.SUCCESS_TITLE, body: env.SUCCESS_MESG, to: env.EMAIL_RECEPIENTS, from: env.EMAIL_SENDER)
                }
            }
        }
        failure {
            script {
                env.FAILURE_TITLE = 'from Jenkins -- myServ 앱 배포 실패'
                env.FAILURE_MESG = "(기존 ${GIT_BRANCH} 브랜치)\n " + "${CURRENT_LOCAL_COMMIT}\n" + "${GIT_LOCAL_COMMIT_DETAIL}\n\n" + "(빌드 시도 ${GIT_REMOTE_BRANCH} 브랜치)\n " + "${CURRENT_REMOTE_COMMIT}\n" + "${GIT_REMOTE_COMMIT_DETAIL}\n\n" + "${WORKSPACE}"

                ws("${WORKSPACE}") {
                    if (bothCommitsAreSame() == "true") {
                        // 빌드 실패 발생 시 `다음 번 실행을 위해` 작업경로의 Git commit 를 한 단계 앞으로 되돌림
                        withCredentials([sshUserPrivateKey(credentialsId: 'cc_ssh', keyFileVariable: 'PASSWORD')]) {
                            sh 'git reset --hard HEAD~1'
                        }
                    }
                    else {
                        // Merge 하기 전 commit ID 를 알고 있는 경우 그것으로 되돌림
                        withCredentials([sshUserPrivateKey(credentialsId: 'cc_ssh', keyFileVariable: 'PASSWORD')]) {
                            sh 'git reset --hard "${CURRENT_LOCAL_COMMIT}"'
                        }
                    }
                }
                mail(subject: env.FAILURE_TITLE, body: env.FAILURE_MESG, to: env.EMAIL_RECEPIENTS, from: env.EMAIL_SENDER)
            }
        }
    }
}

/* SSH 로 파일을 전송한다 

   (젠킨스 우선 설정)
   1) {원격계정 홈}/.ssh/known_hosts 에 젠킨스가 설치된 서버계정의 public key 값을 추가한다
   2) {젠킨스 설정}/manage credentials/ 에서 `Username & Password` 항목으로 `접속 계정정보`를 추가한다. 이 때 ID 를 아래 코드와 동일하게 ('distributor_ssh' 로) 기입한다.
   3) 이제 아래 코드를 실행해도 된다.
*/
def distSSH() {
    def remoteObj = [:]
    remoteObj.name = "${DIST_SSH_SVR_TITLE}"
    remoteObj.host = "${DIST_SSH_SVR_IP}"
    remoteObj.allowAnyHosts = true
    
    withCredentials([usernamePassword(credentialsId: 'distributor_ssh', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
        remoteObj.user = USERNAME
        remoteObj.password = PASSWORD
        sshPut remote: remoteObj, from: TARGET_WAR, into: DIST_SSH_PUT_LOCATION
        sshCommand remote: remoteObj, command: DIST_SSH_PUT_LOCATION + '/run.sh stop && ' + DIST_SSH_PUT_LOCATION + '/run.sh start'
    }
}

def checkBranchUsed() {
    ws("${WORKSPACE}") {
        env.BR_USED_B4 = sh (
            script: '''
                #!/usr/bin/bash
                set +x
                git branch | grep "*" | cut -d " " -f 2
            ''',
            returnStdout: true
        ).trim()
        
        if (env.BR_USED_B4 && "${BR_USED_B4}" != "${GIT_BRANCH}") {
            echo '----------------------------------------------------'
            echo "바로 전에 빌드했던 브랜치(${BR_USED_B4})와 현재 빌드 대상 브랜치(${GIT_BRANCH})가 일치하지 않아 작업디렉토리를 초기화합니다."
            echo "${WORKSPACE}"

            sh """
                set +x
                rm -rf ${WORKSPACE}/*
                rm -rf ${WORKSPACE}/.git*
                rm -rf ${WORKSPACE}/.mvn
            """
            echo '----------------------------------------------------'
            
            git branch: "${GIT_BRANCH}", credentialsId: 'cc_ssh', url: "${GIT_URL}"
            env.IS_GIT_INIT = "true"
        }
    }
}

def getCommitLocal() {
    script {
        // echo 'call getCommitLocal()'

        ws("${WORKSPACE}") {
            if ("${LOCAL_FIRST_CHECK}" == "true") {
                withCredentials([sshUserPrivateKey(credentialsId: 'cc_ssh', keyFileVariable: 'PASSWORD')]) {
                    sh 'git rev-parse --is-inside-work-tree'
        
                    env.CURRENT_LOCAL_COMMIT = sh (
                        script: 'git rev-parse "${GIT_BRANCH}"',
                        returnStdout: true
                    ).trim()
                    
                    env.GIT_LOCAL_COMMIT_DETAIL = sh (
                        script: 'git show -s --oneline "${GIT_BRANCH}"',
                        returnStdout: true
                    ).trim()
                }
                
                env.LOCAL_FIRST_CHECK = 'false'
            }
        }        
        echo '----------------------------------------------------'
        echo "${GIT_BRANCH} : " + "${CURRENT_LOCAL_COMMIT}"
        echo "${GIT_BRANCH} : " + "${GIT_LOCAL_COMMIT_DETAIL}"
        echo '----------------------------------------------------'
    }
}

def getCommitRemote() {
    script {
        
        if ("${REMOTE_FIRST_CHECK}" == "true") {
            ws("${WORKSPACE}") {
                git branch: "${GIT_BRANCH}", credentialsId: 'cc_ssh', url: "${GIT_URL}"
                    
                withCredentials([sshUserPrivateKey(credentialsId: 'cc_ssh', keyFileVariable: 'PASSWORD')]) {
                    env.CURRENT_REMOTE_COMMIT = sh (
                        script: 'git rev-parse "${GIT_REMOTE_BRANCH}"',
                        returnStdout: true
                    ).trim()
                    
                    env.GIT_REMOTE_COMMIT_DETAIL = sh (
                        script: 'git show -s --oneline "${GIT_REMOTE_BRANCH}"',
                        returnStdout: true
                    ).trim()
                }
                env.REMOTE_FIRST_CHECK = 'false'
            }
        }
        
        echo '----------------------------------------------------'
        echo "${GIT_REMOTE_BRANCH} : " + "${CURRENT_REMOTE_COMMIT}"
        echo "${GIT_REMOTE_BRANCH} : " + "${GIT_REMOTE_COMMIT_DETAIL}"
        echo '----------------------------------------------------'
    }
}

def compareLocalAndRemote() {
    script {
        if (bothCommitsAreSame() == "true") {

            /* 기존 소스트리의 브랜치가 refs/remotes/origin/브랜치와 같으나
            REPO 에서 처음 가져온 소스라면 `빌드를 허용`! */
            if ("${env.IS_GIT_INIT}" == "true") {
                env.GIT_CHANGED = "true"
                echo '워크스페이스에 처음 가져온 소스이므로 빌드 및 배포를 허용함'
            }
            else {/* 기존 소스트리에서 검사했을 때 로컬과 리모트가 동일한 경우, `빌드 비허용` */
                env.GIT_CHANGED = "false"
                echo '워크스페이스의 소스와 리모트 간에 Revision 이 동일하므로 빌드 및 배포를 하지 않습니다'
            }
        }
        else {
            env.GIT_CHANGED = "true"
        }
    }
}

def bothCommitsAreSame() {
    script {
        /*
         * CURRENT_LOCAL_COMMIT, CURRENT_REMOTE_COMMIT 값을 모르는 경우,
         * 파이프라인 개발자가 소스를 한 번 더 들여다 볼 수 있도록
         * true 를 반환해서 빌드가 실행되는 것을 막는다.
        */
        if ("${CURRENT_LOCAL_COMMIT}" == "" || "${CURRENT_LOCAL_COMMIT}" == "null") {
            echo 'CURRENT_LOCAL_COMMIT 값이 없네요. getCommitLocal() 를 먼저 실행해야 합니다!'
            return "true"
        }
        else if ("${CURRENT_REMOTE_COMMIT}" == "" || "${CURRENT_REMOTE_COMMIT}" == "null") {
            echo 'CURRENT_REMOTE_COMMIT 값이 없네요. getCommitRemote() 를 먼저 실행해야 합니다!'
            return "true"
        }
        
        // 여기서부터 진짜 비교
        else if ("${CURRENT_LOCAL_COMMIT}" == "${CURRENT_REMOTE_COMMIT}") {
            return "true"
        }
        else {
            return "false"
        }
    }
}

def buildEmailContent() {
    script {

        env.NOJOB_TITLE = 'from Jenkins -- No job'
        env.NOJOB_MESG = "(기존 ${GIT_BRANCH} 브랜치)\n " + "${CURRENT_LOCAL_COMMIT}\n" + "${GIT_LOCAL_COMMIT_DETAIL}\n\n" + "(${GIT_REMOTE_BRANCH} 브랜치)\n " + "${CURRENT_REMOTE_COMMIT}\n" + "${GIT_REMOTE_COMMIT_DETAIL}\n\n" + "로컬 저장소의 커밋과 원격 저장소의 커밋이 일치하므로 빌드 및 배포를 하지 않음."
    }
}

'버전관리 및 빌드 > Jenkins' 카테고리의 다른 글

Jenkinsfile - binding Parameters  (0) 2021.03.14
Jenkins 서비스 구축 후기  (0) 2021.03.08