流水线步骤 #
Pipeline步骤(Steps)是Pipeline中最基本的执行单元,每个步骤执行特定的任务。本节将详细介绍常用的Pipeline步骤。
基本步骤 #
echo #
输出文本信息到控制台:
groovy
steps {
echo 'Hello, Jenkins!'
echo "Build number: ${BUILD_NUMBER}"
echo """
Multi-line
text output
"""
}
sh #
执行Shell脚本:
groovy
steps {
sh 'echo "Hello"'
sh '''
echo "Multi-line script"
ls -la
pwd
'''
def output = sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
echo "Commit: ${output}"
def status = sh(script: 'test -f file.txt', returnStatus: true)
if (status == 0) {
echo 'File exists'
}
}
参数说明:
| 参数 | 说明 |
|---|---|
| script | 要执行的脚本 |
| returnStdout | 是否返回标准输出 |
| returnStatus | 是否返回状态码 |
| encoding | 输出编码 |
bat #
执行Windows批处理命令:
groovy
steps {
bat 'echo Hello'
bat '''
echo Multi-line script
dir
'''
}
powershell #
执行PowerShell脚本:
groovy
steps {
powershell 'Write-Host "Hello"'
powershell '''
Get-ChildItem
Get-Process
'''
powershell(script: 'Get-Service', returnStdout: true)
}
script #
执行Groovy脚本块:
groovy
steps {
script {
def files = findFiles(glob: '**/*.java')
files.each { f ->
echo "Found: ${f.name}"
}
}
}
代码管理步骤 #
checkout #
检出源代码:
groovy
steps {
checkout scm
checkout([
$class: 'GitSCM',
branches: [[name: '*/main']],
doGenerateSubmoduleConfigurations: false,
extensions: [
[$class: 'CleanBeforeCheckout'],
[$class: 'CloneOption', depth: 1, noTags: true, shallow: true]
],
submoduleCfg: [],
userRemoteConfigs: [[
url: 'https://github.com/user/repo.git',
credentialsId: 'github-creds'
]]
])
}
常用扩展:
groovy
extensions: [
[$class: 'CleanBeforeCheckout'],
[$class: 'CheckoutOption', timeout: 10],
[$class: 'CloneOption', depth: 1, shallow: true],
[$class: 'LocalBranch', localBranch: 'main'],
[$class: 'RelativeTargetDirectory', relativeTargetDir: 'my-repo'],
[$class: 'SparseCheckoutPaths', sparseCheckoutPaths: [[path: 'src/']]]
]
git #
简化的Git检出:
groovy
steps {
git url: 'https://github.com/user/repo.git',
branch: 'main',
credentialsId: 'github-creds'
git url: 'https://github.com/user/repo.git',
branch: 'develop',
changelog: false,
poll: false
}
svn #
检出SVN代码:
groovy
steps {
checkout([
$class: 'SubversionSCM',
locations: [[
remote: 'https://svn.example.com/repo/trunk',
credentialsId: 'svn-creds',
local: '.'
]]
])
}
文件操作步骤 #
archiveArtifacts #
归档构建产物:
groovy
steps {
archiveArtifacts artifacts: 'target/*.jar',
fingerprint: true,
allowEmptyArchive: false,
excludes: 'target/*.original',
caseSensitive: true
}
stash/unstash #
在节点间传递文件:
groovy
stage('Build') {
steps {
sh 'mvn clean package'
stash includes: 'target/*.jar', name: 'artifacts', allowEmpty: false
}
}
stage('Deploy') {
agent { label 'production' }
steps {
unstash 'artifacts'
sh 'kubectl apply -f k8s/'
}
}
参数说明:
| 参数 | 说明 |
|---|---|
| name | stash名称 |
| includes | 包含的文件模式 |
| excludes | 排除的文件模式 |
| allowEmpty | 是否允许空stash |
| useDefaultExcludes | 是否使用默认排除规则 |
cleanWs #
清理工作空间:
groovy
steps {
cleanWs()
cleanWs cleanWhenSuccess: false,
cleanWhenFailure: true,
deleteDirs: true,
disableDeferredWipeout: true,
patterns: [
[pattern: 'target/**', type: 'INCLUDE'],
[pattern: 'node_modules/**', type: 'INCLUDE']
]
}
dir #
切换工作目录:
groovy
steps {
dir('subdirectory') {
sh 'ls -la'
}
dir('/tmp/custom') {
sh 'pwd'
}
}
writeFile #
写入文件:
groovy
steps {
writeFile file: 'config.json',
text: '{"key": "value"}',
encoding: 'UTF-8'
writeFile file: 'script.sh',
text: '''#!/bin/bash
echo "Hello"
''',
encoding: 'UTF-8'
}
readFile #
读取文件:
groovy
steps {
def content = readFile file: 'config.json', encoding: 'UTF-8'
echo "Content: ${content}"
}
fileExists #
检查文件是否存在:
groovy
steps {
if (fileExists 'config.json') {
echo 'Config file exists'
}
}
findFiles #
查找文件:
groovy
steps {
def files = findFiles(glob: '**/*.java')
files.each { f ->
echo "Found: ${f.name}"
echo "Path: ${f.path}"
echo "Size: ${f.length} bytes"
echo "Last modified: ${f.lastModified}"
echo "Is directory: ${f.directory}"
}
}
deleteDir #
删除目录:
groovy
steps {
deleteDir()
dir('target') {
deleteDir()
}
}
touch #
创建或更新文件时间戳:
groovy
steps {
touch file: 'marker.txt', timestamp: System.currentTimeMillis()
}
测试相关步骤 #
junit #
发布JUnit测试报告:
groovy
steps {
junit '**/target/surefire-reports/*.xml'
junit testResults: '**/target/surefire-reports/*.xml',
allowEmptyResults: true,
healthScaleFactor: 1.0,
keepLongStdio: true,
testDataPublishers: [
[$class: 'ClaimTestDataPublisher'],
[$class: 'StabilityTestDataPublisher']
]
}
publishHTML #
发布HTML报告:
groovy
steps {
publishHTML(target: [
allowMissing: false,
alwaysLinkToLastBuild: true,
keepAll: true,
reportDir: 'target/site',
reportFiles: 'index.html',
reportName: 'Site Report',
reportTitles: 'My Report'
])
}
recordIssues #
记录代码问题:
groovy
steps {
recordIssues(tools: [
java(),
spotBugs(),
checkStyle(),
pmdParser()
])
recordIssues(
tools: [java()],
qualityGates: [[threshold: 10, type: 'TOTAL', status: 'WARNING']],
filters: [excludeFile('**/generated/**')]
)
}
代码质量步骤 #
withSonarQubeEnv #
集成SonarQube:
groovy
steps {
withSonarQubeEnv('SonarQube') {
sh 'mvn sonar:sonar'
}
}
waitForQualityGate #
等待质量门禁:
groovy
steps {
timeout(time: 5, unit: 'MINUTES') {
def qg = waitForQualityGate()
if (qg.status != 'OK') {
error "Quality gate failed: ${qg.status}"
}
}
}
凭据步骤 #
withCredentials #
使用凭据:
groovy
steps {
withCredentials([usernamePassword(
credentialsId: 'docker-registry',
usernameVariable: 'DOCKER_USER',
passwordVariable: 'DOCKER_PASS'
)]) {
sh "docker login -u ${DOCKER_USER} -p ${DOCKER_PASS}"
}
}
凭据类型:
groovy
withCredentials([
usernamePassword(
credentialsId: 'creds',
usernameVariable: 'USER',
passwordVariable: 'PASS'
),
sshUserPrivateKey(
credentialsId: 'ssh-key',
keyFileVariable: 'SSH_KEY',
usernameVariable: 'SSH_USER'
),
file(
credentialsId: 'kube-config',
variable: 'KUBECONFIG'
),
string(
credentialsId: 'api-token',
variable: 'API_TOKEN'
),
certificate(
credentialsId: 'client-cert',
keystoreVariable: 'KEYSTORE',
passwordVariable: 'PASSWORD'
)
]) {
}
usernamePassword #
groovy
withCredentials([usernameColonPassword(
credentialsId: 'creds',
variable: 'USERPASS'
)]) {
sh "curl -u ${USERPASS} https://api.example.com"
}
Docker步骤 #
withDockerContainer #
在Docker容器中执行:
groovy
steps {
withDockerContainer('maven:3.8-openjdk-11') {
sh 'mvn clean package'
}
}
withDockerRegistry #
登录Docker仓库:
groovy
steps {
withDockerRegistry(credentialsId: 'docker-registry', url: 'https://registry.example.com') {
sh 'docker push myapp:latest'
}
}
docker #
Docker操作:
groovy
steps {
script {
def image = docker.build('myapp:${BUILD_NUMBER}')
image.push()
image.push('latest')
docker.image('maven:3.8').inside {
sh 'mvn clean package'
}
}
}
通知步骤 #
mail #
发送邮件:
groovy
steps {
mail to: 'team@example.com',
subject: "Build ${BUILD_NUMBER}",
body: "Build completed: ${BUILD_URL}",
from: 'jenkins@example.com',
replyTo: 'no-reply@example.com',
mimeType: 'text/html'
}
emailext #
扩展邮件:
groovy
steps {
emailext(
subject: "Build ${BUILD_NUMBER}: ${currentBuild.result}",
body: '''${SCRIPT, template="groovy-html.template"}''',
to: 'team@example.com',
from: 'jenkins@example.com',
replyTo: 'no-reply@example.com',
mimeType: 'text/html',
attachLog: true,
attachmentsPattern: '**/target/*.jar',
recipientProviders: [
[$class: 'DevelopersRecipientProvider'],
[$class: 'RequesterRecipientProvider']
]
)
}
slack #
发送Slack消息:
groovy
steps {
slackSend(
channel: '#builds',
color: 'good',
message: "Build ${BUILD_NUMBER} succeeded: ${BUILD_URL}",
baseUrl: 'https://hooks.slack.com/services/',
tokenCredentialId: 'slack-token'
)
}
输入步骤 #
input #
等待用户输入:
groovy
stage('Deploy') {
steps {
input message: 'Deploy to production?',
ok: 'Yes, deploy!',
submitter: 'admin,deploy-team',
submitterParameter: 'APPROVER',
parameters: [
choice(name: 'ENVIRONMENT',
choices: ['prod', 'staging'],
description: 'Select environment'),
string(name: 'VERSION',
defaultValue: "${BUILD_NUMBER}",
description: 'Version to deploy')
]
}
}
milestone #
确保构建顺序:
groovy
stage('Deploy') {
steps {
milestone 1
sh 'deploy.sh'
}
}
等待步骤 #
waitUntil #
等待条件满足:
groovy
steps {
waitUntil {
script {
def r = sh script: 'curl -s http://localhost:8080/health', returnStatus: true
return r == 0
}
}
waitUntil(initialRecurrencePeriod: 5000, quiet: false) {
script {
return fileExists 'marker.txt'
}
}
}
sleep #
暂停执行:
groovy
steps {
sleep time: 10, unit: 'SECONDS'
sleep 30
sleep(time: 1, unit: 'MINUTES')
}
retry #
重试执行:
groovy
steps {
retry(3) {
sh 'deploy.sh'
}
retry(count: 3, conditions: [
unstable(),
failure()
]) {
sh 'test.sh'
}
}
timeout #
超时控制:
groovy
steps {
timeout(time: 5, unit: 'MINUTES') {
sh 'long-running-task.sh'
}
timeout(time: 30, activity: true) {
input message: 'Approve deployment?'
}
}
构建触发步骤 #
build #
触发其他任务:
groovy
steps {
build job: 'downstream-job',
parameters: [
string(name: 'VERSION', value: "${BUILD_NUMBER}"),
booleanParam(name: 'DEPLOY', value: true)
],
wait: true,
propagate: true,
quietPeriod: 10
}
triggerRemoteJob #
触发远程任务:
groovy
steps {
triggerRemoteJob(
project: 'remote-job',
auth: 'credentials-id',
parameters: 'VERSION=${BUILD_NUMBER}',
shouldNotFailBuild: true
)
}
工具步骤 #
tool #
使用预配置工具:
groovy
steps {
def mvnHome = tool 'Maven 3.8'
def javaHome = tool name: 'JDK 11', type: 'jdk'
env.PATH = "${mvnHome}/bin:${env.PATH}"
env.JAVA_HOME = javaHome
sh 'mvn clean package'
}
withMaven #
Maven环境:
groovy
steps {
withMaven(maven: 'Maven 3.8', jdk: 'JDK 11') {
sh 'mvn clean package'
}
withMaven(
maven: 'Maven 3.8',
jdk: 'JDK 11',
mavenSettingsConfig: 'maven-settings',
mavenLocalRepo: '.repository'
) {
sh 'mvn clean package'
}
}
withAnt #
Ant环境:
groovy
steps {
withAnt(installation: 'Ant 1.10') {
sh 'ant build'
}
}
withGradle #
Gradle环境:
groovy
steps {
withGradle {
sh 'gradle build'
}
}
环境步骤 #
withEnv #
设置环境变量:
groovy
steps {
withEnv(['PATH+MAVEN=/opt/maven/bin', 'JAVA_HOME=/usr/lib/jvm/java-11']) {
sh 'mvn clean package'
}
withEnv(['MY_VAR=value', 'ANOTHER_VAR=another']) {
echo "MY_VAR: ${env.MY_VAR}"
}
}
withAnt #
配置环境:
groovy
steps {
withEnv(["BUILD_ENV=production"]) {
sh 'deploy.sh'
}
}
锁定步骤 #
lock #
锁定资源:
groovy
steps {
lock(resource: 'production-server') {
sh 'deploy.sh'
}
lock(resource: 'production-server', inversePrecedence: true, quantity: 1) {
sh 'deploy.sh'
}
lock(label: 'production', quantity: 2) {
sh 'deploy.sh'
}
}
完整示例 #
综合使用各种步骤 #
groovy
pipeline {
agent any
environment {
APP_NAME = 'myapp'
VERSION = "${BUILD_NUMBER}"
}
stages {
stage('Checkout') {
steps {
checkout scm
script {
env.GIT_COMMIT = sh(script: 'git rev-parse HEAD', returnStdout: true).trim()
}
echo "Commit: ${env.GIT_COMMIT}"
}
}
stage('Build') {
steps {
withMaven(maven: 'Maven 3.8', jdk: 'JDK 11') {
sh 'mvn clean package -DskipTests'
}
archiveArtifacts artifacts: 'target/*.jar', fingerprint: true
stash includes: 'target/*.jar', name: 'artifacts'
}
}
stage('Test') {
parallel {
stage('Unit Tests') {
steps {
sh 'mvn test'
}
post {
always {
junit '**/target/surefire-reports/*.xml'
}
}
}
stage('Integration Tests') {
steps {
sh 'mvn verify -P integration'
}
post {
always {
junit '**/target/failsafe-reports/*.xml'
}
}
}
}
}
stage('SonarQube') {
steps {
withSonarQubeEnv('SonarQube') {
sh 'mvn sonar:sonar'
}
}
}
stage('Quality Gate') {
steps {
timeout(time: 5, unit: 'MINUTES') {
waitForQualityGate abortPipeline: true
}
}
}
stage('Build Image') {
steps {
script {
docker.withRegistry('https://registry.example.com', 'docker-registry') {
def image = docker.build("${APP_NAME}:${VERSION}")
image.push()
image.push('latest')
}
}
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
input message: 'Deploy to production?',
submitter: 'admin'
lock(resource: 'production-environment') {
unstash 'artifacts'
withCredentials([file(credentialsId: 'kube-config', variable: 'KUBECONFIG')]) {
sh "kubectl set image deployment/${APP_NAME} ${APP_NAME}=${APP_NAME}:${VERSION}"
}
waitUntil {
script {
def r = sh(script: 'kubectl rollout status deployment/${APP_NAME}', returnStatus: true)
return r == 0
}
}
}
}
}
}
post {
always {
cleanWs()
}
success {
emailext(
subject: "Success: ${JOB_NAME} #${BUILD_NUMBER}",
body: '''Build succeeded!
Commit: ${GIT_COMMIT}
URL: ${BUILD_URL}''',
to: 'team@example.com'
)
slackSend(
channel: '#builds',
color: 'good',
message: "Build ${BUILD_NUMBER} succeeded: ${BUILD_URL}"
)
}
failure {
emailext(
subject: "Failed: ${JOB_NAME} #${BUILD_NUMBER}",
body: 'Build failed!',
to: 'team@example.com',
attachLog: true
)
slackSend(
channel: '#builds',
color: 'danger',
message: "Build ${BUILD_NUMBER} failed: ${BUILD_URL}"
)
}
}
}
下一步学习 #
小结 #
- 步骤是Pipeline的基本执行单元
- 支持丰富的内置步骤
- 可以使用插件扩展更多步骤
- 合理组合步骤实现复杂流程
- 使用Pipeline Syntax生成器辅助编写
最后更新:2026-03-28