web_shell_flutter/Jenkinsfile

218 lines
8.7 KiB
Groovy
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 统一Jenkins构建配置 (单仓多App定制版)
// 专注 Android APK 构建,支持多应用与版本动态传参
pipeline {
agent any
options {
// 超时时间
timeout(time: 45, unit: 'MINUTES')
// 保留构建历史
buildDiscarder(logRotator(numToKeepStr: '20'))
// 时间戳显示
timestamps()
// 彩色输出
ansiColor('xterm')
}
environment {
// 基础配置
PROJECT_NAME = 'Web Android Shell'
// 此项目不使用 Melos但通过 dart run tool/generate_app.dart 生成 App 工程
FVM_VERSION = '3.41.2'
// 动态环境配置 (根据参数设置)
ENVIRONMENT = "${params.BUILD_ENVIRONMENT}"
// 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}"
}
parameters {
// 🎯 目标应用 (可由 tool/update_jenkins_apps.dart 动态更新内容)
choice(
name: 'APP_NAME',
choices: ['aixue', 'test', 'yunxiao', 'all'],
description: '📱 选择要打包的应用 (从 flavors 目录生成)'
)
// 🎯 环境选择
choice(
name: 'BUILD_ENVIRONMENT',
choices: ['development', 'preview', 'production'],
description: '🏗️ 构建环境: development(开发测试) | preview(预发布) | production(正式发布)'
)
// 🎯 版本相关参数
string(
name: 'VERSION_NAME',
defaultValue: '',
description: '🏷️ 覆盖版本名称 (例如 1.0.0)。留空则使用默认配置中的版本名。'
)
string(
name: 'BUILD_NUMBER',
defaultValue: '',
description: '🔢 覆盖构建编号 (例如 1)。留空则使用默认配置中的构建版本号。'
)
// 🧹 清理构建
booleanParam(
name: 'CLEAN_BUILD',
defaultValue: true,
description: '🧹 是否执行清理构建 (生产环境推荐开启)'
)
}
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)"
else
echo "❌ 错误: 未找到FVM环境"
exit 1
fi
'''
}
}
stage('🏗️ 生成与构建') {
steps {
script {
def appsToBuild = []
if (params.APP_NAME == 'all') {
// 动态读取 flavors 目录下的所有 yaml 文件
def flavorFiles = sh(script: "ls flavors/*.yaml | awk -F'/' '{print \$2}' | awk -F'.' '{print \$1}'", returnStdout: true).trim().split('\n')
appsToBuild = flavorFiles.toList()
} else {
appsToBuild = [params.APP_NAME]
}
for (int i = 0; i < appsToBuild.size(); i++) {
def currentApp = appsToBuild[i]
if (!currentApp) continue
echo "🤖 开始处理应用: ${currentApp}..."
// 步骤1生成 App 工程 (如果不存在)
sh """
#!/bin/zsh
set +x
[ -f "\\$HOME/.zshrc" ] && source "\\$HOME/.zshrc" > /dev/null 2>&1
if [ ! -d "apps/${currentApp}" ]; then
echo " - 应用目录 apps/${currentApp} 不存在,正在执行 generate_app 脚本生成项目..."
dart run tool/generate_app.dart ${currentApp}
else
echo " - 应用目录 apps/${currentApp} 已存在,跳过生成工程阶段。"
fi
"""
// 步骤2执行 Flutter 构建
echo " - 正在为 ${currentApp} 构建 Android APK..."
sh """
#!/bin/zsh
set +x
[ -f "\\$HOME/.zshrc" ] && source "\\$HOME/.zshrc" > /dev/null 2>&1
cd apps/${currentApp}
# 先写入 shell 变量,避免 GString 里出现 "\${params.xxx}" 触发 Groovy 解析错误
CLEAN_BUILD='${params.CLEAN_BUILD}'
VERSION_NAME='${params.VERSION_NAME}'
BUILD_NUMBER='${params.BUILD_NUMBER}'
BUILD_ENV='${params.BUILD_ENVIRONMENT}'
if [ "\$CLEAN_BUILD" = "true" ]; then
echo " - 执行 flutter clean..."
fvm flutter clean
fi
fvm flutter pub get
EXTRA_ARGS=""
if [ -n "\$VERSION_NAME" ]; then
EXTRA_ARGS="\$EXTRA_ARGS --build-name=\$VERSION_NAME"
fi
if [ -n "\$BUILD_NUMBER" ]; then
EXTRA_ARGS="\$EXTRA_ARGS --build-number=\$BUILD_NUMBER"
fi
EXTRA_ARGS="\$EXTRA_ARGS --dart-define=APP_ENV=\$BUILD_ENV"
echo " - 开始构建 APK 参数: \$EXTRA_ARGS"
echo " - 开始执行 flutter build apk构建期间每30秒输出一次心跳..."
(fvm flutter build apk --release \$EXTRA_ARGS) &
BUILD_PID=\$!
while kill -0 \$BUILD_PID 2>/dev/null; do
echo " - 构建仍在进行中... \$(date '+%H:%M:%S')"
sleep 30
done
wait \$BUILD_PID
"""
echo " ✅ ${currentApp} Android APK 构建完成"
}
}
}
}
}
post {
always {
script {
echo "🧹 处理构建产物"
// 仅归档 APK 产物
def findCount = sh(
script: "find apps/*/build/app/outputs/flutter-apk -name 'app-release.apk' 2>/dev/null | wc -l | tr -d ' '",
returnStdout: true
).trim()
if (findCount && findCount != '0') {
echo " - 归档Android Release APK..."
archiveArtifacts artifacts: 'apps/*/build/app/outputs/flutter-apk/app-release.apk', fingerprint: true
echo " ✅ 归档完成 (找到 ${findCount} 个 APK 文件)"
} else {
echo " ⚠️ 未找到任何构建的 APK"
}
// 归档FVM配置便于追踪环境记录
if (fileExists('.fvmrc')) {
archiveArtifacts artifacts: '.fvmrc', fingerprint: true, allowEmptyArchive: true
}
}
}
success {
script {
echo "🎉 构建流程成功完成!"
echo "✅ 详情:"
echo " - 目标: ${params.APP_NAME}"
echo " - 环境: ${params.BUILD_ENVIRONMENT}"
if (params.VERSION_NAME) echo " - 版本名: ${params.VERSION_NAME}"
if (params.BUILD_NUMBER) echo " - 构建号: ${params.BUILD_NUMBER}"
}
}
failure {
echo "❌ 构建流程遇到错误并终止。请检查上方日志找出失败原由。"
}
}
}