Jenkins tutorial implements GitLab to trigger Jenkins to automatically publish the front end by module

Keywords: git jenkins

Wedge

The last article solved the requirement of automatic triggering of submit / merge requests, but all front-end modules are in the same code warehouse. How to obtain the change file path and determine which module to publish? This article will take you to solve this problem.

thinking

Solve three problems respectively:

  1. Get a list of changed files
  2. Determine the module according to the file list
  3. Build and publish scripts

process

The GitLab event triggered Jenkins construction is just a start signal. To obtain the list of change files, we need to know the version number of a warehouse during the last construction. Here, Jenkins plug-in git plugin has helped us realize this work. Therefore, you only need to check out the code through git plugin.

Check out code

    checkout([
        $class: 'GitSCM',
        branches: [[name: "*/$branchName"]],
        doGenerateSubmoduleConfigurations: false,
        extensions: [
            [$class: 'RelativeTargetDirectory',
            relativeTargetDir: "$relativeTarget"]
        ],
        submoduleCfg: [],
        userRemoteConfigs: [
            [credentialsId: "$credentialsId", url: "$gitUrl"]
        ]
    ])

Please replace $branchName as the branch name, $relativeTarget as the check-out relative path, $credentialsId as the user credentials and $gitUrl as the GIT warehouse address.

Get change file list

//Get the list of changed files and return to HashSet. Note that the added affected file path does not contain the warehouse directory name
@NonCPS
def getChangeFilePathSet() {
    def changedFiles = new HashSet<String>();
    echo "Start getting the list of changed files"
    for (int i = 0; i < currentBuild.changeSets.size(); i++) {
        def entries = currentBuild.changeSets[i].items
        for (int j = 0; j < entries.length; j++) {
            def entry = entries[j]
            changedFiles.addAll(entry.getAffectedPaths());
        }
    }
    println 'Output modified file list:' + changedFiles
    return changedFiles;
}

This method can be placed outside the pipeline block and directly referenced in the script block. The implementation idea is to access currentBuild.changeSets to obtain the change list of this build compared with the last build. The HashSet is returned for convenience, and other containers can also be used.

Note: each file in the changed file list is the path relative to its warehouse!

Change the file list truncation string, obtain the module list and remove the duplicate

//Gets the set set of the automatic publishing module at the front end of the consolidated report.
//pathPrefix is the module path prefix, such as develop/@gc
@NonCPS
def getAutoPublishModuleSet(pathPrefix) {
    //Use the Set container to remove the weight and ensure that there is only one module to be released
    def modulePaths = new HashSet<String>();
    for(def filePath in getChangeFilePathSet()){
        //Ignore files that are not front-end modules, such as Jenkinsfile
        if(filePath.startsWith(pathPrefix)){
            //Gets the position of the next / starting from the subscript that exceeds the length of the module prefix. String position
            int index = filePath.indexOf('/', pathPrefix.length()+1)
            //Get the module path by string, such as develop/@gc/test
            def modulePath = filePath.substring(0, index)
            println 'add module path: ' + modulePath
            modulePaths.add(modulePath)
        }
    }
    println 'Output the list of modules to be released:' + modulePaths
    return modulePaths;
}

Write a build publish Shell script

publish-web-module.sh

#!/bin/bash
#This script is used to build the publishing front-end module, @ author: Hellxz
#$1: release version / $2: module directory
set -eu

echo "------------Start publishing $2 modular------------>"
cd $2
echo "clear dist node_modules package-lock.json ......"
rm -rf dist node_modules package-lock.json
echo "Installing dependencies"
npm i
echo "Start building"
npm run build:dev
echo "Start publishing"
npm --no-git-tag-version version $1
npm publish
echo "<------------release $2 Module complete------------"

cd ${WORKSPACE}/web; #Back to the front-end source directory
exit 0;

Loop call build publish script

for(def modulePath in modulePaths){
    sh label: "Build release front end module ${publishVersion} ${modulePath}", 
       script: "bash ${SHELL_PATH}/publish-web-module.sh ${publishVersion} ${modulePath}"
}

Pipeline example

Jenkinsfile

pipeline{
    agent any;
    environment{
        gitUrl="http://xxxxxxxx/xxxx/web.git"
        branchName=dev
        relativeTarget="web"
        credentialsId=credentials('git-user')
        pathPrefix="develop/@gc"
        publishVersion="v1.0"
        npmRepo="http://xxxxxx/nexus/repository/npm-public/"
        npmToken=credentials('npm-token')
    }
    stages{
        stage("Check out code"){
            steps{
                script {
                    cleanWs()
                    checkoutRepo("master", "jenkins", "${credentialsId}", "http://xxxxxxxx/xxxx/jenkins.git")
                    checkoutRepo("${branchName}", "${relativeTarget}", "${credentialsId}", "${gitUrl}")
                }
            }
        }
        stage("Build release"){
            steps{
                script{
                    sh label: "set up npm Warehouse", script: "npm set registry ${npmRepo}"
                    sh label: "Sign in npm Warehouse", script: "npm config set //xxxxxx/nexus/repository/npm-public/:_authToken ${npmToken}"
                    def modulePaths = getAutoPublishModuleSet(env.pathPrefix)
                    for(def modulePath in modulePaths){
                        sh label: "Build release front end module ${publishVersion} ${modulePath}", 
                           script: "bash ${SHELL_PATH}/publish-web-module.sh ${publishVersion} ${modulePath}"
                    }
                }
            }
            post{
                script{
                    cleanWs()
                }
            }
        }
    }
}

//Method of extracting check-out code
@NonCPS
def checkoutRepo(branchName, relativeTarget, credentialsId, gitUrl){
    checkout([
        $class: 'GitSCM',
        branches: [[name: "*/$branchName"]],
        doGenerateSubmoduleConfigurations: false,
        extensions: [
            [$class: 'RelativeTargetDirectory',
            relativeTargetDir: "$relativeTarget"]
        ],
        submoduleCfg: [],
        userRemoteConfigs: [
            [credentialsId: "$credentialsId", url: "$gitUrl"]
        ]
    ])
}

//Get the list of changed files and return to HashSet. Note that the added affected file path does not contain the warehouse directory name
@NonCPS
def getChangeFilePathSet() {
    def changedFiles = new HashSet<String>();
    echo "Start getting the list of changed files"
    for (int i = 0; i < currentBuild.changeSets.size(); i++) {
        def entries = currentBuild.changeSets[i].items
        for (int j = 0; j < entries.length; j++) {
            def entry = entries[j]
            changedFiles.addAll(entry.getAffectedPaths());
        }
    }
    println 'Output modified file list:' + changedFiles
    return changedFiles;
}

//Gets the set set of the automatic publishing module at the front end of the consolidated report.
@NonCPS
def getAutoPublishModuleSet(pathPrefix) {
    //Use the Set container to remove the weight and ensure that there is only one module to be released
    def modulePaths = new HashSet<String>();
    for(def filePath in getChangeFilePathSet()){
        //Ignore files that are not front-end modules, such as Jenkinsfile
        if(filePath.startsWith(pathPrefix)){
            //Gets the position of the next / starting from the subscript that exceeds the length of the module prefix. String position
            int index = filePath.indexOf('/', pathPrefix.length()+1)
            //Get the module path by string, such as develop/@gc/test
            def modulePath = filePath.substring(0, index)
            println 'add module path: ' + modulePath
            modulePaths.add(modulePath)
        }
    }
    println 'Output the list of modules to be released:' + modulePaths
    return modulePaths;
}

It's just for reference. I put the extracted methods into the shared library and write scripts more clearly and briefly.

Any questions

  • The submission record will not be recognized during the first build, and may be missed once
  • If you cut to a branch that has not been built, it will also be missed once
  • Limited to the length of the article, the function of manually transmitting parameters to the specified module is not added

For the problem of missing distribution detected by multiple branches for the first time, this is because there is no reference to the same branch submission ID for reference. It is not a technical problem in itself. All front-end distribution branches are submitted in advance. As long as the construction is triggered, there will be no missing distribution in the future.

last

I hope it can inspire you. If you have a more elegant implementation or there are mistakes in the article, I hope you can comment and point out. Thank you.

This article is published in blog Park (northeast little fox) https://www.cnblogs.com/hellxz/ )And CSDN (northeast little fox hellxz) https://blog.csdn.net/u012586326 )Reprint is prohibited.

Posted by deep on Sat, 18 Sep 2021 21:12:36 -0700