第七章:触发器与通知¶
触发器概述¶
Jenkins Pipeline 可以通过多种方式触发构建:
- 定时触发:cron 表达式定时执行
- SCM 轮询:定期检查代码变更
- Webhook:Git 仓库推送触发
- 上游 Job:其他 Job 完成后触发
- 手动触发:用户手动执行
triggers 指令¶
cron 定时触发¶
pipeline {
agent any
triggers {
cron('H/15 * * * *') // 每 15 分钟执行一次
}
stages {
stage('Build') {
steps {
echo 'Scheduled build'
}
}
}
}
cron 表达式¶
┌───────────── 分钟 (0-59)
│ ┌───────────── 小时 (0-23)
│ │ ┌───────────── 日 (1-31)
│ │ │ ┌───────────── 月 (1-12)
│ │ │ │ ┌───────────── 星期 (0-7, 0 和 7 都是周日)
│ │ │ │ │
* * * * *
H 表示自动分散负载(Hash)
常用表达式:
triggers {
// 每 15 分钟
cron('H/15 * * * *')
// 每小时
cron('H * * * *')
// 每天凌晨 2 点
cron('H 2 * * *')
// 每周一早上 9 点
cron('H 9 * * 1')
// 每月 1 号凌晨
cron('H H 1 * *')
// 工作日早上 9 点
cron('H 9 * * 1-5')
// 每小时 30 分
cron('30 * * * *')
}
pollSCM 轮询¶
pipeline {
agent any
triggers {
pollSCM('H/5 * * * *') // 每 5 分钟检查一次 SCM
}
stages {
stage('Build') {
steps {
echo 'SCM change detected'
}
}
}
}
GitHub Webhook¶
pipeline {
agent any
triggers {
githubPush()
}
stages {
stage('Build') {
steps {
echo 'GitHub push triggered'
}
}
}
}
GitLab Webhook¶
pipeline {
agent any
triggers {
gitlab(
triggerOnPush: true,
triggerOnMergeRequest: true,
branchFilterType: 'All'
)
}
stages {
stage('Build') {
steps {
echo 'GitLab event triggered'
}
}
}
}
上游 Job 触发¶
pipeline {
agent any
triggers {
upstream(
upstreamProjects: 'build-job',
threshold: hudson.model.Result.SUCCESS
)
}
stages {
stage('Deploy') {
steps {
echo 'Upstream job completed'
}
}
}
}
组合触发器¶
pipeline {
agent any
triggers {
cron('H 2 * * *') // 每天凌晨 2 点
pollSCM('H/5 * * * *') // 每 5 分钟检查 SCM
githubPush() // GitHub 推送触发
}
stages {
stage('Build') {
steps {
script {
def causes = currentBuild.getBuildCauses()
causes.each { cause ->
echo "Triggered by: ${cause.shortDescription}"
}
}
}
}
}
}
通知机制¶
Email 通知¶
pipeline {
agent any
post {
success {
mail to: 'team@example.com',
subject: "✅ Build Success: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: """
Build succeeded!
Job: ${env.JOB_NAME}
Build: ${env.BUILD_NUMBER}
URL: ${env.BUILD_URL}
"""
}
failure {
mail to: 'team@example.com',
subject: "❌ Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: """
Build failed!
Job: ${env.JOB_NAME}
Build: ${env.BUILD_NUMBER}
URL: ${env.BUILD_URL}
"""
}
}
}
emailext 插件¶
pipeline {
agent any
post {
always {
emailext(
subject: '${DEFAULT_SUBJECT}',
body: '${DEFAULT_CONTENT}',
to: 'team@example.com',
attachLog: true,
compressLog: true
)
}
}
}
Slack 通知¶
pipeline {
agent any
post {
success {
slackSend(
channel: '#builds',
color: 'good',
message: "✅ Build Success: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
)
}
failure {
slackSend(
channel: '#builds',
color: 'danger',
message: "❌ Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}\nURL: ${env.BUILD_URL}"
)
}
}
}
钉钉通知¶
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'npm run build'
}
}
}
post {
success {
dingtalk(
robot: 'dingtalk-robot',
type: 'MARKDOWN',
title: '构建成功',
text: [
"### 构建成功 ✅",
"- 任务: ${env.JOB_NAME}",
"- 构建: #${env.BUILD_NUMBER}",
"- [查看详情](${env.BUILD_URL})"
]
)
}
failure {
dingtalk(
robot: 'dingtalk-robot',
type: 'MARKDOWN',
title: '构建失败',
text: [
"### 构建失败 ❌",
"- 任务: ${env.JOB_NAME}",
"- 构建: #${env.BUILD_NUMBER}",
"- [查看详情](${env.BUILD_URL})"
]
)
}
}
}
企业微信通知¶
pipeline {
agent any
post {
always {
script {
def status = currentBuild.result ?: 'SUCCESS'
def emoji = status == 'SUCCESS' ? '✅' : '❌'
def color = status == 'SUCCESS' ? 'green' : 'red'
def message = """
{
"msgtype": "markdown",
"markdown": {
"content": "${emoji} **构建${status == 'SUCCESS' ? '成功' : '失败'}**\\n> 任务: ${env.JOB_NAME}\\n> 构建: #${env.BUILD_NUMBER}\\n> [查看详情](${env.BUILD_URL})"
}
}
"""
withCredentials([string(credentialsId: 'wechat-webhook', variable: 'WEBHOOK_URL')]) {
sh "curl -X POST -H 'Content-Type: application/json' -d '${message}' $WEBHOOK_URL"
}
}
}
}
}
HTTP 通知¶
pipeline {
agent any
post {
always {
script {
def payload = """
{
"job": "${env.JOB_NAME}",
"build": "${env.BUILD_NUMBER}",
"status": "${currentBuild.result ?: 'SUCCESS'}",
"url": "${env.BUILD_URL}"
}
"""
withCredentials([string(credentialsId: 'api-token', variable: 'API_TOKEN')]) {
sh """
curl -X POST \
-H "Authorization: Bearer $API_TOKEN" \
-H "Content-Type: application/json" \
-d '${payload}' \
https://api.example.com/webhook/jenkins
"""
}
}
}
}
}
条件通知¶
根据分支发送¶
pipeline {
agent any
post {
success {
script {
if (env.BRANCH_NAME == 'main') {
slackSend(
channel: '#production',
color: 'good',
message: "Production deployed: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
)
} else if (env.BRANCH_NAME == 'develop') {
slackSend(
channel: '#development',
color: 'good',
message: "Staging deployed: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
)
}
}
}
}
}
根据构建原因发送¶
pipeline {
agent any
post {
success {
script {
def causes = currentBuild.getBuildCauses()
def isScheduled = causes.any { it._class.contains('TimerTriggerCause') }
if (isScheduled) {
mail to: 'team@example.com',
subject: "Scheduled build completed: ${env.JOB_NAME}",
body: "Scheduled build finished successfully."
}
}
}
}
}
通知责任人¶
pipeline {
agent any
post {
failure {
script {
// 获取最近提交者
def committer = sh(
script: 'git log -1 --pretty=format:"%ae"',
returnStdout: true
).trim()
mail to: committer,
subject: "Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "Your commit caused the build to fail.\n\nURL: ${env.BUILD_URL}"
}
}
}
}
构建状态徽章¶
生成徽章¶
pipeline {
agent any
stages {
stage('Build') {
steps {
sh 'npm run build'
}
}
}
post {
always {
// 添加构建状态到描述
script {
def status = currentBuild.result ?: 'SUCCESS'
def emoji = status == 'SUCCESS' ? '✅' : '❌'
currentBuild.description = "${emoji} Build ${status}"
}
}
}
}
README 徽章¶
在 README 中添加 Jenkins 构建状态徽章:
[](https://jenkins.example.com/job/my-job/)
完整示例¶
pipeline {
agent any
triggers {
cron('H 2 * * *') // 每天凌晨 2 点
githubPush() // GitHub 推送
}
environment {
SLACK_CHANNEL = '#builds'
}
stages {
stage('Build') {
steps {
sh 'npm run build'
}
}
stage('Test') {
steps {
sh 'npm test'
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh 'npm run deploy'
}
}
}
post {
always {
// 清理
cleanWs()
// 归档日志
archiveArtifacts artifacts: 'logs/**', allowEmptyArchive: true
}
success {
script {
// Slack 通知
slackSend(
channel: env.SLACK_CHANNEL,
color: 'good',
message: """
✅ Build Success
• Job: ${env.JOB_NAME}
• Build: #${env.BUILD_NUMBER}
• Branch: ${env.BRANCH_NAME ?: 'N/A'}
• Duration: ${currentBuild.durationString}
• URL: ${env.BUILD_URL}
"""
)
// 主分支发送邮件
if (env.BRANCH_NAME == 'main') {
emailext(
subject: "✅ Production Deployed: ${env.JOB_NAME}",
body: """
Production deployment completed successfully.
Job: ${env.JOB_NAME}
Build: #${env.BUILD_NUMBER}
Duration: ${currentBuild.durationString}
URL: ${env.BUILD_URL}
""",
to: 'team@example.com'
)
}
}
}
failure {
script {
// 获取提交者
def committer = sh(
script: 'git log -1 --pretty=format:"%ae"',
returnStdout: true
).trim()
// Slack 通知
slackSend(
channel: env.SLACK_CHANNEL,
color: 'danger',
message: """
❌ Build Failed
• Job: ${env.JOB_NAME}
• Build: #${env.BUILD_NUMBER}
• Branch: ${env.BRANCH_NAME ?: 'N/A'}
• Committer: ${committer}
• URL: ${env.BUILD_URL}
"""
)
// 邮件通知提交者
emailext(
subject: "❌ Build Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: """
Your commit caused the build to fail.
Job: ${env.JOB_NAME}
Build: #${env.BUILD_NUMBER}
Branch: ${env.BRANCH_NAME ?: 'N/A'}
URL: ${env.BUILD_URL}
Please check the build log for details.
""",
to: committer
)
}
}
unstable {
slackSend(
channel: env.SLACK_CHANNEL,
color: 'warning',
message: "⚠️ Build Unstable: ${env.JOB_NAME} #${env.BUILD_NUMBER}"
)
}
}
}
小结¶
本章介绍了触发器与通知:
- cron 定时触发和 pollSCM 轮询
- Webhook 触发(GitHub、GitLab)
- 上游 Job 触发
- Email、Slack、钉钉、企业微信通知
- 条件通知和责任人通知
- 构建状态徽章
下一章将介绍分布式构建。