Jenkins + Groovy script = efficient ✔✔ (pure dry goods)

Posted by banacan on Sun, 02 Jan 2022 22:56:44 +0100

1, Configuring Groovy in IntelliJ IDEA

The mode of writing script in Jenkinsfile is to verify the script in IDE and then copy the code to Jenkinsfile. For writing Groovy script in IntelliJ IDEA, please refer to this article IntelliJ IDEA configuring Groovy tutorial

2, Using Groovy scripts in Pipeline

1. Use the method provided by the default jdk

Jenkinsfile supports the definition directly outside the Pipeline block. We can imagine the whole script as a class, pipeline block is main function, and it is the entrance of the program, and custom class can be called in main function, of course. Here we will conduct security check, and do not check groovy sandbox on the configuration page.

pipeline{
	angent any
	stages{
		stage("Hello World"){
				helloWrold()
		}

		stage("Read file test"){
				getFileText()
		}
  }
}

// ================= Groovy Script ===============

def helloWrold(){
	println("Hello World")
}

def getFileText(file){
	return (new File(file)).text
}

Smart, you must think of which libraries you can use? Generally speaking, if your Jenkins version comes with its own jdk during installation, all libraries in the jdk can be called by default. Before writing a script for pipeline, you can verify whether the script function references other third-party libraries in other editors. In the example, it is obvious that the Date class is in the jdk by default, so you do not need to import the package

2. Use the methods in the third-party library

Refer to @ Grab in the article https://www.jenkins.io/doc/book/pipeline/shared-libraries/

The default Jenkins will not go to% JAVA_HOME% or% grovey_ Home% path to read the third-party package, which is different from IntelliJ IDEA and other ides. Instead, it will go to the user folder C: \ users \ chao9441 \ To read groovy \ grades, you need to place the jar package in this directory

pipeline{
	angent any
environment{
      SourceDir = "D:\\Temp\\1\\"
      DisDir = "D:\\Temp\\2\\"
  }
	stages{
		stage("Use 3rd party function"){
				groovyCopyDir("${SourceDir}", "${DisDir}")
		}
  }
}

// ================= Groovy Script ===============

@Grab(group='commons-io', module='commons-io', version='2.11.0')
import org.apache.commons.io.FileUtils
def void groovyCopyDir(String srcDir, String disDir){
    def srcFile = new File(srcDir)
    def disFile = new File(disDir)
    FileUtils.copyDirectory(srcFile,disFile)
}

Here we will use the third-party library Apache Commons IO https://commons.apache.org/proper/commons-io/ The process steps are

  • Download the package commons-io-2.11.0 Jar placed on individual grapes directory under groovy folder
  • Reference the package @ Grab(group='commons-io', module='commons-io', version='2.11.0 ') in Jenkinsfile
  • Import specific libraries, using the methods provided

Tip: don't implement the logic of copying files in Groovy. You need to give it to the plug-in or directly use the shell command. This is just a demonstration

For information about @ Grab annotation, please refer to the reference document Dependency management with Grape Generally speaking, the package download page will have @ Grab

In this way, we can use the third-party library in Jenkinsfile

3, Reuse code using Shared Libraries

Smart, you must have found a problem. Although you can customize methods and even reference the functions of third-party libraries in Jenkinsfile, it is limited to a single Jenkinsfile. What if there are many jenkinsfiles that want to use the same code? Here we need to use Shared Libraries to encapsulate the code

About how to configure Official documents It's very clear. I won't repeat it here. You still need to add the third-party library to the personal grades directory of the master node, and then import the package with the @ Grab annotation in the custom Google class

@Grab(group='commons-io', module='commons-io', version='2.11.0')
import org.apache.commons.io.FileUtils
class CustomUtil{
	def void groovyCopyDir(String srcDir, String disDir){
    def srcFile = new File(srcDir)
    def disFile = new File(disDir)
    FileUtils.copyDirectory(srcFile,disFile)
}

Use this in Jenkinsfile

// Replace with your own shared library
@Library(['jenkins-shared-libs'])_
import com.esri.CustomUtils

pipeline{
	angent any
	environment{
      SourceDir = "D:\\Temp\\1\\"
      DisDir = "D:\\Temp\\2\\"
  }
	stages{
		stage("Use 3rd party function"){
				CustomUtils.groovyCopyDir("${SourceDir}", "${DisDir}")
		}
  }
}

4, Script security check

1. No groovy sandbox check

Jenkins will check the security of the script by default. As long as a specific type declaration appears in the script, it needs to be approved manually. At this time, Sandbox testing is used by default

pipeline{
    agent any
    stages{
        stage("test"){
            steps{
                script{
                    test()
                }
            }
        }
    }
}

def test(){
    ArrayList list = new ArrayList()
    println("Script approval is required to declare a type on the pipeline!")
}

In this example, there is a declaration of ArrayList type, so you will find an error after execution. It can only be executed after manual approval

If you do not want to manually approve each time, you can ignore the sandbox check and it will succeed

Obviously, the Pipeline script in the production environment will not be written manually in the project configuration environment, but will be placed in a script warehouse and configured from the script warehouse checkout. By default, the sandbox will be opened. So how to avoid manual script checking?

2. The specific type is always declared in the shared library

The Jenkins shared library has the highest security level. It has all permissions by default. The effect is equivalent to not applicable to groovy sandbox. Therefore, the code of shared library should be quite cautious. For the configuration of shared libraries, please refer to the official documents

Write a test method in the shared library. This method should strictly follow the Java syntax and import the library used, not as short as Groovy, as shown in the following example

// The library used for importing types is very important and cannot be omitted
import java.util.ArrayList

// The return type must be strictly defined. If there is no return, it will be void. groovy shorthand cannot be used
def static void testArr(){
      ArrayList list = new ArrayList()
      println("Types in shared libraries are safe by default and do not require script approval!")
}

/* You can't use shorthand. This will fail and prompt the method without signature
// hudson.remoting.ProxyException: groovy.lang.MissingMethodException: No signature of method: static com.esri.CommonUtils.testArr() is applicable for argument types: () values: []
def static testArr(){
      ArrayList list = new ArrayList()
      println("The types in the shared library are safe by default, and script approval is not required! ")
}
*/

Code that references a shared library in Pipeline

@Library('jenkins-shared-libs@chao9441/use_3rd_party_lib')_
import com.esri.CommonUtils

pipeline{
    agent any
    stages{
        stage("test"){
            steps{
                script{
                    CommonUtils.testArr()
                }
            }
        }
    }
}


Another more advanced usage is that we can take the class in the shared library as a factory class, instantiate the class used in security here, and then call the instantiated object in Piepline, so as to avoid directly instantiating objects in Piepline and maximally self defining functions.

For example, an instance of File is returned in the shared library

import java.io.File
def static File createNewFile(String filepath){
        return new File(filepath)
}

This instance can be used in custom methods in Pipeline

@Library('jenkins-shared-libs@chao9441/use_3rd_party_lib')_
import com.esri.CommonUtils

pipeline{
    agent any
    stages{
        stage("test"){
            steps{
                script{
                    def content = readContent("F:\\test.txt")
                    print(content)
                }
            }
        }
    }
}

def readContent(path){
    // The specific instantiation is placed in the shared library to ensure security
    def file = CommonUtils.createNewFile(path)
    return file.text
}

5, Custom function parameter passing in Piepline

If you don't want your script to manually approve some libraries, instantiate objects in the shared library. The custom functions in pipeline, whether input parameters or return parameters, can only be strings, and some necessary declarations can be omitted

@Library('jenkins-shared-libs@chao9441/use_3rd_party_lib')_
import com.esri.CommonUtils

pipeline{
    agent any
    stages{
        stage("test"){
            steps{
                script{
                   .....
                }
            }
        }
    }
}

// ===================================

// The default input parameter is string. Do not write String par, and do not write void for the return parameter
def method1(par){
   ....
}

def method2(){
   ....
}

// The default return parameter is string
def method3(){
  retun sss
}

6, Best practice summary

  • In the shared library as a factory class, returns the instantiated object, and then calls the object in Piepline. The custom function in Piepline does not involve instantiation and object declaration of any class.
  • All custom function input and return types in Piepline are strings, no other specific types are used, and groovy is used to ignore the necessary parameters to avoid manually approving script types
  • The shared library provides some small public methods, class instantiation objects or string operations. It is officially recommended not to write some complex logic in the shared library. In the Jenkins platform, it rarely involves the implementation of specific execution, because any Groovy operation will eat memory and CPU, and the speed is not fast. Jenkins's specific execution behavior will be handed over to the plug-in or directly implemented by shell commands. It itself is a role of scheduling and command! For example, Groovy script can also copy files, but it is much slower than calling system commands directly

Topics: jenkins DevOps