第五章:Pipeline 语法详解
深入学习 Declarative Pipeline 语法,包括 directives、steps、脚本式步骤。
最后更新: 2024-01-15
页面目录
Pipeline 语法详解
本章详细介绍 Declarative Pipeline 的完整语法和使用技巧。
Pipeline 结构概览
pipeline {
// 指令定义
directive {
// 配置
}
stages {
stage('Stage 1') {
steps {
// 步骤
}
}
}
}
Agent Directive
可用选项
// 任意可用节点
agent any
// 指定标签
agent { label 'linux && docker' }
// 多个标签(必须同时满足)
agent { label 'linux && 64bit' }
// 排除标签
agent {
label '!windows'
}
// Docker 容器
agent {
docker {
image 'maven:3.8-openjdk-11'
args '-v /repo:/root/.m2'
reuseNode true
}
}
// Docker Compose
agent {
dockerfile {
filename 'Dockerfile.test'
additionalBuildArgs '--build-arg version=1.0'
}
}
// Kubernetes
agent {
kubernetes {
label 'jenkins-agent'
defaultContainer 'jnlp'
yamlFile 'pod-template.yaml'
}
}
// 不分配节点(配合 stage agent)
agent none
Stages Directive
stages {
// 至少包含一个 stage
stage('构建') {
// ...
}
stage('测试') {
// ...
}
}
Stage Directive
stage('名称') {
agent { /* ... */ }
environment { /* ... */ }
tools { /* ... */ }
input { /* ... */ }
when { /* ... */ }
steps { /* ... */ }
post { /* ... */ }
}
Steps Directive
基本步骤
steps {
// 打印消息
echo 'Hello World'
// 执行 Shell 命令
sh 'ls -la'
sh(script: 'mvn clean', returnStdout: true)
sh(label: 'Build', script: 'make')
// Windows Batch
bat 'dir'
bat 'call build.bat'
// PowerShell (需要插件)
powershell 'Get-Process'
// 切换目录
dir('/path/to/dir') {
sh 'pwd'
}
// 设置变量
script {
def version = sh(script: 'cat version.txt', returnStdout: true).trim()
env.APP_VERSION = version
}
// 静默执行
sshagent(['ssh-key']) {
sh 'ssh -i key.pem user@host "command"'
}
}
高级步骤
steps {
// 脚本块
script {
if (env.BRANCH_NAME == 'main') {
echo '部署生产环境'
} else {
echo '部署测试环境'
}
}
// 条件执行
script {
if (fileExists('package.json')) {
sh 'npm install'
}
}
// 循环
script {
['dev', 'staging', 'prod'].each { envName ->
echo "部署到 ${envName}"
}
}
// 错误处理
script {
try {
sh 'mvn test'
} catch (Exception e) {
echo "测试失败: ${e.message}"
} finally {
echo '清理'
}
}
// 超时重试
retry(3) {
sh 'curl -X POST https://api.example.com/deploy'
}
// 等待时间
timeout(time: 5, unit: 'MINUTES') {
waitUntil {
script {
def result = sh(script: 'curl -s http://service/health', returnStdout: true)
return result.contains('OK')
}
}
}
}
认证凭据
steps {
// 使用凭据 ID
withCredentials([string(credentialsId: 'api-key', variable: 'API_KEY')]) {
sh 'curl -H "X-API-Key: $API_KEY" https://api.example.com'
}
// 用户名密码
withCredentials([usernamePassword(credentialsId: 'github',
usernameVariable: 'USER',
passwordVariable: 'PASS')]) {
sh 'git push https://$USER:$PASS@github.com/org/repo.git'
}
// SSH 密钥
sshagent(['ssh-key']) {
sh 'ssh -i key.pem user@host'
}
// 文件凭据
withCredentials([file(credentialsId: 'config', variable: 'CONFIG_FILE')]) {
sh 'app --config $CONFIG_FILE'
}
}
Input Directive
stage('Deploy Approval') {
steps {
input {
message '是否部署到生产环境?'
ok '批准'
submitter 'admin,dev-lead'
parameters {
choice(name: 'DEPLOY_ENV',
choices: ['staging', 'production'],
description: '选择部署环境')
string(name: 'DEPLOY_NOTES',
defaultValue: '',
description: '部署备注')
}
}
}
}
// 或者使用脚本式
steps {
script {
def userInput = input(
id: 'deploy',
message: '部署确认',
parameters: [
choice(name: 'ENV', choices: ['dev', 'staging', 'prod'], description: '环境'),
string(name: 'VERSION', description: '版本号')
]
)
env.DEPLOY_ENV = userInput.ENV
env.DEPLOY_VERSION = userInput.VERSION
}
}
Options Directive
pipeline {
options {
// 构建历史保留策略
buildDiscarder(logRotator(
numToKeepStr: '30',
daysToKeepStr: '7',
artifactNumToKeepStr: '10'
))
// 禁用并发构建
disableConcurrentBuilds()
// 启用并发构建(带锁)
disableConcurrentBuildsAbortable()
// 超时设置
timeout(time: 1, unit: 'HOURS')
timeout(time: 30, unit: 'SECONDS')
// 重试次数
retry(3)
// 保持工作区
preserveStashes()
preserveStashes(buildCount: 5)
// 时间戳
timestamps()
// 跳过默认 Checkout
skipDefaultCheckout()
// 跳过StagesAfterUnstable
skipStagesAfterUnstable()
// 覆盖构建序号
overrideIndexTriggers(false)
// Parallels 失败后强制终止
parallelsAlwaysFailFast()
// 锁定资源
lock(resource: 'deploy-server') {
sh './deploy.sh'
}
}
stages {
stage('Deploy') {
// stage 级别的 options
options {
timeout(time: 5, unit: 'MINUTES')
lock(resource: 'database-${params.DB}')
}
steps {
// ...
}
}
}
}
Tools Directive
pipeline {
agent any
tools {
maven 'maven-3.8'
jdk 'jdk-11'
gradle 'gradle-7.0'
go 'go-1.17'
nodejs 'nodejs-16'
}
stages {
stage('Build') {
steps {
sh 'mvn --version'
sh 'java -version'
}
}
}
}
注意:需要在 Jenkins 全局工具配置中预先安装这些工具。
When Directive
条件类型
when {
// 分支条件
branch 'main'
branch 'feature/*'
branch pattern: 'release-\\d+', comparator: 'REGEXP'
// 环境变量
environment name: 'DEPLOY_ENV', value: 'production'
environment name: 'BRANCH_NAME', matches: 'main|develop'
// 表达式
expression { params.BUILD_DEBUG == true }
expression { return fileExists('special-file.txt') }
// 条件组合
allOf {
branch 'main'
environment name: 'DEPLOY', value: 'true'
}
anyOf {
branch 'main'
branch 'release/*'
tag 'v*'
}
not {
branch 'feature/*'
}
}
嵌套条件
when {
anyOf {
allOf {
branch 'main'
environment name: 'ENABLE_PROD', value: 'true'
}
allOf {
branch 'release/*'
expression { params.IS_STABLE == true }
}
}
}
Environment Directive
pipeline {
agent any
environment {
APP_NAME = 'myapp'
APP_VERSION = '1.0.0'
JAVA_OPTS = '-Xmx512m -Xms256m'
// 从凭据获取
API_KEY = credentials('api-key-id')
}
stages {
stage('Build') {
environment {
// stage 级别的环境变量
BUILD_MODE = 'release'
}
steps {
sh 'printenv | grep APP_'
}
}
}
}
Post Directive
执行顺序
post {
// 总是执行(无论成功失败)
always { }
// 仅成功时执行
success { }
// 仅失败时执行
failure { }
// 仅不稳定时执行
unstable { }
// 仅中止时执行
aborted { }
// 最后执行(在 always 之后)
cleanup { }
}
常用 post 步骤
post {
always {
// 归档日志
writeFile file: 'build.log', text: currentBuild.rawBuild.log
archiveArtifacts artifacts: 'build.log', allowEmptyArchive: true
// 清理工作区
cleanWs()
// 删除工作区(详细)
dir('${WORKSPACE}@script') {
deleteDir()
}
dir('${WORKSPACE}@tmp') {
deleteDir()
}
deleteDir()
// 清理 Docker
sh 'docker system prune -f'
}
success {
// 归档产物
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
// 发布 JUnit 报告
junit 'target/surefire-reports/*.xml'
// 发送通知
emailext(
subject: "构建成功: ${env.JOB_NAME}",
body: "构建 #${env.BUILD_NUMBER} 成功",
to: 'team@example.com'
)
}
failure {
// Slack 通知
slackSend(
channel: '#jenkins',
color: 'danger',
message: "构建失败: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
)
// 钉钉通知
dingtalk(
robot: 'build-bot',
type: 'markdown',
title: '构建失败',
text: "**构建失败**\n项目: ${env.JOB_NAME}\n构建号: ${env.BUILD_NUMBER}"
)
}
}
Matrix Directive
pipeline {
agent none
stages {
stage('Matrix Build') {
matrix {
axes {
axis {
name 'PLATFORM'
values 'linux', 'windows', 'macos'
}
axis {
name 'ARCH'
values 'x64', 'arm64'
}
axis {
name 'JDK'
values '11', '17'
}
}
// 排除特定组合
excludes {
exclude {
axis {
name 'PLATFORM'
values 'windows'
}
axis {
name 'ARCH'
values 'arm64'
}
}
}
stages {
stage('Build') {
steps {
echo "Building ${PLATFORM}/${ARCH}/JDK${JDK}"
sh "./build.sh"
}
}
stage('Test') {
steps {
echo "Testing ${PLATFORM}/${ARCH}/JDK${JDK}"
sh "./test.sh"
}
}
}
}
}
}
}
Scripted Pipeline 语法
虽然 Declarative 是推荐的写法,但了解 Scripted 语法有助于理解 Jenkins Pipeline 的本质。
节点和阶段
node('docker') {
stage('Checkout') {
checkout scm
}
stage('Build') {
sh 'mvn clean package'
}
stage('Test') {
sh 'mvn test'
}
}
条件分支
if (env.BRANCH_NAME == 'main') {
stage('Deploy Prod') {
sh './deploy-prod.sh'
}
} else if (env.BRANCH_NAME == 'develop') {
stage('Deploy Dev') {
sh './deploy-dev.sh'
}
} else {
stage('Skip Deploy') {
echo 'Feature branch, skipping deploy'
}
}
循环
def branches = ['dev', 'staging', 'prod']
for (int i = 0; i < branches.size(); i++) {
def branch = branches[i]
stage("Deploy ${branch}") {
sh "./deploy.sh ${branch}"
}
}
函数定义
def buildApp(String version) {
sh "mvn clean package -Dversion=${version}"
}
def notify(String channel, String message) {
slackSend channel: channel, message: message
}
pipeline {
agent any
stages {
stage('Build') {
steps {
script {
buildApp('1.0.0')
notify('#ci', 'Build completed')
}
}
}
}
}
常用 Step 速查表
| Step | 说明 | 示例 |
|---|---|---|
echo |
打印消息 | echo 'Hello' |
sh |
执行 Shell | sh 'ls -la' |
bat |
执行 Windows Batch | bat 'dir' |
powershell |
执行 PowerShell | powershell 'Get-Process' |
checkout |
检出代码 | checkout scm |
git |
Git 操作 | git url: '...' |
dir |
切换目录 | dir('/path') { } |
fileExists |
检查文件 | fileExists('file.txt') |
archiveArtifacts |
归档产物 | archiveArtifacts '*.jar' |
junit |
JUnit 报告 | junit 'test-reports/*.xml' |
stash |
暂存文件 | stash includes: '**/*' |
unstash |
恢复文件 | unstash 'workspace' |
timeout |
超时设置 | timeout(5) { } |
retry |
重试 | retry(3) { } |
waitUntil |
等待条件 | waitUntil { } |
input |
暂停等待输入 | input 'Continue?' |
error |
抛出错误 | error 'Failed' |
catchError |
捕获错误 | catchError { } |
withCredentials |
使用凭据 | withCredentials([...]) { } |
withEnv |
设置环境变量 | withEnv([...]) { } |
script |
脚本块 | script { } |
下一步
现在您已经掌握了 Pipeline 语法。接下来让我们学习实际应用中的 Pipeline 模板和示例。
📖 参考资源: