Skip to content

Jenkins Pipeline语法进阶

一、Groovy

1.1 Groovy简介

Groovy是一种基于JVM的动态语言,它与Java有很高的兼容性,同时提供了更多的语法糖和动态特性。Jenkins Pipeline使用Groovy作为其脚本语言,这使得Pipeline脚本更加灵活和强大。

1.2 Groovy数据类型

1.2.1 String(字符串)

在Groovy中,字符串可以用单引号、双引号或三引号定义。双引号字符串支持插值(变量替换),而单引号字符串则是纯文本。

基本用法:

groovy
// 单引号字符串(不支持插值)
def singleQuoted = 'Hello World'

// 双引号字符串(支持插值)
def name = "Jenkins"
def doubleQuoted = "Hello ${name}"

// 三引号字符串(支持多行和插值)
def multiLine = """
    This is a multi-line string
    that spans multiple lines
    and can contain ${name}
"""

字符串方法:

groovy
// 字符串长度
def length = "Hello".size()

// 字符串连接
def concat = "Hello" + " World"

// 字符串替换
def replaced = "Hello World".replace("World", "Jenkins")

// 字符串分割
def parts = "a,b,c".split(",")

Jenkins Pipeline示例:

groovy
pipeline {
    agent any
    
    environment {
        PROJECT_NAME = "my-app"
        VERSION = "1.0.0"
    }
    
    stages {
        stage('String操作示例') {
            steps {
                script {
                    // 字符串插值
                    def message = "构建项目: ${PROJECT_NAME} 版本: ${VERSION}"
                    echo message
                    
                    // 字符串操作
                    def upperCase = PROJECT_NAME.toUpperCase()
                    echo "项目名称大写: ${upperCase}"
                    
                    // 多行字符串
                    def buildInfo = """
                    =========构建信息=========
                    项目名称: ${PROJECT_NAME}
                    版本号: ${VERSION}
                    构建编号: ${BUILD_NUMBER}
                    ========================
                    """
                    echo buildInfo
                    
                    // 字符串判断
                    if (PROJECT_NAME.startsWith("my")) {
                        echo "项目名称以'my'开头"
                    }
                    
                    // 字符串替换
                    def newName = PROJECT_NAME.replace("my", "our")
                    echo "新项目名称: ${newName}"
                }
            }
        }
    }
}

1.2.2 List(列表)

Groovy中的List是一个有序集合,可以存储不同类型的元素。

基本用法:

groovy
// 创建列表
def emptyList = []
def numbers = [1, 2, 3, 4]
def mixed = [1, "two", 3.0, true]

// 访问元素
def first = numbers[0]  // 索引从0开始
def last = numbers[-1]  // 负索引表示从末尾开始

// 添加元素
numbers.add(5)
numbers << 6  // 使用左移操作符添加元素

// 合并列表
def combined = numbers + [7, 8, 9]

// 列表切片
def slice = numbers[1..3]  // 获取索引1到3的元素

列表方法:

groovy
// 列表长度
def size = numbers.size()

// 检查元素是否存在
def contains = numbers.contains(3)

// 查找元素索引
def index = numbers.indexOf(4)

// 排序
numbers.sort()

// 过滤
def evenNumbers = numbers.findAll { it % 2 == 0 }

// 映射
def doubled = numbers.collect { it * 2 }

Jenkins Pipeline示例:

groovy
pipeline {
    agent any
    
    stages {
        stage('List操作示例') {
            steps {
                script {
                    // 创建服务器列表
                    def servers = ['prod-server1', 'prod-server2', 'prod-server3']
                    echo "服务器列表: ${servers}"
                    
                    // 添加新服务器
                    servers << 'prod-server4'
                    echo "更新后的服务器列表: ${servers}"
                    
                    // 遍历服务器列表
                    echo "开始部署到以下服务器:"
                    servers.each { server ->
                        echo "- 正在部署到 ${server}..."
                    }
                    
                    // 过滤服务器
                    def backupServers = servers.findAll { it.contains('server2') || it.contains('server3') }
                    echo "备份服务器: ${backupServers}"
                    
                    // 检查服务器是否在列表中
                    if (servers.contains('prod-server1')) {
                        echo "prod-server1在部署列表中"
                    }
                    
                    // 使用join方法
                    def serverString = servers.join(', ')
                    echo "服务器列表字符串: ${serverString}"
                    
                    // 列表排序
                    servers.sort()
                    echo "排序后的服务器列表: ${servers}"
                }
            }
        }
    }
}

1.2.3 Map(映射/字典)

Map是键值对的集合,在Groovy中非常灵活和强大。

基本用法:

groovy
// 创建Map
def emptyMap = [:]
def person = [name: 'John', age: 30, city: 'New York']

// 访问值
def name = person.name  // 使用点符号
def age = person['age']  // 使用方括号

// 添加或更新键值对
person.job = 'Developer'  // 添加新键值对
person['age'] = 31  // 更新现有键值对

// 检查键是否存在
def hasName = person.containsKey('name')

Map方法:

groovy
// 获取所有键
def keys = person.keySet()

// 获取所有值
def values = person.values()

// 获取键值对数量
def size = person.size()

// 遍历Map
person.each { key, value ->
    println "$key: $value"
}

Jenkins Pipeline示例:

groovy
pipeline {
    agent any
    
    stages {
        stage('Map操作示例') {
            steps {
                script {
                    // 创建配置Map
                    def config = [
                        app: 'my-application',
                        version: '1.2.3',
                        deploy: [
                            environment: 'production',
                            replicas: 3,
                            healthCheck: true
                        ]
                    ]
                    
                    // 安全访问嵌套Map(避免空指针)
                    echo "应用名称: ${config.app}"
                    echo "部署环境: ${config.deploy?.environment ?: '未配置'}"  // 安全导航 + 默认值
                    echo "副本数量: ${config.deploy?.replicas ?: 0}"
                    
                    // 添加新配置
                    config.timeout = 300
                    config.deploy.strategy = 'rolling'
                    
                    // 遍历配置(优化输出格式)
                    echo "完整配置:"
                    config.each { key, value ->
                        if (value instanceof Map) {
                            echo "  ${key}:"
                            value.each { k, v ->
                                echo "    ${k}: ${v}"
                            }
                        } else {
                            echo "  ${key}: ${value}"
                        }
                    }
                    
                    // 使用Map作为参数(定义闭包替代方法)
                    def deployOptions = [
                        environment: 'staging',
                        namespace: 'my-app',
                        wait: true,
                        timeout: 600
                    ]
                    
                    // 定义闭包(替代方法)
                    def deploy = { Map options ->  // 关键修改:用闭包代替方法
                        echo "部署到 ${options.environment} 环境"
                        echo "命名空间: ${options.namespace}"
                        echo "等待完成: ${options.wait}"
                        echo "超时时间: ${options.timeout}秒"
                    }
                    
                    // 调用闭包
                    deploy(deployOptions)
                }
            }
        }
    }
}

1.3 Groovy条件语句

1.3.1 if 语句

Groovy的if语句与Java类似,但更加灵活,可以使用更多类型的条件表达式。

基本语法:

groovy
if (condition) {
    // 条件为真时执行
} else if (anotherCondition) {
    // 第一个条件为假,第二个条件为真时执行
} else {
    // 所有条件都为假时执行
}

Groovy中的真值判断:

  • 非零数字为真
  • 非空字符串为真
  • 非空集合为真
  • 非null对象为真
  • true布尔值为真

Jenkins Pipeline示例:

groovy
pipeline {
    agent any
    
    parameters {
        choice(name: 'ENVIRONMENT', choices: ['dev', 'test', 'staging', 'production'], description: '选择部署环境')
        booleanParam(name: 'SKIP_TESTS', defaultValue: false, description: '是否跳过测试')
    }
    
    stages {
        stage('条件构建') {
            steps {
                script {
                    def selectedEnv = params.ENVIRONMENT
                    
                    echo "选择的环境: ${selectedEnv}"
                    
                    if (selectedEnv == 'production') {
                        echo "生产环境部署,需要额外审批"
                        
                        // 使用 input 步骤触发人工审批(仅允许指定用户批准)
                        try {
                            // 输入提示信息,仅允许 admin 用户批准(可修改为其他用户/用户组)
                            def approver = input(
                                message: "⚠️ 确认要部署到生产环境吗?此操作不可逆!",
                                submitter: 'admin',  // 允许审批的用户(支持用户组如 'dev-team')
                                submitterParameter: 'APPROVER'  // 可选:将审批人存入变量
                            )
                            
                            echo "审批通过,审批人:${approver}"
                        } catch (Exception e) {
                            // 用户拒绝或超时未审批时触发
                            error "❌ 生产环境部署被拒绝(审批人:${env.USER}),终止流程"
                        }
                        
                        echo "继续执行生产环境部署..."
                    } else if (selectedEnv == 'staging') {
                        echo "部署到预发布环境,执行完整测试套件"
                    } else if (selectedEnv == 'test') {
                        echo "部署到测试环境,执行基本测试"
                    } else {
                        echo "部署到开发环境,跳过大部分测试"
                    }
                    
                    // 测试控制逻辑
                    if (params.SKIP_TESTS) {
                        echo "根据用户选择,跳过测试阶段"
                    } else {
                        echo "执行测试阶段"
                        // sh 'mvn test'  // 实际测试命令
                    }
                    
                    // 每10次构建执行完整扫描
                    if (env.BUILD_NUMBER.toInteger() % 10 == 0) {
                        echo "🔍 每10次构建触发一次完整扫描(构建号:${env.BUILD_NUMBER})"
                        // sh 'scan-tool full-scan'  // 实际扫描命令
                    }
                    
                    // 部署类型标识
                    def deploymentType = (selectedEnv == 'production') ? '生产部署' : '非生产部署'
                    echo "部署类型: ${deploymentType}"
                }
            }
        }
    }
}

1.3.2 switch 语句

switch语句用于多条件分支判断,Groovy的switch比Java更强大,可以匹配各种类型的值。

基本语法:

groovy
switch (value) {
    case value1:
        // 当value等于value1时执行
        break
    case value2:
        // 当value等于value2时执行
        break
    case valueList:
        // 当value在valueList中时执行
        break
    case pattern:
        // 当value匹配pattern时执行
        break
    default:
        // 当没有匹配的case时执行
}

Jenkins Pipeline示例:

groovy
pipeline {
    agent any
    
    parameters {
        choice(name: 'ACTION', choices: ['build', 'test', 'deploy', 'rollback', 'cleanup'], description: '选择操作类型')
        string(name: 'VERSION', defaultValue: '1.0.0', description: '版本号')
    }
    
    stages {
        stage('执行操作') {
            steps {
                script {
                    def action = params.ACTION
                    def version = params.VERSION
                    
                    echo "执行操作: ${action}, 版本: ${version}"
                    
                    switch (action) {
                        case 'build':
                            echo "构建应用版本 ${version}"
                            // 执行构建命令
                            sh "echo '模拟构建过程'"
                            break
                            
                        case 'test':
                            echo "测试应用版本 ${version}"
                            // 执行测试命令
                            sh "echo '模拟测试过程'"
                            break
                            
                        case 'deploy':
                            echo "部署应用版本 ${version}"
                            // 执行部署命令
                            sh "echo '模拟部署过程'"
                            break
                            
                        case ['rollback', 'cleanup']:
                            // 多值匹配
                            echo "执行维护操作: ${action}"
                            sh "echo '模拟${action}过程'"
                            break
                            
                        default:
                            error "未知操作: ${action}"
                            break
                    }
                    
                    // 使用正则表达式匹配
                    switch (version) {
                        case ~/^\d+\.\d+\.\d+$/:
                            echo "标准版本格式: ${version}"
                            break
                            
                        case ~/^\d+\.\d+\.\d+-SNAPSHOT$/:
                            echo "快照版本: ${version}"
                            break
                            
                        default:
                            echo "非标准版本格式: ${version}"
                            break
                    }
                }
            }
        }
    }
}

1.4 Groovy异常处理

异常处理是处理程序运行时错误的机制。Groovy的异常处理与Java类似,但提供了更多的简化语法。

1.4.1 try-catch-finally

基本语法:

groovy
try {
    // 可能抛出异常的代码
} catch (ExceptionType1 e) {
    // 处理ExceptionType1类型的异常
} catch (ExceptionType2 e) {
    // 处理ExceptionType2类型的异常
} finally {
    // 无论是否发生异常都会执行的代码
}

Jenkins Pipeline示例:

groovy
pipeline {
    agent any
    stages {
        stage("run") {
            steps {
                script {  // 进入脚本模式(支持复杂 Groovy 逻辑)
                    try {
                        println(a)   // 尝试打印未定义的变量 `a`(必然失败)
                    } catch (Exception e) {  // 捕获所有 Exception 类型的异常
                        println(e)  // 打印异常对象(实际输出异常信息)
                        // error "error..."  // (注释中)主动抛出异常终止流程
                    } finally {
                        println("always....")  // 无论是否异常都会执行
                    }
                }
            }
        }
    }
}

1.4.2 error 直接终止流程

Jenkins Pipeline 提供了内置的 error 步骤,专门用于​​终止流程并抛出错误​​

groovy
pipeline {
    agent any
    
    parameters {
        string(name: 'VERSION', defaultValue: '1.0.0', description: '版本号')
    }
    
    stages {
        stage('版本验证') {
            steps {
                script {
                    def version = params.VERSION
                    
                    // 验证版本格式
                    if (!(version =~ /^\d+\.\d+\.\d+$/)) {
                        // 使用 Pipeline 内置的 error 步骤替代 throw
                        error "无效的版本格式: ${version},必须是 x.y.z 格式"
                    }
                    
                    echo "版本格式有效: ${version}"
                }
            }
        }
    }
}

1.5 Groovy函数

Groovy函数(或方法)是可重用代码块,可以接受参数并返回值。在Jenkins Pipeline中,函数可以帮助组织和重用代码。

1.5.1 函数定义和调用

基本语法:

groovy
// 定义函数
def functionName(param1, param2) {
    // 函数体
    return result  // 返回值(可选)
}

// 调用函数
def result = functionName(arg1, arg2)

Jenkins Pipeline示例:

groovy
pipeline {
    agent any
    
    stages {
        stage('函数示例') {
            steps {
                script {
                    // 定义一个简单的函数
                    def greet = { name ->
                        return "Hello, ${name}!"
                    }
                    
                    // 调用函数
                    def message = greet("Jenkins")
                    echo message
                    
                    // 定义带有多个参数的函数
                    def calculateDeployTime = { environment, instanceCount ->
                        def baseTime = 0
                        
                        switch (environment) {
                            case 'dev':
                                baseTime = 5
                                break
                            case 'test':
                                baseTime = 8
                                break
                            case 'production':
                                baseTime = 15
                                break
                            default:
                                baseTime = 10
                        }
                        
                        return baseTime + (instanceCount * 2)
                    }
                    
                    // 调用带有多个参数的函数
                    def devTime = calculateDeployTime('dev', 3)
                    def prodTime = calculateDeployTime('production', 5)
                    
                    echo "开发环境部署预计耗时: ${devTime} 分钟"
                    echo "生产环境部署预计耗时: ${prodTime} 分钟"
                }
            }
        }
    }
}

1.5.2 闭包

闭包是Groovy中的一个重要概念,它是一个可以作为对象传递的代码块。

基本语法:

groovy
def closure = { param ->
    // 闭包体
    return result
}

Jenkins Pipeline示例:

groovy
pipeline {
    agent any
    
    stages {
        stage('闭包示例') {
            steps {
                script {
                    // 定义部署步骤列表
                    def deploySteps = [
                        { -> echo "1. 准备部署环境" },
                        { -> echo "2. 停止旧版本服务" },
                        { -> echo "3. 部署新版本" },
                        { -> echo "4. 启动服务" },
                        { -> echo "5. 健康检查" }
                    ]
                    
                    // 执行部署步骤
                    deploySteps.each { step ->
                        step()  // 调用每个闭包
                    }
                    
                    // 带参数的闭包
                    def verifyService = { serviceName, port ->
                        echo "验证服务 ${serviceName} 在端口 ${port}"
                        // 模拟验证过程
                        return true
                    }
                    
                    // 调用带参数的闭包
                    def serviceOk = verifyService('web-service', 8080)
                    if (serviceOk) {
                        echo "服务验证通过"
                    }
                }
            }
        }
    }
}

二、Jenkins Pipeline开发技巧

2.1 编写测试Pipeline

  1. 使用Replay功能

    • 在Jenkins UI中可以快速测试Pipeline修改
    • 不需要提交到SCM就能测试改动
  2. 使用Pipeline Syntax生成器

    • Jenkins提供了语法生成器
    • 位于Pipeline job的"Pipeline Syntax"链接
  3. 分阶段验证

groovy
pipeline {
    agent any
    stages {
        stage('Validate') {
            steps {
                sh 'echo "First, validate the app..."'
                sh 'make validate'
            }
        }
        stage('Test') {
            steps {
                sh 'echo "Next, test the app..."'
                sh 'make test'
            }
        }
    }
}

2.2 Pipeline问题排错思路

  1. 查看Pipeline Steps

    • 使用Blue Ocean插件可视化查看步骤
    • 检查每个步骤的日志输出
  2. 使用echo调试

groovy
steps {
    script {
        echo "DEBUG: Current environment is ${env.ENVIRONMENT}"
        echo "DEBUG: Build number is ${env.BUILD_NUMBER}"
    }
}
  1. 使用try-catch处理错误
groovy
steps {
    script {
        try {
            sh 'some-command'
        } catch (exc) {
            echo 'Something failed, but I want to keep going'
        }
    }
}

2.3 Jenkinsfile的常见管理方式

  1. 源代码控制

    • 将Jenkinsfile放在代码仓库的根目录
    • 与应用代码一起版本控制
  2. 共享库

groovy
@Library('my-shared-library') _

pipeline {
    agent any
    stages {
        stage('Example') {
            steps {
                mySharedFunction()
            }
        }
    }
}
  1. 模板化
    • 创建标准化的Pipeline模板
    • 在不同项目中重用
groovy
// Template Jenkinsfile
def call(Map config) {
    pipeline {
        agent any
        stages {
            stage('Build') {
                steps {
                    sh "${config.buildCommand}"
                }
            }
        }
    }
}
  1. 配置即代码
    • 使用YAML或JSON配置文件
    • Pipeline读取配置文件动态生成步骤
groovy
def config = readYaml file: 'jenkins-config.yml'
pipeline {
    agent any
    stages {
        stage('Dynamic Stage') {
            steps {
                script {
                    config.steps.each { step ->
                        sh "${step}"
                    }
                }
            }
        }
    }
}

通过以上内容,你应该能够开始使用Jenkins Pipeline,并且了解如何编写、测试和管理Pipeline代码。记住,好的Pipeline应该是可维护的、可重用的,并且能够清晰地表达你的持续交付流程。

最近更新

采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 运维小弟