|
|
|
|
@ -0,0 +1,149 @@
|
|
|
|
|
import 'dart:io'; |
|
|
|
|
import 'dart:convert'; |
|
|
|
|
import 'package:yaml/yaml.dart'; // 用于解析 pubspec.yaml |
|
|
|
|
import 'package:args/args.dart'; // 用于解析命令行参数 |
|
|
|
|
|
|
|
|
|
// --- 配置区 --- |
|
|
|
|
const String pubspecPath = 'pubspec.yaml'; |
|
|
|
|
const String baseApkPath = 'build/app/outputs/flutter-apk/'; |
|
|
|
|
const String sourceApkName = 'app-release.apk'; |
|
|
|
|
// --- |
|
|
|
|
|
|
|
|
|
Future<void> main(List<String> rawArgs) async { |
|
|
|
|
// 使用 args 包来专业地解析命令行参数 |
|
|
|
|
final parser = ArgParser() |
|
|
|
|
..addFlag('arm64-only', negatable: false, help: '只构建 ARM64 架构的 APK。') |
|
|
|
|
..addOption( |
|
|
|
|
'description', |
|
|
|
|
abbr: 'd', |
|
|
|
|
help: '版本更新的描述。', |
|
|
|
|
defaultsTo: '常规更新。', |
|
|
|
|
); |
|
|
|
|
final args = parser.parse(rawArgs); |
|
|
|
|
|
|
|
|
|
final isArm64Only = args['arm64-only'] as bool; |
|
|
|
|
final description = args['description'] as String; |
|
|
|
|
|
|
|
|
|
print('🚀 开始构建Flutter Android应用...'); |
|
|
|
|
|
|
|
|
|
final targetPlatform = isArm64Only |
|
|
|
|
? 'android-arm64' |
|
|
|
|
: 'android-arm64,android-arm'; |
|
|
|
|
final finalApkName = isArm64Only ? 'app-arm64-release.apk' : sourceApkName; |
|
|
|
|
final finalApkPath = '$baseApkPath$finalApkName'; |
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
await checkFlutterEnvironment(); |
|
|
|
|
await buildFlutterApp(targetPlatform, finalApkPath); |
|
|
|
|
await generateVersionJson(finalApkPath, targetPlatform, description); |
|
|
|
|
|
|
|
|
|
print('✅ 构建全部完成!'); |
|
|
|
|
print('📦 APK位置: $finalApkPath'); |
|
|
|
|
} catch (e) { |
|
|
|
|
print('❌ 构建过程中出现错误: $e'); |
|
|
|
|
exit(1); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Future<void> buildFlutterApp(String targetPlatform, String finalApkPath) async { |
|
|
|
|
print('📱 正在构建Flutter Android应用...'); |
|
|
|
|
print('🎯 目标平台: $targetPlatform'); |
|
|
|
|
|
|
|
|
|
await runProcess('flutter', ['clean']); |
|
|
|
|
await runProcess('flutter', ['pub', 'get']); |
|
|
|
|
|
|
|
|
|
print('🔨 构建Release APK...'); |
|
|
|
|
await runProcess('flutter', [ |
|
|
|
|
'build', |
|
|
|
|
'apk', |
|
|
|
|
'--release', |
|
|
|
|
'--target-platform=$targetPlatform', |
|
|
|
|
]); |
|
|
|
|
|
|
|
|
|
final sourceApkFile = File('$baseApkPath$sourceApkName'); |
|
|
|
|
if (await sourceApkFile.exists()) { |
|
|
|
|
print('✍️ 正在重命名APK为: ${finalApkPath.split('/').last}'); |
|
|
|
|
await sourceApkFile.rename(finalApkPath); |
|
|
|
|
} else { |
|
|
|
|
throw Exception('构建产物 $sourceApkName 未找到!'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
print('✅ Flutter应用构建成功!'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Future<void> generateVersionJson( |
|
|
|
|
String apkPath, |
|
|
|
|
String targetPlatform, |
|
|
|
|
String description, |
|
|
|
|
) async { |
|
|
|
|
print('📄 正在生成version.json文件...'); |
|
|
|
|
|
|
|
|
|
final apkFile = File(apkPath); |
|
|
|
|
String fileSize = '未知'; |
|
|
|
|
|
|
|
|
|
if (await apkFile.exists()) { |
|
|
|
|
final apkSize = await apkFile.length(); |
|
|
|
|
fileSize = '${(apkSize / (1024 * 1024)).toStringAsFixed(1)}MB'; |
|
|
|
|
print('📦 APK文件大小: $fileSize'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
final pubspecInfo = getPubspecInfo(); |
|
|
|
|
final buildTime = getFormattedDateTime(); |
|
|
|
|
|
|
|
|
|
final versionInfo = { |
|
|
|
|
"version": pubspecInfo['version_name'], |
|
|
|
|
"build_number": pubspecInfo['build_number'], |
|
|
|
|
"build_time": buildTime, |
|
|
|
|
"target_platform": targetPlatform, |
|
|
|
|
"file_size": fileSize, |
|
|
|
|
// [已修正] 从 apkPath 中提取文件名 |
|
|
|
|
"url": "http://xhota.anxincloud.cn/problem/${apkPath.split('/').last}", |
|
|
|
|
"description": description, |
|
|
|
|
"platform": "android", |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
final jsonFile = File('${baseApkPath}version.json'); |
|
|
|
|
await jsonFile.writeAsString( |
|
|
|
|
const JsonEncoder.withIndent(' ').convert(versionInfo), |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
print('✅ version.json文件生成成功!'); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// --- 辅助函数 --- |
|
|
|
|
|
|
|
|
|
Future<void> runProcess(String executable, List<String> arguments) async { |
|
|
|
|
print('⚙️ 执行命令: $executable ${arguments.join(' ')}'); |
|
|
|
|
final result = await Process.run(executable, arguments, runInShell: true); |
|
|
|
|
if (result.exitCode != 0) { |
|
|
|
|
print(result.stdout); |
|
|
|
|
print(result.stderr); |
|
|
|
|
throw Exception('命令执行失败: $executable ${arguments.join(' ')}'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Future<void> checkFlutterEnvironment() async { |
|
|
|
|
try { |
|
|
|
|
await runProcess('flutter', ['--version']); |
|
|
|
|
print('✅ Flutter环境正常'); |
|
|
|
|
} catch (e) { |
|
|
|
|
throw Exception('Flutter环境检查失败'); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Map<String, String> getPubspecInfo() { |
|
|
|
|
final file = File(pubspecPath); |
|
|
|
|
final content = file.readAsStringSync(); |
|
|
|
|
final doc = loadYaml(content); |
|
|
|
|
final versionString = doc['version'] as String; |
|
|
|
|
final parts = versionString.split('+'); |
|
|
|
|
return { |
|
|
|
|
'version_name': parts[0], |
|
|
|
|
'build_number': parts.length > 1 ? parts[1] : '1', |
|
|
|
|
}; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
String getFormattedDateTime() { |
|
|
|
|
final now = DateTime.now(); |
|
|
|
|
return '${now.year}-${now.month.toString().padLeft(2, '0')}-${now.day.toString().padLeft(2, '0')} ${now.hour.toString().padLeft(2, '0')}:${now.minute.toString().padLeft(2, '0')}'; |
|
|
|
|
} |