Skip to content

语法

内置语法

http://192.168.0.137:9191/pipeline-syntax/

https://jenkins.io/zh/doc/book/blueocean/creating-pipelines/

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                println "Build"
            }
        }

        stage('Test') {
            steps {
                println "Test"
            }
        }

        stage('Deploy') {
            steps {
                println "Deploy"
            }
        }
    }
//
    post { //这里定义的是后置处理
        success {
            // 构建成功
            echo '构建完成,正在清理工作空间'
            cleanWs();
            echo '清理工作空间完成'
        }
        failure {
            // 构建失败,这里使用sh是因为echo不支持使用参数
            sh 'echo "构建失败,详情请查看$WORKSPACE"'
        }
        aborted {
            // 构建被中止
            echo '构建被中止'
        }
    }
}
pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                println "Build"
            }
        }

        stage('Test') {
            steps {
                println "Test"
            }
        }

        stage('Deploy') {
            steps {
                println "Deploy"
            }
        }
    }
//
    post { //这里定义的是后置处理
        success {
            // 构建成功
            echo '构建完成,正在清理工作空间'
            cleanWs();
            echo '清理工作空间完成'
        }
        failure {
            // 构建失败,这里使用sh是因为echo不支持使用参数
            sh 'echo "构建失败,详情请查看$WORKSPACE"'
        }
        aborted {
            // 构建被中止
            echo '构建被中止'
        }
    }
}
  • 切换目录
 stage('Build') { 
            steps {
                dir('demo') { //切换目录到demo
                    //执行构建镜像命令,这里起作用的是maven的插件
                    //可以参考https://github.com/hellxz/SpringBoot-DockerDemo.git的使用方法,在docker-maven-plugin-2分支
                    sh 'mvn clean package docker:build -DskipTests'  
                }
            }
        }
 stage('Build') { 
            steps {
                dir('demo') { //切换目录到demo
                    //执行构建镜像命令,这里起作用的是maven的插件
                    //可以参考https://github.com/hellxz/SpringBoot-DockerDemo.git的使用方法,在docker-maven-plugin-2分支
                    sh 'mvn clean package docker:build -DskipTests'  
                }
            }
        }
  • git clone
bash
stage('Pull Git Demo') {
            steps{
                //清理工作空间
                cleanWs();
                //拉取代码
                checkout([$class: 'GitSCM', branches: [[name: '*/docker-maven-plugin-2']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'demo']], submoduleCfg: [], userRemoteConfigs: [[url: 'https://github.com/hellxz/SpringBoot-DockerDemo.git']]])
            }
        }
stage('Pull Git Demo') {
            steps{
                //清理工作空间
                cleanWs();
                //拉取代码
                checkout([$class: 'GitSCM', branches: [[name: '*/docker-maven-plugin-2']], doGenerateSubmoduleConfigurations: false, extensions: [[$class: 'RelativeTargetDirectory', relativeTargetDir: 'demo']], submoduleCfg: [], userRemoteConfigs: [[url: 'https://github.com/hellxz/SpringBoot-DockerDemo.git']]])
            }
        }

2.shell命令的标准输出或者状态

//获取标准输出
//第一种
result = sh returnStdout: true ,script: "<shell command>"
result = result.trim()
//第二种
result = sh (script: "<shell command>", returnStdout: true).trim()
//第三种
sh "<shell command> > commandResult"
result = readFile('commandResult').trim()

//获取执行状态
//第一种
result = sh returnStatus: true ,script: "<shell command>"
result = result.trim()
//第二种
result = sh (script: "<shell command>", returnStatus: true).trim()
//第三种
sh '<shell command>; echo $? > status'
def r = readFile('status').trim()
//获取标准输出
//第一种
result = sh returnStdout: true ,script: "<shell command>"
result = result.trim()
//第二种
result = sh (script: "<shell command>", returnStdout: true).trim()
//第三种
sh "<shell command> > commandResult"
result = readFile('commandResult').trim()

//获取执行状态
//第一种
result = sh returnStatus: true ,script: "<shell command>"
result = result.trim()
//第二种
result = sh (script: "<shell command>", returnStatus: true).trim()
//第三种
sh '<shell command>; echo $? > status'
def r = readFile('status').trim()

groovy语句写法为:

    def exitValue = sh(script: "grep -i 'xxx' /etc/myfolder", returnStatus: true)
    echo "return exitValue :${exitValue}"
    if(exitValue != 0){
    	执行操作
    }
如果grep命令执行没有报错,正常情况下exitValue为0,报错则为非0
    def exitValue = sh(script: "grep -i 'xxx' /etc/myfolder", returnStatus: true)
    echo "return exitValue :${exitValue}"
    if(exitValue != 0){
    	执行操作
    }
如果grep命令执行没有报错,正常情况下exitValue为0,报错则为非0
  • 输出日志
 stage('输出日志') {
            steps {
                script{
                    //调用方法得到日志 并 输出
                    def changeString = getChangeString()
                    echo "$changeString"
                }
            }
        }
 stage('输出日志') {
            steps {
                script{
                    //调用方法得到日志 并 输出
                    def changeString = getChangeString()
                    echo "$changeString"
                }
            }
        }

3.输出保存到变量

StdOut

pipeline{
  agent any
  stages{
    stage('sh输出'){
      steps{
        script{
          out = sh(
            label: 'listing directory',
            returnStdout: true,
            script: "ls /"
          )
        }
      }
    }
  }
}

#这段话将运行 script 指定的命令,也就是 ls / 的标准输出 StdOut 赋值给 out 变量
pipeline{
  agent any
  stages{
    stage('sh输出'){
      steps{
        script{
          out = sh(
            label: 'listing directory',
            returnStdout: true,
            script: "ls /"
          )
        }
      }
    }
  }
}

#这段话将运行 script 指定的命令,也就是 ls / 的标准输出 StdOut 赋值给 out 变量

StdErr

out = sh(
  label: 'listing directory',
  returnStdout: true,
  script: "ls /notExist"
)

这个命令将在标准错误 StdErr 输出下面的信息:
ls: cannot access /notExist: No such file or directory


#然而标准输出 StdOut 则为空。那么上面这种 Pipeline 写法有两个问题:

不能返回执行命令的标准错误信息,只能返回 StdOut 内容,异常情况下为空
不能抛错,使当前和后续 stage 停止,也不能中断整个任务,更不能将整个任务的状态标记为 Failure
out = sh(
  label: 'listing directory',
  returnStdout: true,
  script: "ls /notExist"
)

这个命令将在标准错误 StdErr 输出下面的信息:
ls: cannot access /notExist: No such file or directory


#然而标准输出 StdOut 则为空。那么上面这种 Pipeline 写法有两个问题:

不能返回执行命令的标准错误信息,只能返回 StdOut 内容,异常情况下为空
不能抛错,使当前和后续 stage 停止,也不能中断整个任务,更不能将整个任务的状态标记为 Failure

目前的 Jenkins 版本,不提供输出标准错误 StdErr 的方法,不过可以通过下面的做法绕过:

  • 标准错误合并输出到标准输出,再输出到文件
  • 判断运行状态 returnStatus
pipeline{
  agent any
  stages{
    stage('sh输出'){
      steps{
        script{
          status = sh(
            label: 'listing directory',
            // 返回运行状态
            returnStatus: true, 
            // 合并标准错误 StdErr 到文件和标准输出 StdOut
            script: "ls /ff \>test.log 2>&1" 
          )
          // 从文件读出标准错误 StdErr 信息
          out = readFile(file: "test.log") 
          if (status != 0) {
            // 使当前 stage 运行失败,同时输出错误信息。
            error('TEST FAILED.') 
          }
        }
      }
    }
  }
}
pipeline{
  agent any
  stages{
    stage('sh输出'){
      steps{
        script{
          status = sh(
            label: 'listing directory',
            // 返回运行状态
            returnStatus: true, 
            // 合并标准错误 StdErr 到文件和标准输出 StdOut
            script: "ls /ff \>test.log 2>&1" 
          )
          // 从文件读出标准错误 StdErr 信息
          out = readFile(file: "test.log") 
          if (status != 0) {
            // 使当前 stage 运行失败,同时输出错误信息。
            error('TEST FAILED.') 
          }
        }
      }
    }
  }
}

4.重试次数

实现重试的方法,是在 stage 内部增加 options,如下面的 Jenkinsfile 所示。

groovy
stage('some node'){
  agent {
    label 'some agent'
  }
  options{
    retry(3)
  }
  steps{
  // 其他任务
  }
}
stage('some node'){
  agent {
    label 'some agent'
  }
  options{
    retry(3)
  }
  steps{
  // 其他任务
  }
}

5.如何及时中止pipeline

场景

使用Jenkins的流水线时,有时明明某些Python或Shell执行结果错误,但仍会继续运行下去,甚至于最终提示成功。这并不合我们的预期——出现错误及时停止。当然,还有那种小错误不影响构建的,希望继续执行下去的情况。

所以,场景有两个:

  • 希望发现错误,及时中止执行的情况。
  • 希望错误发生时,脚本仍继续执行的情况。

本文以上两种场景分别给出示例。

本文不考虑能被pipeline捕获的异常中断。

及时中止,如何做?

1、对于Jenkins流水线中,使用groovy语法实现的功能,可以考虑使用Java的异常。

示例:

groovy
stage('test'){
	step{
		script{
			def res = someGroovyMethod()
			if(res == false){
				throw new RuntimeException('提示信息')
			}
		}
	}
}
stage('test'){
	step{
		script{
			def res = someGroovyMethod()
			if(res == false){
				throw new RuntimeException('提示信息')
			}
		}
	}
}

2、对于流水线中调用Shell,可以考虑匹配返回值,抛异常。

示例:

groovy
stage('test'){
	step{
		script{
			def res = sh(label:"测试分支是否存在:", script: """
						echo 'hello world!'
						""", returnStdout: true);
			if(!res.contains("hello world")){
				throw new RuntimeException('提示信息')
			}
		}
	}
}
stage('test'){
	step{
		script{
			def res = sh(label:"测试分支是否存在:", script: """
						echo 'hello world!'
						""", returnStdout: true);
			if(!res.contains("hello world")){
				throw new RuntimeException('提示信息')
			}
		}
	}
}

除了 returnStdout 用于返回执行输出,还可以使用 returnStatusreturnStatusreturnStdout 不能同时使用,returnStatus 表示脚本执行完毕的返回值是0还是非0,非0值即有问题的。

3、对于以上两种情况,只要能获取返回输出或状态,就可以终止流水线,使用 error

示例:

groovy
stage('test'){
	step{
		script{
			def res = someGroovyMethod()
			if(res == false){
				error '提示信息'
			}
		}
	}
}
stage('test'){
	step{
		script{
			def res = someGroovyMethod()
			if(res == false){
				error '提示信息'
			}
		}
	}
}

报错忽略,如何做?

1、使用异常捕获,try-catch block 或 try-catch-finally block。

示例:

groovy
stage('test'){
	step{
		script{
			try{
				someMethodMaybeThrowException()
			} catch(Exception e){
				// do something you want. e.g,print logs.
			}
		}
	}
}
stage('test'){
	step{
		script{
			try{
				someMethodMaybeThrowException()
			} catch(Exception e){
				// do something you want. e.g,print logs.
			}
		}
	}
}

2、使用Jenkins 流水线中的 catchError 忽略异常或可能中断构建的错误。

示例:

groovy
stage('test'){
	step{
		script{
			//无论是否会报错,这个stage以及构建结果都不会因这个错误而失败。
			catchError(buildResult: 'SUCCESS', catchInterruptions: false) {
			    someMethodMaybeThrowException()
			}
			//some post step will continued.
		}
	}
}
stage('test'){
	step{
		script{
			//无论是否会报错,这个stage以及构建结果都不会因这个错误而失败。
			catchError(buildResult: 'SUCCESS', catchInterruptions: false) {
			    someMethodMaybeThrowException()
			}
			//some post step will continued.
		}
	}
}

catchError 的玩法还有很多,典型应用场景是针对一些后置的操作,比如构建完成发消息给构建者,保证这个消息不会因为脚本执行中断而停止

6. try-catch

加上try-catch之后如果try中的语句出错就会被catch捕获输出错误信息,而不会终止程序

groovy
script{
    try{
        gradleHome = tool "GRADLE"
        sh """
        ${gradleHome}/bin/gradle -v
        """
    }catch(e){
        println(e)
    }
}
script{
    try{
        gradleHome = tool "GRADLE"
        sh """
        ${gradleHome}/bin/gradle -v
        """
    }catch(e){
        println(e)
    }
}

案例

groovy
import hudson.model.*;
println env.JOB_NAME
println env.BUILD_NUMBER
pipeline{
    agent any
    stages{
        
        stage("git checkout") {
            steps{
                script {
                checkout([$class: 'GitSCM', 
                branches: [[name: '*/master']], 
                doGenerateSubmoduleConfigurations: false, 
                extensions: [], 
                submoduleCfg: [], 
                userRemoteConfigs: [[credentialsId: 'e3e48ed7-dbce-4642-bb18-28e0c71ab962', 
                url: 'http://192.168.132.132/root/hello-world-greeting.git']]])
                }
            }
        }
        stage("Check file download") {
            steps {
                script {
                    try{
                    out = sh(script: "ls /opt ", returnStdout: true).toString().trim()
                        println out
                        if(out.contains("Python-3.7.1.tgz")) {
                            println "file download successfully."
                        }else {
                            sh("exit 1")
                        }
                    }
                    catch(Exception e) {
                        println "e"
                        error("fond error during check file download.")
                    }
                }
            }
        }
    }
}
import hudson.model.*;
println env.JOB_NAME
println env.BUILD_NUMBER
pipeline{
    agent any
    stages{
        
        stage("git checkout") {
            steps{
                script {
                checkout([$class: 'GitSCM', 
                branches: [[name: '*/master']], 
                doGenerateSubmoduleConfigurations: false, 
                extensions: [], 
                submoduleCfg: [], 
                userRemoteConfigs: [[credentialsId: 'e3e48ed7-dbce-4642-bb18-28e0c71ab962', 
                url: 'http://192.168.132.132/root/hello-world-greeting.git']]])
                }
            }
        }
        stage("Check file download") {
            steps {
                script {
                    try{
                    out = sh(script: "ls /opt ", returnStdout: true).toString().trim()
                        println out
                        if(out.contains("Python-3.7.1.tgz")) {
                            println "file download successfully."
                        }else {
                            sh("exit 1")
                        }
                    }
                    catch(Exception e) {
                        println "e"
                        error("fond error during check file download.")
                    }
                }
            }
        }
    }
}

7.获取状态

获取执行状态

groovy
// 执行脚本,并获取脚本的exit状态,针对不同状态,做逻辑
def run_status = sh (
    script: """cd ${ROOT_BIN_PATH} &&  \
           ./run.sh  ${env.var1} '${str_param_1}' """,
        returnStatus:true
)
print run_status
// 失败强制退出
if (run_status != 0) {
    print '脚本执行失败,强制退出'
    sh 'exit 1'
}
// 执行脚本,并获取脚本的exit状态,针对不同状态,做逻辑
def run_status = sh (
    script: """cd ${ROOT_BIN_PATH} &&  \
           ./run.sh  ${env.var1} '${str_param_1}' """,
        returnStatus:true
)
print run_status
// 失败强制退出
if (run_status != 0) {
    print '脚本执行失败,强制退出'
    sh 'exit 1'
}

8.在目录下执行

groovy
//例 在 **.git/QloudMartUI 目录下 执行编译命令
dir('QloudMartUI/qloudmart'){
    sh 'npm install'
}
//例 在 **.git/QloudMartUI 目录下 执行编译命令
dir('QloudMartUI/qloudmart'){
    sh 'npm install'
}