| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- pipeline {
- agent any
- environment {
- // 语言环境设置
- LANG = 'en_US.UTF-8'
- LC_ALL = 'en_US.UTF-8'
- // 项目配置
- VITE_NODE_ENV = 'production'
- PROJECT_NAME = 'tg-live-game-web'
- // 分支配置
- BUILD_BRANCH = 'main'
- // Node 配置
- NODE_VERSION = 'v18.20.4'
- NODE_PATH = "/root/.nvm/versions/node/${NODE_VERSION}/bin"
- PNPM_PATH = '/root/.nvm/versions/node/v18.20.4/bin/pnpm'
- // Cloudflare 配置
- CLOUDFLARE_PROJECT_NAME = 'tg-live-game'
- DEPLOY_PATH = 'web'
- DEPLOY_URL = "https://${CLOUDFLARE_PROJECT_NAME}.pwtk.cc"
- // 日志配置
- BUILD_LOG_FILE = "${WORKSPACE}/build_${BUILD_NUMBER}.log"
- // ========================================
- // 第三方服务配置
- // ========================================
- // Lark (飞书) 配置
- LARK_WEBHOOK_URL = 'https://open.larksuite.com/open-apis/bot/v2/hook/7142cabc-603b-423f-8081-315cd3c19365'
- // Telegram 配置
- TELEGRAM_BOT_TOKEN = '8132916684:AAEd1oObTMPxxcLV6LICKIDTPf1vBSn2rGk'
- TELEGRAM_CHAT_ID = '7061322031'
- // 禅道配置 (可选)
- ZENTAO_PRODUCT_ID = '1'
- ZENTAO_API_URL = 'https://zentao.pwtk.cc/zentao/api.php/v1'
- ZENTAO_BUG_BASE_URL = 'https://zentao.pwtk.cc/zentao/bug-view-'
- }
- options {
- // 保留最近的构建记录
- buildDiscarder(logRotator(
- numToKeepStr: '1',
- daysToKeepStr: '1'
- ))
- // 禁止并发构建
- disableConcurrentBuilds()
- // 构建超时时间
- timeout(time: 30, unit: 'MINUTES')
- }
- stages {
- stage('初始化') {
- steps {
- script {
- sh '''
- echo "=============== 初始化 ==============="
- echo "PROJECT_NAME=${PROJECT_NAME}"
- echo "BUILD_BRANCH=${BUILD_BRANCH}"
- echo "NODE_VERSION=${NODE_VERSION}"
- source ${WORKSPACE}/jenkins/lib/logger.sh
- source ${WORKSPACE}/jenkins/lib/time-utils.sh
- source ${WORKSPACE}/jenkins/lib/telegram.sh
- source ${WORKSPACE}/jenkins/lib/lark_dev.sh
- source ${WORKSPACE}/jenkins/lib/cloudflare_sync.sh
- # 导出函数供后续使用
- export -f log_message
- export -f send_telegram_message
- export -f send_telegram_file
- export -f send_lark_message
- export -f send_lark_fix_notification
- export -f update_progress
- export -f get_network_time
- export -f deploy_to_cloudflare
- # 清理旧日志
- cleanup_old_logs "/tmp" "build_"
- # 初始化日志文件
- init_log_file
- log_message "=============== 构建开始 ==============="
- log_message "构建编号: ${BUILD_NUMBER}"
- log_message "工作目录: ${WORKSPACE}"
- # 记录构建开始时间
- BUILD_START_TIME=$(get_network_time)
- BUILD_START_TIMESTAMP=$(get_timestamp "$BUILD_START_TIME")
- echo "BUILD_START_TIME='${BUILD_START_TIME}'" > ${WORKSPACE}/build_vars.env
- echo "BUILD_START_TIMESTAMP=${BUILD_START_TIMESTAMP}" >> ${WORKSPACE}/build_vars.env
- log_message "构建开始时间: ${BUILD_START_TIME}"
- # 发送初始进度
- update_progress 0 "初始化"
- '''
- }
- }
- }
- stage('代码拉取') {
- steps {
- script {
- sh '''
- source ${WORKSPACE}/jenkins/lib/logger.sh
- source ${WORKSPACE}/jenkins/lib/telegram.sh
- source ${WORKSPACE}/build_vars.env
- log_message "检查当前 Git 状态"
- log_message "当前分支: $(git rev-parse --abbrev-ref HEAD)"
- log_message "最新提交: $(git log -1 --oneline)"
- # 获取最新提交信息
- LAST_COMMIT_DATE=$(git log -1 --format='%ci' | sed 's/ [+-][0-9]\\{4\\}$//')
- LAST_COMMIT_MESSAGE=$(git log -1 --pretty=format:"%s")
- LAST_COMMIT_HASH=$(git log -1 --pretty=format:"%H" | cut -c1-8)
- echo "LAST_COMMIT_DATE='${LAST_COMMIT_DATE}'" >> ${WORKSPACE}/build_vars.env
- echo "LAST_COMMIT_MESSAGE='${LAST_COMMIT_MESSAGE}'" >> ${WORKSPACE}/build_vars.env
- echo "LAST_COMMIT_HASH='${LAST_COMMIT_HASH}'" >> ${WORKSPACE}/build_vars.env
- log_message "提交日期: ${LAST_COMMIT_DATE}"
- log_message "提交信息: ${LAST_COMMIT_MESSAGE}"
- log_message "提交哈希: ${LAST_COMMIT_HASH}"
- # 提取修复信息
- log_message "提取本次构建的修复信息"
- LAST_SUCCESS_HASH=$(cat ${WORKSPACE}/.last_success_commit 2>/dev/null || echo "")
- if [ -z "${LAST_SUCCESS_HASH}" ]; then
- log_message "首次构建,提取最近 20 个 fix 提交"
- FIX_COMMITS=$(git log -20 --oneline | grep -E "^[a-f0-9]+ fix[:(]" || true)
- else
- log_message "上次成功构建: ${LAST_SUCCESS_HASH}"
- FIX_COMMITS=$(git log ${LAST_SUCCESS_HASH}..HEAD --oneline | grep -E "^[a-f0-9]+ fix[:(]" || true)
- fi
- if [ -z "${FIX_COMMITS}" ]; then
- log_message "没有找到包含 fix: 的提交"
- echo "FIX_LIST='无修复记录'" >> ${WORKSPACE}/build_vars.env
- echo "FIX_COUNT=0" >> ${WORKSPACE}/build_vars.env
- else
- FIX_COUNT=$(echo "$FIX_COMMITS" | wc -l)
- log_message "找到 ${FIX_COUNT} 个修复"
- echo "FIX_COUNT=${FIX_COUNT}" >> ${WORKSPACE}/build_vars.env
- fi
- # 发送构建开始通知
- send_build_start_notification \
- "${PROJECT_NAME}" \
- "${BUILD_START_TIME}" \
- "${LAST_COMMIT_DATE}" \
- "${LAST_COMMIT_MESSAGE}" \
- "${BUILD_BRANCH}" \
- "${VITE_NODE_ENV}"
- update_progress 20 "代码拉取完成"
- '''
- }
- }
- }
- stage('更新子模块') {
- steps {
- script {
- sh '''
- source ${WORKSPACE}/jenkins/lib/logger.sh
- source ${WORKSPACE}/jenkins/lib/telegram.sh
- log_message "检查并更新 Git 子模块"
- if [ -f ".gitmodules" ]; then
- log_message "发现 .gitmodules,开始更新子模块"
- git submodule update --init --recursive
- log_message "子模块更新完成"
- else
- log_message "没有子模块,跳过"
- fi
- update_progress 30 "子模块更新完成"
- '''
- }
- }
- }
- stage('依赖安装') {
- steps {
- script {
- sh '''
- source ${WORKSPACE}/jenkins/lib/logger.sh
- source ${WORKSPACE}/jenkins/lib/telegram.sh
- source ${WORKSPACE}/jenkins/lib/time-utils.sh
- source ${WORKSPACE}/build_vars.env
- export PATH="${NODE_PATH}:$PATH"
- log_message "Node 版本: $(node -v)"
- log_message "pnpm 版本: $(${PNPM_PATH} -v)"
- log_message "清理旧的 node_modules"
- rm -rf node_modules
- log_message "开始安装依赖"
- INSTALL_START=$(date +%s)
- if [ -f pnpm-lock.yaml ]; then
- log_message "使用 pnpm install --frozen-lockfile"
- if ! ${PNPM_PATH} install --frozen-lockfile 2>&1 | tee -a "$BUILD_LOG_FILE"; then
- log_message "frozen-lockfile 失败,尝试普通安装"
- ${PNPM_PATH} install 2>&1 | tee -a "$BUILD_LOG_FILE"
- fi
- else
- ${PNPM_PATH} install 2>&1 | tee -a "$BUILD_LOG_FILE"
- fi
- INSTALL_END=$(date +%s)
- INSTALL_DURATION=$((INSTALL_END - INSTALL_START))
- log_message "依赖安装完成,耗时: ${INSTALL_DURATION}秒"
- update_progress 40 "依赖安装完成"
- '''
- }
- }
- }
- stage('生成配置文件') {
- steps {
- script {
- sh '''
- source ${WORKSPACE}/jenkins/lib/logger.sh
- source ${WORKSPACE}/jenkins/lib/telegram.sh
- log_message "检查环境配置文件"
- if [ -f ".env.production" ]; then
- log_message "✅ 找到 .env.production"
- log_message "配置内容:"
- cat .env.production | grep -v "^#" | grep -v "^$" | tee -a "$BUILD_LOG_FILE"
- else
- log_message "⚠️ 未找到 .env.production,使用默认配置"
- fi
- if [ -f ".env" ]; then
- log_message "✅ 找到 .env (基础配置)"
- fi
- log_message "配置文件检查完成"
- update_progress 55 "配置文件检查完成"
- '''
- }
- }
- }
- stage('项目构建') {
- steps {
- script {
- sh '''
- source ${WORKSPACE}/jenkins/lib/logger.sh
- source ${WORKSPACE}/jenkins/lib/telegram.sh
- source ${WORKSPACE}/jenkins/lib/time-utils.sh
- source ${WORKSPACE}/build_vars.env
- export PATH="${NODE_PATH}:$PATH"
- log_message "开始执行构建"
- if ! ${PNPM_PATH} run build 2>&1 | tee -a "$BUILD_LOG_FILE"; then
- log_message "错误: build 失败"
- FAIL_TIME=$(get_network_time)
- FAIL_TIMESTAMP=$(get_timestamp "$FAIL_TIME")
- eval $(calculate_duration ${BUILD_START_TIMESTAMP} ${FAIL_TIMESTAMP})
- send_build_failure_notification \
- "${PROJECT_NAME}" \
- "${FAIL_TIME}" \
- "${LAST_COMMIT_DATE}" \
- "${LAST_COMMIT_MESSAGE}" \
- "${LAST_COMMIT_HASH}" \
- "${BUILD_BRANCH}" \
- "${VITE_NODE_ENV}" \
- "build" \
- "构建命令执行失败" \
- "${MINUTES}" \
- "${SECONDS}"
- send_telegram_file "$BUILD_LOG_FILE" "❌ 构建失败 - 完整日志"
- exit 1
- fi
- update_progress 70 "构建完成"
- '''
- }
- }
- }
- stage('Cloudflare 部署') {
- steps {
- script {
- withCredentials([
- string(credentialsId: 'CLOUDFLARE_API_TOKEN', variable: 'CLOUDFLARE_API_TOKEN'),
- string(credentialsId: 'CLOUDFLARE_ACCOUNT_ID', variable: 'CLOUDFLARE_ACCOUNT_ID')
- ]) {
- sh '''
- source ${WORKSPACE}/jenkins/lib/lark_dev.sh
- source ${WORKSPACE}/jenkins/lib/logger.sh
- source ${WORKSPACE}/jenkins/lib/telegram.sh
- source ${WORKSPACE}/jenkins/lib/time-utils.sh
- source ${WORKSPACE}/jenkins/lib/cloudflare_sync.sh
- source ${WORKSPACE}/build_vars.env
- export PATH="${NODE_PATH}:$PATH"
- export CLOUDFLARE_API_TOKEN="${CLOUDFLARE_API_TOKEN}"
- if ! deploy_to_cloudflare "${CLOUDFLARE_PROJECT_NAME}" "${BUILD_BRANCH}" "dist"; then
- log_message "❌ Cloudflare Pages 部署失败"
- FAIL_TIME=$(get_network_time)
- FAIL_TIMESTAMP=$(get_timestamp "$FAIL_TIME")
- eval $(calculate_duration ${BUILD_START_TIMESTAMP} ${FAIL_TIMESTAMP})
- send_build_failure_notification \
- "${PROJECT_NAME}" \
- "${FAIL_TIME}" \
- "${LAST_COMMIT_DATE}" \
- "${LAST_COMMIT_MESSAGE}" \
- "${LAST_COMMIT_HASH}" \
- "${BUILD_BRANCH}" \
- "${VITE_NODE_ENV}" \
- "Cloudflare 部署" \
- "Cloudflare Pages 部署失败" \
- "${MINUTES}" \
- "${SECONDS}"
- send_telegram_file "$BUILD_LOG_FILE" "❌ 构建失败 - 完整日志"
- exit 1
- fi
- log_message "发送部署完成通知"
- PACKAGE_VERSION=$(node -p "require('./package.json').version" 2>/dev/null || echo "未知")
- log_message "项目版本: ${PACKAGE_VERSION}"
- send_lark_message \
- "🌐 Cloudflare 部署完成 - ${PROJECT_NAME}" \
- "**访问地址**: [${DEPLOY_URL}](${DEPLOY_URL})
- **版本**: ${PACKAGE_VERSION}
- **分支**: ${BUILD_BRANCH}
- **环境**: ${VITE_NODE_ENV}
- **提交**: ${LAST_COMMIT_HASH}
- **部署时间**: $(get_network_time)"
- log_message "✅ 部署完成: ${DEPLOY_URL}"
- update_progress 100 "部署完成"
- '''
- }
- }
- }
- }
- }
- post {
- success {
- script {
- sh '''
- source ${WORKSPACE}/jenkins/lib/logger.sh
- source ${WORKSPACE}/jenkins/lib/telegram.sh
- source ${WORKSPACE}/jenkins/lib/lark_dev.sh
- source ${WORKSPACE}/jenkins/lib/time-utils.sh
- source ${WORKSPACE}/build_vars.env
- eval $(get_log_stats)
- log_message "Warning数量: ${WARNING_COUNT}"
- log_message "Error数量: ${ERROR_COUNT}"
- BUILD_END_TIME=$(get_network_time)
- BUILD_END_TIMESTAMP=$(get_timestamp "$BUILD_END_TIME")
- eval $(calculate_duration ${BUILD_START_TIMESTAMP} ${BUILD_END_TIMESTAMP})
- log_message "=============== 构建完成 ==============="
- log_message "总耗时: ${MINUTES}分${SECONDS}秒"
- send_build_success_notification \
- "${PROJECT_NAME}" \
- "${BUILD_END_TIME}" \
- "${LAST_COMMIT_DATE}" \
- "${LAST_COMMIT_MESSAGE}" \
- "${LAST_COMMIT_HASH}" \
- "${BUILD_BRANCH}" \
- "${VITE_NODE_ENV}" \
- "${MINUTES}" \
- "${SECONDS}"
- send_telegram_file "$BUILD_LOG_FILE" "✅ 构建成功 - 完整日志"
- if [ "${FIX_COUNT:-0}" -gt 0 ]; then
- log_message "发送修复通知到 Lark"
- FORMATTED_FIXES=$(echo -e "${FIX_LIST}")
- send_lark_fix_notification \
- "${PROJECT_NAME}" \
- "${BUILD_END_TIME}" \
- "${LAST_COMMIT_HASH}" \
- "${FIX_COUNT}" \
- "${FORMATTED_FIXES}" \
- "${BUILD_BRANCH}" \
- "${VITE_NODE_ENV}"
- fi
- echo "${LAST_COMMIT_HASH}" > ${WORKSPACE}/.last_success_commit
- log_message "保存成功构建标记: ${LAST_COMMIT_HASH}"
- '''
- }
- }
- failure {
- script {
- sh '''
- source ${WORKSPACE}/jenkins/lib/logger.sh
- source ${WORKSPACE}/jenkins/lib/telegram.sh
- log_message "构建失败,请查看日志"
- if [ -f "${WORKSPACE}/build_vars.env" ]; then
- source ${WORKSPACE}/build_vars.env
- send_telegram_message "❌ 构建失败 - ${PROJECT_NAME} - 请查看 Jenkins 日志"
- fi
- '''
- }
- }
- always {
- script {
- archiveArtifacts artifacts: 'build_*.log', allowEmptyArchive: true
- }
- }
- }
- }
|