在前后端分离、电商业务频繁变更的场景下,仅靠手工打包、手工上传、手工重启容器很难支撑快速迭代,也无法满足对可观测性和反馈速度的要求。 这篇文章围绕一个真实的 B2C 前端项目,展示如何用 Jenkins Pipeline 打造“一键热部署 + Discord 实时通知”的前端部署流水线。
通过将流水线写成pipeline,不仅可以版本化管理 CI/CD 流程,还能将工程化能力显式地沉淀在仓库中,方便团队复用和评审。
以下主要解决三件事:自动构建产物、将产物安全地分发到远端环境、以及在构建结束后主动推送通知到 discord。pipeline作为主干结构,保持了配置的可读性,同时用少量 script 区块处理 shell 命令和 JSON 组装等“不可避免的脚本化逻辑”。
pipeline {
agent { label 'built' }
environment {
TAR_PKG_NAME = 'storefront.tar.gz'
REMOTE_MACHINE_CONFIG_NAME = 'dev-52'
BASE_DIR = '/opt/dev'
WORK_DIR = '/opt/dev/yc/mall-b2c-front'
DOCKER_COMPOSE_NAME = 'docker-compose.yaml'
WEBHOOK_URL = "https://discord.com/api/webhooks/xxxx/xxxx" // 示例占位
TITLE = "b2c_frontend_dev_hot_deploy"
PROXY_URL = "http://192.168.2.30:10808"
GIT_REPOSITORY = "http://gitea.repository.yc/mall-b2c/mall-b2c-front.git"
GIT_REPOSITORY_CREDENTIALID = "gitea_devops_pwd"
GIT_REPOSITORY_BRANCH = "dev"
}
stages {
stage('Checkout') {
steps {
git branch: "${GIT_REPOSITORY_BRANCH}",
credentialsId: "${GIT_REPOSITORY_CREDENTIALID}",
url: "${GIT_REPOSITORY}"
}
}
stage('Package') {
steps {
sh """
tar -czvf ${TAR_PKG_NAME} \\
--exclude=.git \\
--exclude=${TAR_PKG_NAME} \\
./
"""
}
}
stage('delivery and deploy') {
steps {
sshPublisher(publishers: [
sshPublisherDesc(
configName: "${REMOTE_MACHINE_CONFIG_NAME}",
transfers: [
sshTransfer(
cleanRemote: false,
excludes: '.git/**',
execCommand: """
mkdir -p ${WORK_DIR}
cd ${BASE_DIR}
tar -xf ${TAR_PKG_NAME} --strip-components=1 -C ${WORK_DIR}
cd ${WORK_DIR}
docker cp src storefront-dev:/app/
docker compose restart
""",
execTimeout: 1800000,
flatten: false,
makeEmptyDirs: false,
noDefaultExcludes: false,
patternSeparator: '[, ]+',
remoteDirectory: '',
remoteDirectorySDF: false,
removePrefix: '',
sourceFiles: "${TAR_PKG_NAME}"
)
],
usePromotionTimestamp: false,
useWorkspaceInPromotion: false,
verbose: true
)
])
}
}
}
post {
success {
script {
sendDiscordNotification('success')
}
}
failure {
script {
sendDiscordNotification('failure')
}
}
}
}
def sendDiscordNotification(String status) {
script {
try {
def epoch = sh(script: 'date +%s', returnStdout: true).trim()
def commitMessage = sh(script: 'git show -s --pretty=%s', returnStdout: true).trim()
def commitUser = sh(script: 'git show --format="%an <%ae>" --no-patch', returnStdout: true).trim()
// 转义特殊字符,避免 JSON 解析错误
commitMessage = commitMessage.replace('"', '\\"').replace('\n', '\\n')
commitUser = commitUser.replace('"', '\\"')
def color = status == 'success' ? '3066993' : '15158332'
def emoji = status == 'success' ? '🎉' : '❌'
def statusText = status == 'success' ? '✅ Success' : '❌ Failed'
def actionText = status == 'success' ? '热部署完成' : '热部署失败'
def jsonFile = 'discord_payload.json'
writeFile file: jsonFile, text: """
{
"content": "@everyone",
"username": "CI · Build Bot",
"avatar_url": "https://i.imgur.com/your-bot-avatar.png",
"embeds": [
{
"title": "${emoji} ${TITLE} ${actionText}",
"description": "**状态:** ${statusText}\\n**结束时间:** <t:${epoch}:F>\\n**提交描述:** ${commitMessage}\\n**提交用户:** ${commitUser}",
"color": ${color}
}
]
}
"""
echo "发送 Discord 通知..."
echo "----------------------------------------------------------------------------------"
def curlResult = sh(
script: """
curl --connect-timeout 10 --retry 5 --retry-all-errors --retry-delay 2 \\
--proxy "${PROXY_URL}" \\
-s -w "%{http_code}" -o /tmp/discord_response.txt \\
-X POST \\
-H "Content-Type: application/json" \\
-d @${jsonFile} \\
"${WEBHOOK_URL}"
""",
returnStdout: true
).trim()
if (curlResult == "200" || curlResult == "204") {
echo "✅ Discord 通知发送成功 (HTTP ${curlResult})"
} else {
error "❌ Discord 通知发送失败 (HTTP ${curlResult})"
}
sh "rm -f ${jsonFile} /tmp/discord_response.txt"
} catch (Exception e) {
echo "⚠️ Discord 通知发送失败: ${e.getMessage()}"
echo "继续执行流水线..."
}
}
}基于当前 Pipeline,很容易进一步增强 devops 能力,比如加入测试阶段,只允许所有用例通过后才打包上传;再比如将环境信息参数化,让同一套 Jenkinsfile 通过参数即可部署到 dev / staging / prod 多个环境。