pipeline基础语法

  官方文档:https://www.jenkins.io/doc/book/pipeline/

  在 Jenkins 中,Pipeline 提供了两种语法来实现流水线即代码(Pipeline as Code):声明式(Declarative)脚本式(Scripted)

  1. 脚本式流水线(也称为“传统”流水线)基于Groovy作为其特定于域的语言。

  2. 而声明式流水线提供了简化且更友好的语法,并带有用于定义它们的特定语句,而无需学习Groovy。声明式流水线语法错误在脚本开始时报告。这是一个很好的功能,因为您不会浪费时间,直到某个步骤未能意识到拼写错误或拼写错误。如前所述,流水线可以以声明式或脚本式编写。而且,声明式方法建立在脚本式方法的基础之上,通过添加”script”步骤,可以很容易地进行扩展。

(1).共同点

  • 两者都是“Pipeline as Code”的有效实现方式,代码都可以持久化并纳入版本控制。
  • 都可以使用 Jenkins 内置或由插件提供的步骤(Steps)。
  • 都支持通过共享库(Shared Libraries)进行扩展。

(2).主要区别

对比维度Declarative Pipeline (声明式)Scripted Pipeline (脚本式)
语法基础 基于结构化的领域特定语言(DSL),语法固定。 基于 Groovy 脚本,遵循命令式编程模型。
可读性与学习成本 结构清晰,可读性强,适合团队协作,上手简单。 灵活性强,但代码可能较复杂,需要掌握 Groovy 语法。
灵活性 适合常规的 CI/CD 流程,复杂逻辑处理有一定限制。 几乎不受结构限制,可编写复杂的逻辑控制、循环和条件嵌套。
错误提示 语法错误在脚本开始时就会报告,提示明确,易于调试。 错误提示通常偏向底层,需要手动处理异常(如 try/catch)。
代码结构 必须包含在 pipeline {} 块内,具有严格的层级结构。 Jenkinsfile 顶部向下串行执行,通常包裹在 node {} 块内。

(3).声明式

  1)核心结构(必要)

    a.pipeline‌

      pipeline是 Jenkins 声明式流水线(Declarative Pipeline)的最顶层根指令。它相当于整个流水线代码的入口和容器,所有的其他指令(如 agent、stages、post 等)都必须包含在这个 pipeline { ... } 代码块内部

    b.agent‌(代理节点)

      agent后面跟随的内容是由 Jenkins 官方预定义的一系列参数(指令) 来决定的。这些参数用于指定整个 Pipeline 或特定阶段(Stage)将在哪种 Jenkins 环境或节点上执行。agent 指令既可以定义在 pipeline 块的顶层(作为全局配置),也可以定义在具体的 stage 块内(作为阶段级配置)。常见的定义内容如下:

      1. 基础执行环境参数

      • any:表示在任何可用的代理(Agent)上执行 Pipeline 或 Stage。
pipeline {
    agent any // 最简单的配置,自动分配空闲节点
    stages {
        stage('Build') {
            steps {
                echo 'Hello World'
            }
        }
    }
}
      • none:表示不为整个 Pipeline 分配全局代理。此时,必须在每个 stage 块中单独定义各自的 agent
pipeline {
    agent none // 顶层不分配全局节点
    stages {
        stage('Build') {
            agent { label 'linux' } // 必须在具体阶段指定节点
            steps {
                echo 'Building...'
            }
        }
    }
}

      2. 节点与标签参数

      • label:使用提供的标签(Label)在 Jenkins 环境中指定的代理上执行。例如:agent { label 'my-defined-label' }
pipeline {
    agent { label 'my-defined-label' } // 指定在具有特定标签的节点上运行
    stages {
        stage('Test') {
            steps {
                echo 'Running tests...'
            }
        }
    }
}
      • node:行为与 label 类似,但允许配置更多额外选项(如自定义工作空间 customWorkspace)。例如:agent { node { label 'labelName' } }
pipeline {
    agent {
        node {
            label 'my-defined-label'
            customWorkspace '/some/other/path' // 指定自定义的工作目录
        }
    }
    stages {
        stage('Deploy') {
            steps {
                echo 'Deploying...'
            }
        }
    }
}

      3. Docker 容器相关参数

      • docker:动态提供一个 Docker 容器节点来执行 Pipeline。可以直接指定外部镜像,也可以传入 args 参数(直接传递给 docker run)。例如:agent { docker 'maven:3-alpine' }
pipeline {
    agent {
        docker {
            image 'maven:3-alpine' // 指定 Docker 镜像
            label 'my-defined-label'
            args '-v /tmp:/tmp' // 传递额外的 docker run 参数
        }
    }
    stages {
        stage('Compile') {
            steps {
                sh 'mvn --version'
            }
        }
    }
}
      • dockerfile:使用源码存储库中包含的 Dockerfile 构建容器来执行 Pipeline。支持指定 filename(文件名)、dir(构建目录)等选项。
pipeline {
    agent {
        dockerfile {
            filename 'Dockerfile.build' // 指定 Dockerfile 文件名
            dir 'build' // 指定 Dockerfile 所在的子目录
            label 'my-defined-label'
            additionalBuildArgs '--build-arg version=1.0.2' // 传递构建参数
        }
    }
    stages {
        stage('Custom Build') {
            steps {
                echo 'Building from custom Dockerfile...'
            }
        }
    }
}

      4. Kubernetes 参数

      • kubernetes:支持使用 Kubernetes 动态创建 Slave Pod 来执行任务,后面通常跟随 YAML 格式的 Pod 配置信息。(安装kubernetes插件
pipeline {
    agent {
        kubernetes {
            label 'jenkins-agent'
            yaml """
                kind: Pod
                metadata:
                  name: jenkins-agent
                spec:
                  containers:
                  - name: maven
                    image: maven:3.8.6-openjdk-11
                    command: ['cat']
                    tty: true
            """
        }
    }
    stages {
        stage('K8s Build') {
            steps {
                container('maven') { // 指定在 Pod 中的特定容器内执行
                    sh 'mvn clean package'
                }
            }
        }
    }
}

    c.stages‌(阶段集合)

      stages是顶层容器,必须包含在 pipeline 块内,只能有一个且不能嵌套,并且至少需要包含一个 stage。它的主要作用是定义和分组整个流水线中按顺序执行的一系列工作阶段(例如:构建、测试、部署等)

    d.stage‌(阶段)

      stage(阶段)是流水线中一个概念上不同的逻辑步骤或任务子集。它被嵌套在 stages 容器内,用于将复杂的 CI/CD 流程拆分为易于管理和可视化的独立模块(例如:“代码拉取”、“项目构建”、“单元测试”、“部署”等)。每个 stage 必须包含一个 steps(步骤)块,这是该阶段具体执行操作(如运行 Shell 命令、拉取代码等)的最小执行单元。

pipeline {
    agent any
    stages {
        stage('项目构建') { // 定义阶段名称,建议简洁明了
            steps {         // 必选:具体执行的操作
                sh 'mvn clean package'
            }
        }
    }
}

      1.核心特性

      • 顺序执行:默认情况下,stages 中的各个 stage 会按照从上到下的顺序依次执行。只有当上一个 stage 成功完成后,才会进入下一个。
      • 可视化支持:在 Jenkins 的 Blue Ocean 或经典 UI 中,stages 会被渲染成直观的流程图或阶段视图,方便查看每个阶段的耗时和状态。
      • 独立配置:每个 stage 都可以拥有自己独立的options、 agent、environment、when(条件判断)、post(后置操作)等指令。

      2.高级用法:并行执行 (Parallel)

        虽然 stages 默认是顺序执行的,但你可以将多个 stage 嵌套在一个父 stage 中,并使用 parallel 关键字让它们同时执行

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                echo 'Building...'
            }
        }
        stage('Test') {
            parallel { // 并行执行块
                stage('Unit Tests') {
                    steps {
                        echo 'Running Unit Tests...'
                    }
                }
                stage('Integration Tests') {
                    steps {
                        echo 'Running Integration Tests...'
                    }
                }
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying...'
            }
        }
    }
}

    e.steps‌(步骤)

      steps 是 stage 内部最核心的执行块。如果说 stage 是流水线上的“加工站”,那么 steps 就是这个站点里具体干活的“工人”。一个 stage 中必须且只能包含一个 steps 块。在这个块内,你可以定义一个或多个具体的执行指令(Step),Jenkins 会按照从上到下的顺序依次执行它们。

pipeline {
    agent any
    stages {
        stage('Build and Test') {
            steps {
                // 这里可以写多个步骤,按顺序执行
                echo 'Step 1: Cleaning workspace...'
                sh 'mvn clean'
                echo 'Step 2: Compiling code...'
                sh 'mvn compile'
            }
        }
    }
}

      1.steps 中常用的内置指令

      • echo:在控制台输出日志信息,常用于调试或打印进度。
      • sh / bat / powershell:执行操作系统命令。Linux/Mac 使用 sh,Windows 使用 batpowershell执行多条命令时使用三个单引号/双引号代替单引号/双引号
      • git:拉取代码(虽然通常在顶层有 checkout scm,但也可以在 steps 中手动指定仓库)。
      • dir:切换工作目录,在指定的目录下执行后续步骤。
      • timeout:设置超时时间,防止步骤卡死。
      • retry:设置重试次数,当步骤失败时自动重试。

      2.高级用法:组合与嵌套

      • 并行执行 (parallel):在同一个 stagesteps 中让多个任务同时运行。

stage('Parallel Tasks') {
    steps {
        parallel(
            frontend: {
                echo 'Building Frontend...'
                sh 'npm run build'
            },
            backend: {
                echo 'Building Backend...'
                sh 'mvn package'
            }
        )
    }
}
      • 脚本块 (script):在声明式 Pipeline 中插入脚本式(Scripted)Pipeline 代码,用于处理复杂的逻辑判断、变量赋值等。

stage('Complex Logic') {
    steps {
        script {
            def env = 'dev'
            if (env == 'dev') {
                echo 'Deploying to Dev environment...'
            } else {
                echo 'Deploying to Prod environment...'
            }
        }
    }
}
      • 包装器指令 (Wrappers):例如 withCredentials(注入凭据)、withEnv(注入环境变量)。

stage('Deploy') {
    steps {
        withEnv(['DEPLOY_TARGET=192.168.1.100']) {
            sh 'echo Deploying to $DEPLOY_TARGET'
        }
    }
}

  2)常用指令

      a.post(后置处理)

        post 块用于定义在 Pipeline 或特定 stage 执行结束时需要执行的操作。post 块可以定义在两个位置:1、Pipeline 级别:在整个流水线的所有 stages 执行完毕后触发;2、Stage 级别:在特定的 stage 执行完毕后触发。

        1.post的常用条件

        • always:无论状态如何(成功、失败、取消等),都会执行。常用于清理工作空间。

        • success:仅在构建成功时执行。常用于发送成功通知或部署。

        • failure:仅在构建失败时执行。常用于发送失败告警。

        • changed:仅在当前构建状态与上一次构建状态不同时执行(例如从失败变为成功)。

        • unstable:当构建因为测试失败或代码规范问题而处于“不稳定”状态时执行。

        • aborted:当构建被手动取消或中止时执行。

        2.在 Pipeline 级别使用

          通常用于全局的通知和清理。

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                echo 'Building...'
            }
        }
    }
    post {
        always {
            echo 'Cleaning up workspace...'
            cleanWs() // 清理工作空间
        }
        success {
            echo 'Build succeeded! Sending email to team...'
            // emailext subject: "Build Success", body: "..."
        }
        failure {
            echo 'Build failed! Sending alert...'
            // emailext subject: "Build Failed", body: "..."
        }
        changed {
            echo 'Build status has changed from the previous run!'
        }
    }
}

        3.在 Stage 级别使用

          通常用于处理某个特定阶段的收尾工作,比如测试阶段的报告归档。

pipeline {
    agent any
    stages {
        stage('Test') {
            steps {
                echo 'Running tests...'
                // 假设这里运行了测试并生成了报告
            }
            post {
                always {
                    // 无论测试是否成功,都归档测试报告
                    junit '**/target/surefire-reports/*.xml' 
                }
                failure {
                    echo 'Tests failed in this stage!'
                }
            }
        }
    }
}

        4.注意事项

        • 执行顺序:如果同时定义了多个条件(如 alwayssuccess),always 通常会最先执行,然后执行 successfailure
        • 状态不受影响:在 post 块中执行的步骤失败,不会改变当前 Pipeline 或 Stage 的最终状态(例如,测试失败了,但在 postalways 中清理文件失败,最终状态依然是失败)。
        • script 结合:如果需要在 post 中执行复杂的逻辑判断,可以像 steps 一样嵌套 script { ... } 块。

      b.environment(环境变量)

        environment 指令用于定义和管理环境变量。通过环境变量,你可以统一管理配置(如项目名称、版本号、路径、账号密码等),避免在脚本中硬编码,从而提升脚本的复用性、灵活性和安全性。environment 指令可以定义在两个位置:1、Pipeline 级别:定义全局环境变量,所有 stage 均可引用;2、Stage 级别:定义局部环境变量,仅对当前阶段生效,且优先级高于全局同名变量。

         1.基础用法与局部覆盖

          可以在 environment 块中以 变量名 = '值' 的格式定义变量,并在后续步骤中通过 ${变量名}env.变量名 进行引用。

pipeline {
    agent any
    // 全局环境变量
    environment {
        APP_NAME = 'demo-service'
        BUILD_VERSION = '1.0.0'
    }
    stages {
        stage('Test') {
            // 局部环境变量,覆盖全局的 BUILD_VERSION
            environment {
                BUILD_VERSION = '1.0.1' 
            }
            steps {
                echo "项目名称:${APP_NAME}"
                echo "当前版本:${BUILD_VERSION}" // 输出 1.0.1
            }
        }
    }
}

        2.安全注入凭据 (Credentials)

          environment 支持一个特殊的辅助方法 credentials(),可以通过标识符安全地访问 Jenkins 中预定义的凭据,避免将敏感信息暴露在代码中。

stage('Deploy') {
    environment {
        // 自动注入用户名和密码,分别生成 MY_CREDS_USR 和 MY_CREDS_PSW
        MY_CREDS = credentials('my-predefined-username-password') 
        
        // 注入 Secret Text
        API_KEY = credentials('my-secret-text-id') 
    }
    steps {
        sh 'echo "Using user: $MY_CREDS_USR"'
    }
}

        3.动态生成环境变量

          环境变量的值可以在运行时通过执行 Shell 命令动态获取。这通常需要结合 script 块和 returnStdout 参数来实现。

pipeline {
    agent any
    environment {
        // 动态获取 Git 当前分支名
        CURRENT_BRANCH = """${sh(
            returnStdout: true,
            script: 'git rev-parse --abbrev-ref HEAD'
        )}""".trim()
    }
    stages {
        stage('Build') {
            steps {
                echo "正在构建分支: ${CURRENT_BRANCH}"
            }
        }
    }
}

        4.使用 Jenkins 内置环境变量

          Jenkins 自身提供了一系列内置的环境变量,无需在 environment 中定义,直接通过 env.变量名 即可引用。常用的内置变量包括:

          • env.BUILD_NUMBER:当前构建的自增编号。
          • env.JOB_NAME:当前流水线/任务的名称。
          • env.BRANCH_NAME:当前构建的分支名称。
          • env.WORKSPACE:当前工作空间的绝对路径。

        5.注意事项

        • 命名规范:环境变量名称不能包含空格或特殊字符,建议使用“大写+下划线”的格式(如 APP_NAME),以避免语法错误。
        • 作用域优先级:如果在 stage 中定义了与全局同名的环境变量,局部变量会覆盖全局变量,但仅在当前 stage 内生效。

      c.options(选项)

        options 指令用于配置整个 Pipeline 或特定 Stage 的行为属性。这些选项可以控制诸如流水线如何保留构建记录、是否允许并发执行、超时时间等全局或局部设置。options 块可以定义在两个位置:1、Pipeline 级别:对整个流水线生效;2、Stage 级别:仅对当前阶段生效(例如限制某个特定阶段的超时时间)。

        1.常用选项

        • timeout:设置 Pipeline 或 Stage 的最大执行时间,超时后自动终止。
        • retry:当 Pipeline 或 Stage 失败时,自动重试指定的次数。
        • disableConcurrentBuilds:禁止并发构建。如果上一次构建还在运行,新的触发将被丢弃或等待(避免多分支同时部署导致冲突)。
        • buildDiscarder:配置构建记录的保留策略(如只保留最近 N 次构建,或保留最近 N 天的构建),防止 Jenkins 磁盘被占满。
        • timestamps:在控制台输出的每一行前面加上时间戳,方便排查耗时问题。
        • skipDefaultCheckout:跳过默认的源码拉取(Checkout)操作。当你需要手动控制代码拉取逻辑时非常有用。

        2.在 Pipeline 级别使用

          通常用于全局的构建保护和日志优化。

pipeline {
    agent any
    options {
        // 整个流水线超时时间为 1 小时
        timeout(time: 1, unit: 'HOURS') 
        
        // 禁止并发构建
        disableConcurrentBuilds() 
        
        // 在控制台输出添加时间戳
        timestamps() 
        
        // 只保留最近 10 次构建记录
        buildDiscarder(logRotator(numToKeepStr: '10'))
    }
    stages {
        stage('Build') {
            steps {
                echo 'Building...'
            }
        }
    }
}

        3.在 Stage 级别使用

          通常用于对某个特定阶段设置严格的限制。

pipeline {
    agent any
    stages {
        stage('Deploy to Prod') {
            options {
                // 部署阶段最多重试 2 次
                retry(2) 
                
                // 部署阶段超时时间为 15 分钟
                timeout(time: 15, unit: 'MINUTES') 
            }
            steps {
                echo 'Deploying to Production...'
            }
        }
    }
}

        4.注意事项

        • 语法格式:大多数 options 都是方法调用的形式,即使没有参数也需要加上括号(例如 timestamps() 而不是 timestamps)。
        • 不要混淆options 用于控制流水线的运行机制(如超时、并发),而 environment 用于传递数据(如变量、密码),post 用于善后处理

      d.parameters(参数)

        parameters 指令用于实现参数化构建。它允许在流水线运行时动态传入参数,从而灵活控制构建行为(例如选择部署环境、指定代码分支、控制功能开关等)。parameters 块必须放在 pipeline 块下,且只能出现一次。定义参数后,在流水线内部通过 params.参数名 的方式来访问传入的值。

        1.基础用法与参数访问

pipeline {
    agent any
    parameters {
        string(name: 'BRANCH', defaultValue: 'main', description: '指定构建分支')
        choice(name: 'ENV', choices: ['dev', 'test', 'prod'], description: '选择部署环境')
    }
    stages {
        stage('Deploy') {
            steps {
                echo "正在将 ${params.BRANCH} 分支部署到 ${params.ENV} 环境..."
            }
        }
    }
}

        2.常用参数类型

        • string:单行文本输入框。常用于指定版本号、Git 分支名、镜像 Tag 等。
        • booleanParam:布尔值复选框(true/false)。常用于功能开关,如控制是否执行测试、是否清理环境等。
        • choice:下拉单选列表。提供预定义的选项,常用于选择部署环境或构建模式。
        • password:密码输入框。输入时内容会被隐藏,专门用于接收 API Token、数据库密码等敏感凭证。
        • text:多行文本输入框。适合提交构建备注、变更日志或配置文件片段。
        • file:文件上传。允许用户在构建时上传本地文件(如证书、测试数据包)。

        3.高级动态参数(需插件支持)

          除了基础参数,企业级实战中还可以利用插件实现更复杂的参数化:
          • gitParameter:需要安装 Git Parameter 插件。它可以动态拉取 Git 仓库中最新的分支列表或标签列表,避免手动维护分支下拉框。
          • Active Choices:需要安装 Active Choices 插件。通过编写 Groovy 脚本,根据实时数据(如 API 接口、配置中心)动态生成参数选项,实现跨参数的联动。

        4.注意事项

        • 首次生效机制:在 Pipeline 脚本中新增或修改 parameters 定义后,必须手动执行一次 Pipeline,Jenkins 才会加载并使其生效。生效后,UI 上才会出现 “Build with Parameters” 选项。

        • 作用域隔离:通过 parameters 定义的参数属于全局参数,通过 params.参数名 访问;这与在 environment 块中定义的环境变量(通过 env.参数名 访问)在作用域和机制上有所不同。

      e.triggers(触发器)

        triggers 指令用于定义流水线(Pipeline)的自动化触发方式。它允许流水线根据特定的时间规则或外部事件自动执行,而无需人工干预。triggers 块必须定义在 pipeline 块下。Jenkins 内置支持三种主要的触发方式:cron(定时执行)、pollSCM(轮询代码仓库)和 upstream(上游任务触发)。

        1.常用触发器说明

        • cron(定时触发):类似于 Linux 的 crontab,定义一个时间规则,时间到了就会自动触发流水线执行。常用于周期性的任务,如每夜构建或定时清理。
        • pollSCM(轮询源码触发):Jenkins 会定期去代码仓库检查是否有新的代码变更。如果发现有变更,则触发流水线构建。这种方式可以确保只在有代码更新时才进行构建,节省资源。
        • upstream(上游任务触发):当指定的上游项目(Job)构建完成且状态符合预期时,自动触发当前流水线。常用于构建依赖链,例如:测试任务在上游的构建任务成功后自动执行。

        2.组合使用多种触发器

pipeline {
    agent any
    triggers {
        // 定时触发:每个工作日(周一到周五)的凌晨2点执行
        cron('0 2 * * 1-5') 
        
        // 轮询触发:每5分钟检查一次代码仓库是否有变更
        pollSCM('H/5 * * * *') 
        
        // 上游触发:当 job1 或 job2 构建成功后触发
        upstream(upstreamProjects: 'job1,job2', threshold: hudson.model.Result.SUCCESS)
    }
    stages {
        stage('Build') {
            steps {
                echo 'Building...'
            }
        }
    }
}

        3.使用 cron 的哈希值(推荐)

          在大型组织中,如果大量任务都在整点(如 0 0 * * *)执行,会导致 Jenkins 服务器瞬间负载过高。Jenkins 支持使用 H 字符来代表哈希值,将任务分散执行:

triggers {
    // 每隔15分钟执行一次,但会在随机的分钟数(如 :07, :22, :37, :52)执行,避免拥堵
    cron('H/15 * * * *')
}

        4.注意事项

        • 首次生效机制:与 parameters 类似,在 Pipeline 脚本中新增或修改 triggers 定义后,必须手动执行一次 Pipeline,Jenkins 才会加载并使其生效。
        • Webhook 的替代:虽然 triggers 提供了内置的触发方式,但在实际的企业级 CI/CD 中,更推荐使用代码平台(如 GitLab、GitHub)提供的 Webhook 机制。Webhook 是事件驱动的(代码一推送立刻通知 Jenkins),比 pollSCM 的轮询机制更实时且性能更好。

      f.tools(工具)

        tools 指令用于自动安装构建所需的工具(如 Maven、JDK、Gradle 等),并将它们的路径自动添加到系统的 PATH 环境变量中。这样你就可以在 steps 中直接调用这些工具的命令,而无需手动配置环境。tools 块可以定义在两个位置:1、Pipeline 级别:对整个流水线的所有阶段生效;2、Stage 级别:仅对当前阶段生效,允许不同阶段使用不同版本的工具。

        1.前置条件

          在使用 tools 之前,你必须在 Jenkins 的后台预先配置好这些工具。

          • 配置路径:进入 Jenkins 管理页面 -> Manage Jenkins (系统管理) -> Global Tool Configuration (全局工具配置)。
          • 在此处配置的工具名称,必须与你在 Pipeline tools 块中引用的名称完全一致。

        2.支持的内置工具

          声明式 Pipeline 原生支持以下三种工具的自动安装:

          • maven
          • jdk
          • gradle

          (注:如果需要 SonarQube 等其他工具,通常需要在 steps 中通过 script 块结合 tool 步骤来获取路径)

        3.在 Pipeline 级别全局配置

pipeline {
    agent any
    tools {
        // 引用在 Jenkins 全局工具配置中定义的 JDK 和 Maven 名称
        jdk 'jdk8'
        maven 'apache-maven-3.0.1'
    }
    stages {
        stage('Build') {
            steps {
                // 无需额外配置,直接使用 mvn 和 java 命令
                sh 'java -version'
                sh 'mvn --version'
                sh 'mvn clean package'
            }
        }
    }
}

        4.在 Stage 级别动态切换版本

pipeline {
    agent any
    stages {
        stage('Build with Maven 3.8') {
            tools {
                maven 'apache-maven-3.8.6' // 仅在此阶段使用 3.8.6 版本
            }
            steps {
                sh 'mvn --version' // 输出 3.8.6
            }
        }
        stage('Build with Maven 3.0') {
            tools {
                maven 'apache-maven-3.0.1' // 仅在此阶段使用 3.0.1 版本
            }
            steps {
                sh 'mvn --version' // 输出 3.0.1
            }
        }
    }
}

        5.结合 script 使用 SonarQube Scanner

          由于 tools 块不直接支持 SonarQube,可以通过 script 步骤获取其安装路径:

stage('Code Analysis') {
    steps {
        script {
            // 获取 SonarQube Scanner 的安装路径
            def scannerHome = tool 'sonarqubescanner' 
        }
        // 使用获取到的路径执行分析
        sh "${scannerHome}/bin/sonar-scanner -Dsonar.projectKey=MyProject"
    }
}

        6.注意事项

        • agent none 冲突:如果你在 Pipeline 顶层使用了 agent none,那么顶层的 tools 指令将被忽略。此时必须在每个具体的 stage 中分别定义 agent 和 tools。
        • 名称匹配:如果 tools 中填写的名称在 Jenkins 全局配置中找不到,流水线会报错或尝试自动下载(取决于全局配置中的“自动安装”设置)。

      g.input(输入)

        input 指令用于在流水线执行过程中暂停执行,并等待人工输入或确认。它通常用于需要人工干预的环节,例如:在生产环境部署前等待审批、选择部署目标环境、输入额外的密码或参数等。

        1.基础用法

          最简单的 input 只需要一个提示消息,流水线会暂停,直到有人点击 "Proceed"(继续)或 "Abort"(中止)。

pipeline {
    agent any
    stages {
        stage('Build') {
            steps {
                echo 'Building...'
            }
        }
        stage('Approve Deploy') {
            steps {
                // 暂停流水线,等待人工确认
                input message: '是否允许部署到生产环境?', ok: '确认部署'
            }
        }
        stage('Deploy') {
            steps {
                echo 'Deploying to Production...'
            }
        }
    }
}

        2.结合参数使用

          input 也可以像 parameters 一样,要求用户在暂停时提供特定的输入值。

stage('Select Environment') {
    steps {
        input message: '请选择部署目标环境', parameters: [
            choice(name: 'TARGET_ENV', choices: ['staging', 'production'], description: '选择环境'),
            string(name: 'DEPLOY_NOTE', defaultValue: '', description: '部署备注')
        ]
    }
}

        3.指定审批人

          可以限制只有特定的用户或用户组才能点击确认,防止误操作。

stage('Security Check') {
    steps {
        input message: '安全团队审批', submitter: 'alice,bob,security-team'
    }
}

        4.高级用法:将输入值作为环境变量

          默认情况下,input 产生的参数不会自动变成环境变量。如果需要将用户输入的值传递给后续步骤,通常需要将其包裹在 script 块中,并使用 timeout 防止无限期挂起。

stage('Get User Input') {
    steps {
        script {
            // 设置超时时间,防止流水线永远挂起
            timeout(time: 1, unit: 'HOURS') {
                // input 的返回值是一个 Map,包含用户输入的所有参数
                def userInput = input(
                    message: '请输入版本号',
                    parameters: [string(name: 'VERSION', defaultValue: '1.0.0')]
                )
                
                // 将返回值设置为环境变量,供后续使用
                env.RELEASE_VERSION = userInput.VERSION
            }
        }
        echo "即将发布的版本是: ${env.RELEASE_VERSION}"
    }
}

        5.注意事项

        • 资源占用:当流水线在 input 处暂停时,它仍然会占用一个执行节点(Agent)。如果暂停时间较长,建议将 input 放在 agent none 的顶层,或者放在一个不占用重型资源的轻量级阶段中,以免阻塞其他构建任务。
        • 超时机制:在生产环境中,强烈建议给 input 加上 timeout,避免因为审批人忘记处理导致流水线无限期挂起并占用资源。
        • 触发条件:input 通常配合 when 条件使用,例如只在 main 分支或 prod 环境下才需要人工审批。

      h.when(条件执行)

        when 指令用于控制某个 stage(阶段)是否执行。它允许你基于各种条件(如分支名称、环境变量、构建参数等)来决定是否跳过或运行特定的阶段。when 必须嵌套在 stage 块内,并且位于 steps 之前。

        1.常用条件说明

        • branch:当构建的分支名称匹配时执行(支持通配符)。
        • environment:当指定的环境变量等于某个值时执行。
        • expression:当 Groovy 表达式返回 true 时执行(最灵活,可处理复杂逻辑)。
        • equals:当两个值相等时执行(通常用于比较参数)。
        • not:取反,当内部条件不满足时执行。
        • allOf:所有内部条件都满足时才执行(相当于 AND)。
        • anyOf:任意一个内部条件满足时就执行(相当于 OR)。

        2.基于分支和环境变量

pipeline {
    agent any
    stages {
        stage('Deploy to Prod') {
            when {
                branch 'main' // 仅在 main 分支执行
            }
            steps {
                echo 'Deploying to Production...'
            }
        }
        
        stage('Run Tests') {
            when {
                environment name: 'RUN_TESTS', value: 'true' // 仅当环境变量为 true 时执行
            }
            steps {
                echo 'Running tests...'
            }
        }
    }
}

        3.组合条件 (allOf / anyOf / not)

stage('Complex Deploy') {
    when {
        allOf {
            branch 'main'
            environment name: 'DEPLOY_ENABLED', value: 'true'
            not {
                environment name: 'SKIP_DEPLOY', value: 'true'
            }
        }
    }
    steps {
        echo 'All conditions met, deploying...'
    }
}

        4.使用 Groovy 表达式处理复杂逻辑

stage('Custom Logic') {
    when {
        expression { 
            // 可以在这里写任意 Groovy 代码
            return params.ENV == 'prod' && env.BUILD_NUMBER.toInteger() > 100 
        }
    }
    steps {
        echo 'Custom logic passed!'
    }
}

        5.注意事项

        • 执行时机:默认情况下,when 条件在 stage 开始执行前评估。如果条件不满足,该阶段会被直接跳过。
        • beforeAgent 选项:默认情况下,Jenkins 会先分配 Agent(执行节点),然后再评估 when 条件。如果你希望在评估条件之前不占用 Agent 资源,可以设置 beforeAgent true:
stage('Lightweight Check') {
    when {
        branch 'main'
        beforeAgent true // 先判断分支,再分配节点
    }
    steps { ... }
}
        • beforeInput 选项:如果 stage 同时包含 input 和 when,默认会先暂停等待人工输入,然后再评估 when。设置 beforeInput true 可以让条件判断优先于人工审批。
        • 跳过状态:当 when 条件不满足时,该阶段在 Jenkins UI 中会显示为 "Skipped"(跳过),不会影响整个 Pipeline 的最终成功状态。

      i.parallel(并行执行)

        parallel 指令用于同时执行多个阶段(Stages)。通过将互不依赖的任务并行化,可以大幅缩短流水线的总执行时间,并更高效地利用构建资源(如多核 CPU 或多节点集群)。

        1.声明式语法

          从声明式流水线 1.2 版本开始,推荐使用这种语法。它将多个 stage 嵌套在一个父 stageparallel 块中,结构清晰,且支持为每个并行阶段单独指定 agent(执行节点)。

pipeline {
    agent none // 父级不分配节点,由子阶段自行分配
    stages {
        stage('Run Tests') {
            parallel {
                stage('Test On Linux') {
                    agent { label 'linux' }
                    steps {
                        echo 'Running tests on Linux...'
                    }
                }
                stage('Test On Windows') {
                    agent { label 'windows' }
                    steps {
                        echo 'Running tests on Windows...'
                    }
                }
            }
        }
    }
}

        2.脚本式语法(Scripted Syntax)

          steps 块内使用 parallel 方法,传入一个 Map(键值对),键为任务名称,值为包含执行逻辑的闭包。这种方式适合需要动态生成并行任务的复杂场景。

pipeline {
    agent any
    stages {
        stage('Parallel Build') {
            steps {
                script {
                    parallel(
                        frontend: {
                            echo 'Building Frontend...'
                        },
                        backend: {
                            echo 'Building Backend...'
                        }
                    )
                }
            }
        }
    }
}

        3.动态并行构建

          当并行任务的数量或名称在运行时才能确定时(例如根据代码目录动态生成构建任务),可以结合 Groovy 的 for 循环动态创建 Map,然后传给 parallel 执行:

stage('Dynamic Parallel Tasks') {
    steps {
        script {
            def tasks = [:]
            def modules = ['module-A', 'module-B', 'module-C']
            
            for (module in modules) {
                tasks["Build ${module}"] = {
                    echo "Building ${module}..."
                }
            }
            parallel tasks
        }
    }
}

        4.失败处理策略 (failFast)

          默认情况下,如果某个并行阶段失败,其他并行阶段仍会继续执行直到结束。如果你希望“一损俱损”(即任何一个并行任务失败时,立即终止其他正在运行的并行任务),可以在 parallel 块同级添加 failFast true:

stage('Parallel Build') {
    failFast true // 任意一个失败,其余全部中止
    parallel {
        stage('Build 1') { /* ... */ }
        stage('Build 2') { /* ... */ }
    }
}

        5.注意事项

        • 保持阶段独立:并行执行的阶段之间绝对不能有依赖关系。不要让它们读写同一个文件、修改同一个环境变量或争抢同一个端口,否则会导致不可预知的冲突和失败。
        • 语法互斥:在声明式语法中,一个 stage 只能包含 parallel 指令 steps 指令,不能同时包含两者。
        • 嵌套限制parallel 块内部的 stage 不能再嵌套另一个 parallel 指令(即不支持多层并行嵌套),只能包含 steps
        • 资源消耗:并行构建会同时占用多个执行节点或消耗大量 CPU/内存资源。在使用分布式构建时,建议结合 agent { label '...' } 将任务合理分配到不同的机器上。

      j.script(脚本)

        script 步骤是声明式流水线(Declarative Pipeline)中的一个“逃生舱”。由于声明式流水线有着严格的语法结构,当遇到声明式语法无法直接处理的复杂逻辑时,你可以通过 script { ... } 块在 steps 中直接嵌入脚本化流水线(Scripted Pipeline)的 Groovy 代码。

        1.核心作用

          script 块允许你在声明式流水线中编写几乎任意的 Groovy 代码。它通常用于处理以下场景:

          • 复杂的条件判断(如 if/else 逻辑)。
          • 动态变量赋值与处理。
          • 循环操作(如 for 循环)。
          • 异常处理(如 try/catch/finally)。

        2.复杂的条件判断与变量处理

pipeline {
    agent any
    stages {
        stage('Complex Logic') {
            steps {
                script {
                    // 1. 定义和处理变量
                    def version = readFile('VERSION').trim()
                    env.APP_VERSION = version
                    
                    // 2. 使用 if/else 进行复杂判断
                    if (env.BRANCH_NAME == 'master') {
                        echo "主分支构建,执行完整流程"
                        sh 'mvn clean package -P production'
                    } else if (env.BRANCH_NAME.startsWith('feature/')) {
                        echo "功能分支构建,快速验证"
                        sh 'mvn clean compile test'
                    } else {
                        echo "其他分支,跳过构建"
                        return
                    }
                }
            }
        }
    }
}

        3.循环操作

stage('Loop Example') {
    steps {
        script {
            def browsers = ['chrome', 'firefox', 'safari']
            for (int i = 0; i < browsers.size(); ++i) {
                echo "正在测试浏览器: ${browsers[i]}"
                // sh "run-tests.sh --browser ${browsers[i]}"
            }
        }
    }
}

        4.异常处理 (try/catch/finally)

stage('Deploy with Cleanup') {
    steps {
        script {
            try {
                sh 'deploy-to-prod.sh'
            } catch (Exception e) {
                currentBuild.result = 'FAILURE'
                echo "部署失败,错误信息: ${e.message}"
                // 可以在这里发送告警
            } finally {
                // 无论成功或失败,都会执行清理操作
                sh 'docker system prune -f'
            }
        }
    }
}

        5.注意事项

        • 位置限制:script 块必须嵌套在 stage 的 steps 内部。
        • 适度使用:script 块虽然灵活,但过度使用会让声明式流水线失去其结构清晰、易于维护的优势。如果逻辑过于复杂,建议将 Groovy 代码提取到 Jenkins 共享库 (Shared Libraries) 中,然后在 script 块中调用。
        • 语法检查:声明式流水线在加载时会进行语法检查,但 script 块内部的 Groovy 代码只有在运行时才会进行错误检查。因此,在 script 块中编写代码时需要更加小心。

(4).脚本式

   脚本式流水线(Scripted Pipeline) 是最早的、基于 Groovy 的流水线编写方式。与我们之前讨论的声明式流水线(Declarative Pipeline)不同,它没有固定的结构限制,完全由 Groovy 代码控制,因此具有极高的灵活性,但也意味着更高的维护成本和更陡峭的学习曲线。

  1)核心特征

    • 完全由 Groovy 驱动:整个流水线本质上就是一个 Groovy 脚本。
    • 无固定结构:不需要 pipeline {} 块,直接编写 node {} 块来分配执行节点。
    • 原生支持复杂逻辑:可以直接使用 try/catch、if/else、for 循环等所有 Groovy 语法,无需像声明式那样依赖 script {} 逃生舱。
    • 动态能力:可以在运行时动态生成 Stage、动态决定执行路径。

  2)基础语法结构

    脚本式流水线的核心是 node 块。node 用于分配一个执行节点(Agent),并在该节点上执行内部的步骤。

// 1. 分配一个默认节点
node {
    // 2. 检出代码
    stage('Checkout') {
        checkout scm
    }
    
    // 3. 构建
    stage('Build') {
        sh 'mvn clean package'
    }
}

  3)原生异常处理与复杂控制流

    在脚本式中,你可以直接使用原生的 Groovy 异常处理,而不需要借助 post 块。

node {
    stage('Deploy') {
        try {
            sh 'deploy.sh'
        } catch (e) {
            echo "部署失败: ${e.message}"
            currentBuild.result = 'FAILURE'
            // 发送告警邮件
        } finally {
            // 无论成功失败,清理环境
            sh 'rm -rf /tmp/deploy_cache'
        }
    }
}

  4)动态生成 Stage

    这是脚本式流水线最强大的特性之一。当你的构建任务数量在运行时才能确定时(例如根据代码目录动态构建微服务),脚本式可以轻松实现。

node {
    stage('Dynamic Build') {
        def services = ['auth-service', 'user-service', 'order-service']
        
        for (service in services) {
            // 动态创建 Stage
            stage("Build ${service}") {
                echo "正在构建服务: ${service}"
                sh "cd ${service} && mvn clean package"
            }
        }
    }
}

  5)并行执行 (Parallel)

    脚本式使用 parallel 方法(传入一个 Map)来实现并发。

node {
    stage('Parallel Tests') {
        parallel(
            'Unit Tests': {
                sh 'mvn test'
            },
            'Code Analysis': {
                sh 'sonar-scanner'
            }
        )
    }
}

(5).总结

  声明式(Declarative):官方推荐 90% 的场景使用声明式语法。它更稳定、更容易维护,且与 Jenkins 的可视化编辑器(如 Blue Ocean)兼容性更好,是未来的发展趋势。

  脚本式(Scripted):当你的构建逻辑极其复杂(例如需要大量的动态循环、复杂的条件嵌套、自定义类或闭包),且声明式语法无法满足需求时,再考虑使用脚本式。

 

参考文档:https://cloud.tencent.com/developer/article/2287071

     https://cloud.tencent.com/developer/article/2588953?policyId=1003

posted @ 2026-06-12 12:56  苦逼运维  阅读(15)  评论(0)    收藏  举报