diff --git a/apps/test/android/.kotlin/sessions/kotlin-compiler-16397378050838386911.salive b/apps/test/android/.kotlin/sessions/kotlin-compiler-16397378050838386911.salive deleted file mode 100644 index e69de29..0000000 diff --git a/tool/Jenkinsfile b/tool/Jenkinsfile new file mode 100644 index 0000000..3befd68 --- /dev/null +++ b/tool/Jenkinsfile @@ -0,0 +1,397 @@ +// 统一Jenkins构建配置 (Melos Monorepo版本) +// 同时支持开发和生产环境 + +pipeline { + agent any + + options { + // 超时时间 + timeout(time: 45, unit: 'MINUTES') + // 保留构建历史 + buildDiscarder(logRotator(numToKeepStr: '20')) + // 时间戳显示 + timestamps() + // 彩色输出 + ansiColor('xterm') + } + + environment { + // 基础配置 + PROJECT_NAME = 'Learning Officer OA' + APP_NAME = 'LearningOfficerOA' + FVM_VERSION = '3.38.9' + + // 构建模式 (始终使用 release 获得最佳性能) + BUILD_MODE = 'release' + + // 动态环境配置 (根据参数设置) + ENVIRONMENT = "${params.BUILD_ENVIRONMENT}" + + // 归档配置 + RELEASE_DIR = "${env.HOME}/release/oa/${params.BUILD_ENVIRONMENT}" + + // Melos Monorepo 配置 + APP_DIR = 'apps/oa_app' + + // Fastlane和Flutter配置 + LC_ALL = 'en_US.UTF-8' + LANG = 'en_US.UTF-8' + CI = 'true' + FLUTTER_SUPPRESS_ANALYTICS = 'true' + FLUTTER_NO_ANALYTICS = 'true' + + // PATH配置(使用环境变量,避免硬编码用户路径) + PATH = "${env.HOME ?: '/Users/yuanxuan'}/.pub-cache/bin:/opt/homebrew/bin:/usr/local/bin:${env.PATH}" + + // 签名配置 + KEYCHAIN_PATH = "${HOME}/Library/Keychains/login.keychain-db" + KEYCHAIN_PASSWORD = "${KEYCHAIN_PASSWORD}" + + // 版本信息 + APP_VERSION = '' + BUILD_NUMBER = '' + MAIN_APK_FOUND = 'false' + } + + parameters { + // 🎯 环境选择 (核心参数) + choice( + name: 'BUILD_ENVIRONMENT', + choices: ['development', 'preview', 'production'], + description: '🏗️ 构建环境: development(开发测试) | preview(预发布) | production(正式发布)' + ) + + // 📱 平台选择 + choice( + name: 'PLATFORM', + choices: ['android', 'ios', 'all'], + description: '📱 构建平台: android | ios | all' + ) + + // 🧹 清理构建 + booleanParam( + name: 'CLEAN_BUILD', + defaultValue: true, + description: '🧹 是否执行清理构建 (生产环境推荐开启)' + ) + + // 🍎 iOS配置 (开发/预发布环境) + booleanParam( + name: 'UPLOAD_TESTFLIGHT', + defaultValue: true, + description: '🚀 是否自动上传iOS到TestFlight (仅开发/预发布环境有效)' + ) + + // 🍎 iOS分发类型 (生产环境) + choice( + name: 'IOS_DISTRIBUTION_TYPE', + choices: ['testflight', 'adhoc', 'appstore'], + description: '🍎 iOS分发类型: adhoc(企业分发) | testflight(外部测试) | appstore(应用商店)' + ) + } + + stages { + stage('🔧 环境初始化') { + steps { + script { + echo "🚀 开始构建: ${PROJECT_NAME} [${params.BUILD_ENVIRONMENT}]" + } + + sh ''' + #!/bin/zsh + set +x + # 静默加载环境 + [ -f "$HOME/.zshrc" ] && source "$HOME/.zshrc" > /dev/null 2>&1 + + # 仅输出核心版本信息 + if command -v fvm &> /dev/null; then + echo " - Flutter: $(fvm flutter --version | head -1)" + echo " - Melos: $(melos --version 2>/dev/null || echo '本地依赖')" + else + echo "❌ 错误: 未找到FVM环境" + exit 1 + fi + ''' + } + } + + stage('📥 依赖管理') { + steps { + script { + echo "📦 正在同步 Monorepo 依赖 (Bootstrap)..." + sh ''' + #!/bin/zsh + set +x + [ -f "$HOME/.zshrc" ] && source "$HOME/.zshrc" > /dev/null 2>&1 + + # 使用 melos bootstrap 初始化所有包 + melos bootstrap + echo " ✅ 依赖同步完成" + ''' + } + } + } + + stage('📝 版本与生成') { + steps { + script { + sh ''' + #!/bin/zsh + set +x + [ -f "$HOME/.zshrc" ] && source "$HOME/.zshrc" > /dev/null 2>&1 + + # 运行版本管理 (在 app 目录) + # 运行版本管理 (从根目录运行 Dart 脚本,避免子目录缺少依赖) + if [ -f "scripts/version_manager.dart" ]; then + # 确保根目录依赖已准备好 (通常在 Bootstrap 阶段完成,但在某些 CI 环境可能需要显式 pub get) + # dart pub get # 如果需要,取消注释 + + eval $(dart scripts/version_manager.dart ci-clean --path ${APP_DIR}/pubspec.yaml) + echo " - 目标版本: ${APP_FULL_VERSION}" + fi + + # 代码生成 (使用 melos run generate 覆盖所有包) + echo " - 正在为所有包生成代码..." + melos run generate + echo " ✅ 代码生成完成" + ''' + } + } + } + + stage('🏗️ 编译构建') { + steps { + script { + if (params.PLATFORM == 'android' || params.PLATFORM == 'all') { + echo "🤖 正在构建 Android APK..." + sh ''' + #!/bin/zsh + set +x + [ -f "$HOME/.zshrc" ] && source "$HOME/.zshrc" > /dev/null 2>&1 + cd ${APP_DIR} + if [ "${CLEAN_BUILD}" = "true" ]; then + echo " - 执行 flutter clean..." + fvm flutter clean + fi + fvm flutter build apk --release --dart-define=APP_ENV=${ENVIRONMENT} + ''' + echo " ✅ Android 构建成功" + } + + if (params.PLATFORM == 'ios' || params.PLATFORM == 'all') { + echo "🍎 正在构建 iOS IPA (via Melos)..." + sh ''' + #!/bin/zsh + set -e + [ -f "$HOME/.zshrc" ] && source "$HOME/.zshrc" > /dev/null 2>&1 + + cd ${APP_DIR} + if [ "${CLEAN_BUILD}" = "true" ]; then + echo " - 执行 flutter clean..." + fvm flutter clean + fi + + # clean 后必须重新拉依赖,确保插件注册文件 (.dart_tool/flutter_build) 完整 + echo " - 恢复 Flutter 依赖..." + fvm flutter pub get + + # 统一更新 CocoaPods 仓库并按 Podfile 安装依赖 + echo " - CocoaPods: repo update + install..." + retry=0 + until [ $retry -ge 3 ] + do + if (cd ios && pod install --repo-update); then + echo " ✅ CocoaPods 依赖已就绪" + break + fi + retry=$((retry+1)) + echo " ⚠️ pod install 失败,重试 ${retry}/3..." + sleep 3 + done + if [ $retry -ge 3 ]; then + echo "❌ pod install 连续失败,停止构建" + exit 1 + fi + + cd - + # Melos 脚本会自动进入 apps/oa_app 目录并执行,通过 -- 传递额外参数 + melos run build:ipa -- --dart-define=APP_ENV=${ENVIRONMENT} + ''' + echo " ✅ iOS 构建成功" + } + } + } + } + + stage('🚀 上传 TestFlight') { + when { + allOf { + anyOf { + expression { params.BUILD_ENVIRONMENT == 'development' } + expression { params.BUILD_ENVIRONMENT == 'preview' } + } + expression { params.UPLOAD_TESTFLIGHT == true } + anyOf { + expression { params.PLATFORM == 'ios' } + expression { params.PLATFORM == 'all' } + } + } + } + steps { + script { + echo "📤 准备上传到 TestFlight..." + sh ''' + #!/bin/zsh + set +x + [ -f "$HOME/.zshrc" ] && source "$HOME/.zshrc" > /dev/null 2>&1 + + # 清理环境变量引号 + export FASTLANE_APP_STORE_CONNECT_API_KEY_PATH=$(echo "${FASTLANE_APP_STORE_CONNECT_API_KEY_PATH}" | tr -d '"' | tr -d "'") + export APP_STORE_CONNECT_API_KEY_KEY_ID=$(echo "${APP_STORE_CONNECT_API_KEY_KEY_ID}" | tr -d '"' | tr -d "'") + export APP_STORE_CONNECT_API_KEY_ISSUER_ID=$(echo "${APP_STORE_CONNECT_API_KEY_ISSUER_ID}" | tr -d '"' | tr -d "'") + + # 获取绝对路径的 IPA 文件 + ABS_IPA_PATH=$(ls -t $(pwd)/${APP_DIR}/build/ios/ipa/*.ipa 2>/dev/null | head -1) + if [ -z "$ABS_IPA_PATH" ]; then echo "❌ 未找到IPA"; exit 1; fi + + echo " - 产物: $(basename $ABS_IPA_PATH)" + + if [ -f "fastlane/Gemfile" ]; then + cd fastlane + bundle install --quiet + + echo " - 正在上传 (应用 Fastfile 内部补丁)..." + + # 1. 清理环境,依赖 Fastfile 顶部的 require 解决兼容性 + unset RUBYOPT + + # 2. 设置 Fastlane 官方环境变量 + export SPACESHIP_CONNECT_API_KEY_ID="$APP_STORE_CONNECT_API_KEY_KEY_ID" + export SPACESHIP_CONNECT_API_ISSUER_ID="$APP_STORE_CONNECT_API_KEY_ISSUER_ID" + export SPACESHIP_CONNECT_API_KEY_PATH="$FASTLANE_APP_STORE_CONNECT_API_KEY_PATH" + + # 3. 执行上传 (使用绝对路径) + bundle exec fastlane upload_to_testflight_with_key ipa:"$ABS_IPA_PATH" + fi + echo " ✅ TestFlight 上传完成" + ''' + } + } + } + } + + post { + always { + script { + echo "🧹 处理构建产物和清理" + + // 根据环境归档不同的构建产物 + echo "📦 归档${params.BUILD_ENVIRONMENT}环境构建产物..." + + // 归档 APK (仅限 Android 平台) + if ((params.PLATFORM == 'android' || params.PLATFORM == 'all') && fileExists('apps/oa_app/build/app/outputs/flutter-apk/app-release.apk')) { + echo " - 归档Android Release APK..." + archiveArtifacts artifacts: 'apps/oa_app/build/app/outputs/flutter-apk/app-release.apk', fingerprint: true + echo " ✅ Android Release APK已归档" + } + + // 归档 AAB (仅限 Android 平台) + if ((params.PLATFORM == 'android' || params.PLATFORM == 'all') && fileExists('apps/oa_app/build/app/outputs/bundle/release/app-release.aab')) { + echo " - 归档Android AAB..." + archiveArtifacts artifacts: 'apps/oa_app/build/app/outputs/bundle/release/app-release.aab', fingerprint: true + echo " ✅ Android AAB已归档" + } + + // 归档iOS IPA (仅限 iOS 平台) + if (params.PLATFORM == 'ios' || params.PLATFORM == 'all') { + def ipaFileCount = sh( + script: "find apps/oa_app/build/ios/ipa -name '*.ipa' 2>/dev/null | wc -l | tr -d ' '", + returnStdout: true + ).trim() + if (ipaFileCount && ipaFileCount != '0') { + echo " - 归档iOS IPA..." + archiveArtifacts artifacts: 'apps/oa_app/build/ios/ipa/*.ipa', fingerprint: true + echo " ✅ iOS IPA已归档 (找到 ${ipaFileCount} 个文件)" + } else { + echo " ⚠️ 未找到iOS IPA文件" + } + } + + // 归档FVM配置 + if (fileExists('.fvmrc')) { + archiveArtifacts artifacts: '.fvmrc', fingerprint: true, allowEmptyArchive: true + echo " ✅ FVM配置已归档" + } + } + } + + success { + script { + echo "🎉 ${params.BUILD_ENVIRONMENT}环境构建成功!" + echo "" + echo "✅ 构建总结:" + echo " - 项目: ${PROJECT_NAME}" + echo " - 应用: ${APP_NAME}" + echo " - 环境: ${params.BUILD_ENVIRONMENT}" + echo " - 平台: ${params.PLATFORM}" + echo " - 项目类型: Melos Monorepo" + echo " - Flutter版本: ${FVM_VERSION}" + echo " - 归档位置: ${RELEASE_DIR}" + echo "" + + if (params.BUILD_ENVIRONMENT == 'production') { + echo "🏭 生产环境特性:" + echo " - Release APK (生产签名,可发布)" + echo " - iOS ${params.IOS_DISTRIBUTION_TYPE} IPA" + echo " - AAB (Google Play Store)" + echo "" + echo "⚠️ 重要提醒:" + echo " - 生产包仅用于正式发布" + echo " - 发布前请进行充分测试" + echo " - 确认版本号和更新说明" + } else if (params.BUILD_ENVIRONMENT == 'preview') { + echo "🔍 预发布环境特性:" + echo " - Release APK (优化版本,连接预发布库)" + echo " - iOS TestFlight版本" + if (params.UPLOAD_TESTFLIGHT) { + echo " - 已自动上传到TestFlight" + } + echo "" + echo "🚀 下一步:" + echo " - 供测试团队/产品验收" + } else { + echo "🛠️ 开发环境特性:" + echo " - Release APK (优化版本,用于测试)" + echo " - iOS TestFlight版本" + if (params.UPLOAD_TESTFLIGHT) { + echo " - 已自动上传到TestFlight" + } + echo "" + echo "🚀 下一步:" + echo " - 下载APK进行内部测试" + echo " - TestFlight查看iOS测试版本" + } + } + } + + failure { + script { + echo "❌ ${params.BUILD_ENVIRONMENT}环境构建失败" + echo "" + echo "🔍 故障排除建议:" + echo " 1. 检查Flutter环境配置" + echo " 2. 验证Melos Monorepo依赖配置" + if (params.BUILD_ENVIRONMENT == 'production') { + echo " 3. 验证iOS Distribution/Ad-hoc证书" + echo " 4. 确认Android Release签名配置" + echo " 5. 检查生产环境权限和密钥" + } else { + echo " 3. 验证App Store Connect API密钥配置" + echo " 4. 确认TestFlight上传权限" + echo " 5. 检查开发/预发布证书配置" + } + echo " 6. 查看详细构建日志" + } + } + } +}