1.1 Jenkins 流水线介绍
Jenkins 流水线(Pipeline)是一套插件,它支持在 Jenkins 中实现和集成持续交付流水线。流水线提供了一组可拓展的工具,用于自动化构建、测试和部署过程的功能。具有以下特点:
- 代码化:流水线可以用 DSL(领域特定语言)编写,通常以文本文件的形式存储在代码库中,比如
Jenkinsfile。这使得流水线易于版本控制和共享。 - 可视化:Jenkins 提供了可视化界面,可以清晰地查看每个阶段的执行状态和历史记录,帮助开发者跟踪构建过程。
- 多阶段支持:可以将整个构建过程分为多个阶段(Stages),例如编译、测试、部署等,每个阶段可以包含多个步骤(Steps)。
- 并行执行:支持在不同的节点上并行执行任务,提高构建效率。
- 灵活性:有多个插件支持,以便与各种工具和服务集成,如 Docker、Kubernetes、Git 等。
Jenkins 流水线的定义被写在一个文本文件中(一般为 Jenkinsfile ),该文件定制了整个构建软件的过程,比如构建、测试和部署。
1.2 流水线种类
Jenkins 流水线主要分为声明式和脚本式两种。
1.2.1 声明式流水线
声明式流水线通过一种较为简洁和结构化的语法来定义构建流程。在声明式流水线语法中,流水线过程定义在 pipeline{} 中,pipeline 块定义了整个流水线中完成的所有工作。
1. 优势
- 可读性高:结构清晰,易于理解,特别适合非技术人员或新手。
- 简化配置:内置的功能(如并行执行、后期处理)减少了样板代码的需要。
- 最佳实践:鼓励使用标准化的流程,便于团队协作。
2. 案例
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building...'
// 构建步骤
}
}
stage('Test') {
steps {
echo 'Testing...'
// 测试步骤
}
}
stage('Deploy') {
steps {
echo 'Deploying...'
// 部署步骤
}
}
}
post {
success {
echo 'Pipeline succeeded!'
}
failure {
echo 'Pipeline failed.'
}
}
}
参数说明:
-
pipeline:声明式流水线的一种特定语法,定义了包含执行整个流水线的所有内容和指令,也是声明式流水线的开始;
-
agent :指定流水线中的步骤在哪个 agent 上执行,该参数同样可以在 stage 中配置;
-
Stages: 包含各个阶段的集合;
-
Stage:定义一个阶段的执行过程,比如上文的 Build、Test、Deploy;
-
Steps:执行某阶段具体的步骤。
1.2.2 脚本式流水线
脚本式流水线使用 Groovy 语言的完整特性,可以实现复杂的控制流和逻辑。其结构更加灵活,可以任意组合。因为使用了 Groovy 脚本,可能对不熟悉编程的用户更加复杂,需要更高的学习成本。
1. 优势
- 灵活性高:可以使用任何 Groovy 语法,支持复杂的逻辑和条件判断。
- 深度控制:允许开发者精细控制每个步骤的执行过程。
- 可以重用代码:可以通过函数和变量来实现代码的重用。
2. 案例
node {
stage('Build') {
echo 'Building...'
// 构建步骤
}
stage('Test') {
try {
echo 'Testing...'
// 测试步骤
} catch (Exception e) {
echo "Test failed: ${e.message}"
}
}
stage('Deploy') {
echo 'Deploying...'
// 部署步骤
}
}
1.3 Jenkinsfile 语法
声明式是 Jenkins 新一代流水线编写方式,而且声明式流水线支持类似 Blue Ocean 这类的图形化工具进行在线编辑。本文主要讲述声明式流水线的使用。
声明式流水线中的 sections 不是一个关键字或指令,而是一个或多个 agent、stages、post、directives 和 steps 的代码区域块。
1.3.1 agent
1. agent 的配置参数
在 Jenkins Pipeline 中,agent 块用于定义构建的执行环境。以下是常见的 agent 配置参数:
- **any:**使用任何可用的代理节点。
agent any
- **none:**不使用默认代理。这通常用于在多个阶段中指定不同的代理,也意味着每个阶段都需要单独指定 agent。
agent none
- **具体节点:**指定特定的节点名称来执行构建。
agent { label 'my-node' }
- **Docker:**使用 Docker 容器作为构建环境。可以指定 Docker 镜像和其他选项。
agent {
docker 'maven:3-alpine'
}
- **Docker with args:**可以传递额外参数,例如设置环境变量或挂载卷。
agent {
docker {
image 'maven:3-alpine'
args '-v /host/path:/container/path'
}
}
- **自定义 Docker 镜像 (使用 Dockerfile):**通过使用自定义 Dockerfile 定义镜像。
agent {
dockerfile {
filename 'Dockerfile'
dir 'docker'
}
}
- **Kubernetes:**在 Kubernetes 集群中指定容器运行环境,也就是常说的动态 Slave。
agent {
kubernetes {
label 'mypod'
yaml """
apiVersion: v1
kind: Pod
metadata:
labels:
some-label: some-value
spec:
containers:
- name: jnlp
image: jenkins/jnlp-slave:alpine
- name: maven
image: maven:3-alpine
"""
}
}
2. agent 的配置示例
以下是一个包含多种 agent 配置的完整示例:
pipeline {
agent none
stages {
stage('Build') {
agent { docker 'maven:3-alpine' }
steps {
sh 'mvn clean install'
}
}
stage('Test') {
agent { label 'linux-node' }
steps {
sh './run-tests.sh'
}
}
}
post {
always {
echo 'Cleaning up...'
}
}
}
这些配置参数使您能够灵活地选择最适合您构建和测试需求的执行环境。根据项目的要求,您可以轻松设置不同的代理。
1.3.2 stages 和 stage
在 Jenkins Pipeline 中,stages 块用于定义一系列构建阶段。每个阶段可以包含多个步骤,允许您组织和控制构建过程。
1. stages 的基本结构
stages 块通常包含一个或多个 stage 块,每个 stage 可以包含 steps、agent(可选项) 和其他选项。
pipeline {
agent any
stages {
stage('Stage 1') {
steps {
echo 'Executing Stage 1'
}
}
stage('Stage 2') {
steps {
echo 'Executing Stage 2'
}
}
}
}
2. stage 的配置参数
- **name:**指定阶段的名称,可以直接在
stage块中定义,也可以使用name属性。
stage('Build') {
steps {
echo 'Building...'
}
}
- **agent:**可以在单独的
stage中指定代理环境。
stage('Test') {
agent { label 'test-node' }
steps {
echo 'Running tests...'
}
}
- **when:**条件执行该阶段,只有在满足特定条件时才会执行。
stage('Deploy') {
when {
branch 'main'
}
steps {
echo 'Deploying to production...'
}
}
- parallel: 定义并行执行的子阶段。
stages {
stage('Build and Test') {
parallel {
stage('Build') {
steps {
echo 'Building...'
}
}
stage('Test') {
steps {
echo 'Testing...'
}
}
}
}
}
3. stages 的配置示例
以下是一个完整的示例,展示了如何使用 stages 和其参数:
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building the application...'
// 其他构建步骤
}
}
stage('Test') {
steps {
echo 'Running tests...'
// 测试步骤
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
echo 'Deploying to production...'
// 部署步骤
}
}
stage('Cleanup') {
steps {
echo 'Cleaning up...'
// 清理步骤
}
}
}
post {
always {
echo 'This will always run'
}
}
}
stages 是 Jenkins Pipeline 中的重要组成部分,允许您将构建过程分解为多个阶段,以便更好地组织和管理。每个阶段可以根据需要设置不同的步骤、条件和代理,使得 CI/CD 流程更加灵活高效。
1.3.3 post
1. post 的配置参数
在 Jenkinsfile 中,post 块用于定义构建完成后要执行的操作。它可以用于处理构建后的清理、通知等任务。post 块支持以下参数:
-
always: 无论构建结果如何(成功、失败、未稳定或中止),都会执行的步骤。
post { always { // 执行的步骤 } } -
success: 仅在构建成功时执行的步骤。
post { success { // 执行的步骤 } } -
failure: 仅在构建失败时执行的步骤。
post { failure { // 执行的步骤 } } -
unstable: 仅在构建不稳定时执行的步骤(如测试失败,但编译成功)。
post { unstable { // 执行的步骤 } } -
aborted: 仅在构建被中止时执行的步骤。
post { aborted { // 执行的步骤 } } -
changed: 当构建结果与上一次构建不同(例如,从失败变为成功)时执行的步骤。
post { changed { // 执行的步骤 } }
2. post 的配置示例
pipeline {
agent any
stages {
stage('Build') {
steps {
// 构建步骤
}
}
}
post {
always {
echo 'This will always run'
}
success {
echo 'This will run only if the build was successful'
}
failure {
echo 'This will run only if the build failed'
}
unstable {
echo 'This will run only if the build was unstable'
}
aborted {
echo 'This will run only if the build was aborted'
}
changed {
echo 'This will run only if the build result has changed'
}
}
}
使用这些参数可以帮助您管理构建后的不同情况,确保适当的响应和处理措施。
1.3.4 directives
在 Jenkins Pipeline 中,directives 不是一个关键字或指令,而是一些用于控制和配置整个 pipeline 行为的设置。它们通常出现在 pipeline 块的顶部,并可以影响整个构建流程。
1. environment:
用于定义环境变量,这些变量在整个 pipeline 中可用,也可以设置在某个stage局部进行使用。
environment {
VAR_NAME = 'value'
}
2. options:
允许您设置一些额外的选项来控制 pipeline 的行为。
常见的选项包括:
- timeout: 设置超时限制。
options {
timeout(time: 30, unit: 'MINUTES')
}
- retry: 在失败时重试指定次数。
options {
retry(3)
}
- timestamps: 在日志输出中添加时间戳。
options {
timestamps()
}
3. parameters
块用于定义构建参数,使得在启动构建时可以根据需要传递不同的值。这样,用户可以在执行 pipeline 时指定特定的输入,例如环境、版本号等。用法如下
parameters {
string(name: 'ENV', defaultValue: 'production', description: 'Deployment environment')
booleanParam(name: 'RUN_TESTS', defaultValue: true, description: 'Should tests be run?')
choice(name: 'VERSION', choices: ['1.0', '2.0', 'latest'], description: 'Select version to deploy')
}
在 parameters 块中,可以使用多种类型的参数,包括:
-
String Parameter: 输入字符串。
string(name: 'PARAM_NAME', defaultValue: 'default', description: 'Description of the parameter') -
Boolean Parameter: 布尔值(勾选框)。
booleanParam(name: 'PARAM_NAME', defaultValue: true, description: 'Description of the parameter') -
Choice Parameter: 下拉选择。
choice(name: 'PARAM_NAME', choices: ['option1', 'option2', 'option3'], description: 'Description of the parameter') -
File Parameter: 上传文件。
file(name: 'FILE_PARAM', description: 'Upload a file')
4. triggers:
定义触发构建的条件,例如定时构建。
triggers {
cron('H 2 * * *') // 每天凌晨 2 点触发
}
cron 字段和 Linux 的 crontab类似,5个字段分别代表分、时、日、月、周,不同的流水线中有一个 "H" 的写法。注意 H 不是 Hour,而是 Hash 的缩写。
5. 示例
以下是一个包含多个 directives 的完整示例:
pipeline {
agent any
environment {
MY_VAR = 'Hello, World!'
}
options {
timeout(time: 30, unit: 'MINUTES')
retry(2)
timestamps()
}
triggers {
cron('H 2 * * *') // 定时构建
}
parameters {
string(name: 'ENV', defaultValue: 'production', description: 'Deployment environment')
booleanParam(name: 'RUN_TESTS', defaultValue: true, description: 'Should tests be run?')
choice(name: 'VERSION', choices: ['1.0', '2.0', 'latest'], description: 'Select version to deploy')
}
stages {
stage('Build') {
steps {
echo "Building with ${MY_VAR} for environment: ${params.ENV}"
// 其他构建步骤
}
}
stage('Test') {
when {
expression { params.RUN_TESTS }
}
steps {
echo 'Running tests...'
// 测试步骤
}
}
stage('Deploy') {
steps {
echo "Deploying version: ${params.VERSION} to ${params.ENV}"
// 部署步骤
}
}
}
post {
always {
echo 'Cleaning up...'
}
}
}
这些 directives 使您能够灵活地配置 Jenkins Pipeline 的各个方面,从执行环境到构建后的处理步骤。通过合理使用这些参数,可以提高构建过程的效率和可维护性。
1.3.5 Jenkinsfile 进阶
1. 环境变量
(1)默认环境变量
Jenkins 提供了一些内置的环境变量,你可以在构建中直接使用它们。常见的默认变量包括:
BUILD_ID: 当前构建的唯一 ID。BUILD_NUMBER: 当前构建的编号(从 1 开始)。JOB_NAME: 当前作业的名称。WORKSPACE: 当前构建的工作目录路径。GIT_COMMIT: 当前构建的 Git 提交 SHA。BRANCH_NAME: 当前构建的分支名称(在 Pipeline 中可用)。
在 Pipeline 中,可以通过 ${env.VARIABLE_NAME} 访问环境变量。例如:
pipeline {
agent any
stages {
stage('Build') {
steps {
echo "Job Name: ${env.JOB_NAME}"
echo "Build Number: ${env.BUILD_NUMBER}"
echo "Workspace: ${env.WORKSPACE}"
}
}
}
}
(2)自定义环境变量
除了默认环境变量,也可以在 Jenkins 作业或 Pipeline 脚本中通过 environment 定义自定义环境变量。
pipeline {
agent any
environment {
MY_VAR = 'my_value'
ANOTHER_VAR = 'another_value'
}
stages {
stage('Build') {
steps {
echo "My variable: ${MY_VAR}"
echo "Another variable: ${ANOTHER_VAR}"
}
}
}
}
(3)动态变量
动态变量根据某个指令的结果进行动态赋值,变量的值根据指令的执行结果而不同,例如
使用 returnStdout 来捕获和处理命令的输出。
使用 returnStatus 来判断命令的执行结果,以便于进行错误处理或逻辑判断。
pipeline {
agent any
stages {
stage('Capture Output') {
steps {
script {
// 执行命令并捕获输出
def output = sh(script: 'echo "Hello, Jenkins!"', returnStdout: true).trim()
echo "Captured Output: ${output}"
// 执行命令并获取状态码
def status = sh(script: 'ls /nonexistent_directory', returnStatus: true)
if (status == 0) {
echo "Command succeeded"
} else {
echo "Command failed with status: ${status}"
}
}
}
}
}
}
-
上述命令返回的是
Hello, Jenkins!,output 的值就是Hello, Jenkins!; -
在这个例子中,因为指定的目录不存在,
ls命令会返回 1,此时 status 的值就是1。
2. 凭证管理
Jenkins 的凭证管理功能允许用户安全地存储和管理敏感信息,例如密码、SSH 密钥、令牌等。这些凭证可以在构建过程中使用,而无需将它们以明文的形式使用,从而提高了安全性。凭证类型有几下几种:
- 用户名和密码:用于存储常规的用户名和密码对。
- SSH 密钥:用于 SSH 连接,通常用于 Git 仓库的访问。
- 秘密文本:用于存储简单的字符串,如 API 密钥或令牌。
- 文件:用于存储文件内容,例如配置文件或证书。
创建好凭证后,在 Jenkins Pipeline 中,environment 和 withCredentials 都可以用来处理凭证,但它们的使用方式和场景有所不同。以下是这两者之间的主要区别:
(1)environment
environment 更适合在整个 Pipeline 或阶段内共享凭证,但要小心可能的泄露。
示例:
pipeline {
agent any
environment {
MY_CREDENTIALS = credentials('my-ssh-key')
}
stages {
stage('Example') {
steps {
sh 'echo $MY_CREDENTIALS' // 可能会暴露密钥
}
}
}
}
(2)withCredentials
withCredentials 提供了一个更安全的方式来临时使用凭证,确保凭证在使用后不会被泄露。
示例:
pipeline {
agent any
stages {
stage('Example') {
steps {
withCredentials([sshUserPrivateKey(credentialsId: 'my-ssh-key', keyVariable: 'SSH_KEY', usernameVariable: 'SSH_USER')]) {
sh '''
echo "$SSH_KEY" > /tmp/id_rsa
chmod 600 /tmp/id_rsa
GIT_SSH_COMMAND="ssh -i /tmp/id_rsa" git clone git@github.com:yourusername/yourrepo.git
'''
}
}
}
}
}
3. 参数处理
例如,在 Jenkinsfile 中指令的 parameters 会在 Jenkins Web UI 自动生成对应的参数列表。
示例:
在 Jenkins 页面进行点击选择拉取不同代码。
pipeline {
agent any
parameters {
choice(name: 'BRANCH', choices: ['main', 'develop', 'feature-1', 'feature-2'], description: '请选择要拉取的分支')
}
stages {
stage('Checkout') {
steps {
script {
// 使用指定的分支进行拉取
sh "git checkout ${params.BRANCH}"
// 或者直接克隆指定的分支
// sh "git clone -b ${params.BRANCH} git@github.com:yourusername/yourrepo.git"
}
}
}
stage('Build') {
steps {
echo "Building branch ${params.BRANCH}..."
// 在这里添加你的构建步骤
}
}
}
}
4. 处理失败
在声明式流水线中,可以根据不同的结果进行后置处理,比如上文1.3.3 中提到的 always 、unstable 、success 、failure 和 changed 等。
示例:
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Building...'
// 这里可以放置构建代码
// 例如: sh 'make'
}
}
stage('Test') {
steps {
echo 'Testing...'
// 这里可以放置测试代码
// 例如: sh 'make test'
}
}
}
post {
success {
emailext (
subject: "成功: ${currentBuild.fullDisplayName}",
body: "构建成功!\n\n查看构建详情: ${env.BUILD_URL}",
to: 'recipient@example.com'
)
}
failure {
emailext (
subject: "失败: ${currentBuild.fullDisplayName}",
body: "构建失败!\n\n查看构建详情: ${env.BUILD_URL}",
to: 'recipient@example.com'
)
}
unstable {
emailext (
subject: "不稳定: ${currentBuild.fullDisplayName}",
body: "构建不稳定!\n\n查看构建详情: ${env.BUILD_URL}",
to: 'recipient@example.com'
)
}
}
}
5. 使用多个代理
在 Jenkins 中使用多个代理(即节点)来构建项目,可以提高资源利用率和构建速度。
pipeline {
agent none // 不指定主代理,允许在各个阶段指定代理
stages {
stage('Build on Node 1') {
agent { label 'node1' } // 指定使用标签为 node1 的代理
steps {
echo 'Building on Node 1...'
// 构建命令,例如: sh 'make'
}
}
stage('Test on Node 2') {
agent { label 'node2' } // 指定使用标签为 node2 的代理
steps {
echo 'Testing on Node 2...'
// 测试命令,例如: sh 'make test'
}
}
stage('Deploy on Node 3') {
agent { label 'node3' } // 指定使用标签为 node3 的代理
steps {
echo 'Deploying on Node 3...'
// 部署命令,例如: sh './deploy.sh'
}
}
}
post {
success {
echo 'Pipeline completed successfully!'
}
failure {
echo 'Pipeline failed.'
}
}
}
1.4 Jenkinsfile 脚本的维护
在 Jenkins 中,通常有两种常见的方式来管理和维护 Jenkinsfile:通过 Web 界面和通过 Git。
1. 通过 Web 界面管理 Jenkinsfile
-
易于访问:直接在 Jenkins 的 Web 界面中编辑,可以快速修改构建设置。
-
即时反馈:可以立即看到更改的效果和构建结果。
-
版本控制缺失:无法像代码一样轻松管理历史版本,难以追踪更改。
-
团队协作困难:多位开发者可能同时进行更改,容易导致冲突和覆盖。
2 通过 Git 管理 Jenkinsfile
在 Jenkins 中,使用 Git 管理 Jenkinsfile 通常需要配置源代码管理(SCM),但是相较于 Web 界面具有以下优点:
- 版本控制:可以追踪所有更改,有助于审计和回滚。
- 团队协作:支持多人协作,利用 Git 的分支和合并功能,避免冲突。
- 持续集成:可以将 Jenkinsfile 存储在与应用代码同一仓库中,确保一致性。
1.5 Jenkinsfile 的初步使用
单节点 Jenkins 整合 K8S
1.5.1 准备工作
1. Jenkins 安装插件
pipeline-stage-view
2. 为 K8S 创建 docker 私服的 secret
kubectl create secret docker-registry myregistrykey \
--docker-server=<你的私有仓库地址> \
--docker-username=<你的用户名> \
--docker-password=<你的密码> \
--docker-email=<你的邮箱>
- kubectl:这是 Kubernetes 的命令行工具,用于与 Kubernetes 集群进行交互。
- create secret:表示要创建一个新的 Secret。
- docker-registry:指定 Secret 的类型为 Docker Registry Secret。这种类型的 Secret 用于存储访问私有 Docker 镜像仓库所需的认证信息。
- myregistrykey:这是你要创建的 Secret 的名称。在这个例子中,Secret 名称为
myregistrykey。
3. 编写 dockerfile 和 yaml 文件
K8S 的 yaml文件:
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: myapp-dev
name: myapp-dev
spec:
replicas: 2
selector:
matchLabels:
app: myapp-dev
template:
metadata:
labels:
app: myapp-dev
spec:
containers:
- image: 192.168.17.210:80/test/myapp:10.2
name: myapp-dev
env:
- name: action
value: "pipeline"
- name: address
value: "beijing"
imagePullSecrets:
- name: myregistrykey # 引用 Secret
---
apiVersion: v1
kind: Service
metadata:
name: myapp-dev # Service 的名称
spec:
selector:
app: myapp-dev # 标签选择器,选择属于 "my-app" 应用程序的 Pod
ports:
- protocol: TCP # 端口协议,TCP 或 UDP
port: 8000 # Service 暴露的端口
targetPort: 8000 # Pod 真实监听的端口
type: NodePort # Service 类型,可以是 ClusterIP、NodePort、LoadBalancer 等
dockerfile
FROM openjdk:11
EXPOSE 8000
WORKDIR root
ADD ./*.jar /root/app.jar
ENTRYPOINT ["java" ,"-jar" , "/root/app.jar"]
1.5.2 编写流水线脚本
1. 总体框架
pipeline {
agent any
stages {
stage('拉取代码') {
steps {
echo '拉取代码'
}
}
stage('打包') {
steps {
echo '打包'
}
}
stage('制作镜像并推送至Harbor') {
steps {
echo '制作镜像并推送至仓库'
}
}
stage('部署到K8S中') {
steps {
echo '部署到K8S中'
}
}
}
}
2. 流水线语法
pipeline {
agent any
environment {
DOCKER_IMAGE = 'myapp:1.4'
REPO_URL = '192.168.17.210:80/test/myapp:1.4'
WORKSPACE_DIR = '/root/.jenkins/workspace/pipeline'
JAR_FILE = 'MySimpleHttpServer-1.0-SNAPSHOT.jar'
}
stages {
stage('清理工作空间') {
steps {
cleanWs()
}
}
stage('拉取代码') {
steps {
script {
git branch: 'main', credentialsId: '9098fd9c-bdc1-46d6-95b8-d4b07fee6e8f', url: 'https://gitee.com/linux-lxf/jenkins.git'
}
}
}
stage('打包') {
steps {
script {
sh '''
cd ${WORKSPACE_DIR}/MySimpleHttpServer
/usr/local/maven/bin/mvn clean package || exit 1
'''
}
}
}
stage('制作镜像并推送至Harbor') {
steps {
script {
sh '''
cd ${WORKSPACE_DIR}/docker
cp ${WORKSPACE_DIR}/MySimpleHttpServer/target/${JAR_FILE} .
docker build -t ${DOCKER_IMAGE} . || exit 1
docker tag ${DOCKER_IMAGE} ${REPO_URL}
docker push ${REPO_URL}
'''
}
}
}
stage('部署到K8S中') {
steps {
sshPublisher(
publishers: [
sshPublisherDesc(
configName: 'K8S',
transfers: [
sshTransfer(
cleanRemote: false,
excludes: '',
execCommand: '/usr/bin/kubectl apply -f /usr/local/k8s/pipeline.yaml',
execTimeout: 120000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+',
remoteDirectory: '/usr/local/k8s',
remoteDirectorySDF: false,
removePrefix: 'Kubernetes',
sourceFiles: 'Kubernetes/pipeline.yaml'
)
],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: false
)
]
)
}
}
}
post {
always {
// 可以选择发送通知或其他操作
echo '构建完成.'
}
success {
echo '构建成功!'
// 发送成功通知
}
failure {
echo '构建失败!'
// 发送失败通知
}
}
}
评论区