Jenkinsfile 是 CI/CD 流水线的“代码化配置文件”。 把构建、测试、发布流程写进仓库后,流水线就可以版本化、可复用、可审计。

1. Jenkinsfile 基本语法

1.1. Pipeline 的定义方式

Pipeline 通常有 3 种维护方式:

  1. 在 Blue Ocean 中可视化创建。
  2. 在 Jenkins 经典 UI 中直接配置脚本。
  3. 在代码仓库中维护 Jenkinsfile(推荐)。

最常见的是第 3 种:和代码一起评审、一起发布。

1.2. 一个最小可运行的声明式 Pipeline

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
pipeline {
agent any

environment {
APP_NAME = "demo-app"
}

parameters {
string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: '部署环境')
booleanParam(name: 'RUN_TEST', defaultValue: true, description: '是否执行测试')
choice(name: 'REGION', choices: ['cn', 'eu', 'us'], description: '部署区域')
}

stages {
stage('Build') {
steps {
sh 'echo "building ${APP_NAME}"'
}
}
stage('Test') {
when {
expression { return params.RUN_TEST }
}
steps {
sh 'echo "running test..."'
}
}
}
}

2. 常用指令说明

2.1. agent

用于指定在哪个执行节点运行任务。

常见写法:

1
agent any

或按标签指定:

1
agent { label 'linux-docker' }

2.2. environment

可全局定义,也可在某个 stage 内定义(仅该阶段生效):

1
2
3
environment {
BUILD_TS = "${new Date().format('yyyyMMddHHmmss')}"
}

2.3. parameters

参数通过 params 对象读取,适合控制分支行为,例如“是否发布”“发布环境”等。

3. 触发器(以 Gerrit 为例)

如果你使用 Gerrit,可以通过 Gerrit Trigger 插件触发 Jenkins 构建。
常见事件包括:Patchset CreatedChange MergedComment Added

你原文中的触发器示意图仍可保留:

在 Jenkinsfile 中也可以声明触发器配置(会覆盖部分 Job UI 配置):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
triggers {
gerrit(
gerritProjects: [[
compareType: 'PLAIN',
pattern: 'repo-name',
branches: [[compareType: 'PLAIN', pattern: 'master']]
]],
serverName: 'gerrit-server',
triggerOnEvents: [
patchsetCreated(excludeDrafts: true),
changeMerged()
]
)
}

4. 共享库与模块化

复杂流水线建议用 Shared Library,避免把所有逻辑都堆进一个 Jenkinsfile。

1
@Library(['ci-lib@main']) _

典型做法:

  1. Jenkinsfile 只负责“流程编排”。
  2. 业务构建、测试、部署逻辑沉淀到共享库方法。
  3. 通过参数或配置 Map 做差异化。

5. 参数化构建实践(Map/List)

对于多镜像构建、多测试用例场景,常用 List/Map 组织配置:

1
2
3
4
5
6
7
8
9
def builds = [
[name: 'backend', agent: 'docker', command: 'ci/build-backend.sh'],
[name: 'frontend', agent: 'docker', command: 'ci/build-frontend.sh']
]

def tests = [
[name: 'unit-test', command: 'ci/test-unit.sh', timeout: 20],
[name: 'e2e-test', command: 'ci/test-e2e.sh', timeout: 30]
]

这样可以通过循环统一执行,减少重复代码。

6. 通知与失败处理

通知建议做成独立配置,按事件触发:

  1. 主分支失败通知。
  2. 主分支成功通知(可选)。
  3. Tag 发布成功/失败通知。

通知通道通常是:

  1. 邮件。
  2. Teams/Yammer/Slack Webhook。

同时建议在 post 块统一做收尾(日志采集、清理资源):

1
2
3
4
5
6
7
8
9
post {
always {
sh 'ci/collect-logs.sh || true'
sh 'ci/cleanup.sh || true'
}
failure {
echo 'Build failed, send notification...'
}
}

7. 环境变量落盘与脚本解耦

实践中常把关键变量输出到 env_vars 文件,再由部署/测试脚本 source 使用。
这样脚本与 Jenkins DSL 之间更解耦,也更便于本地排查。

例如:

1
2
3
4
HELM_NAME="demo-123-main"
NAMESPACE="staging"
BUILD_ID="123"
BRANCH_NAME="main"

8. 一份更完整的 Jenkinsfile 模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@Library(['ci-lib@main']) _

pipeline {
agent { label 'linux-docker' }

parameters {
string(name: 'DEPLOY_ENV', defaultValue: 'staging', description: '部署环境')
booleanParam(name: 'PUBLISH_IMAGE', defaultValue: false, description: '是否发布镜像')
}

stages {
stage('Build') {
steps { sh 'ci/build.sh' }
}
stage('Test') {
steps { sh 'ci/test.sh' }
}
stage('Deploy') {
when { expression { return params.DEPLOY_ENV == 'prod' } }
steps { sh 'ci/deploy.sh' }
}
}

post {
always {
sh 'ci/collect-logs.sh || true'
sh 'ci/cleanup.sh || true'
}
}
}

9. 小结

写 Jenkinsfile 的核心思路是:

  1. 用声明式语法把流程分层(参数、环境、阶段、收尾)。
  2. 用共享库和配置化数据(Map/List)减少重复。
  3. 把触发器、通知、日志清理纳入统一治理。

做到这三点,流水线会更稳定,也更容易被团队协作维护。