|
|
|
|
|
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')}';
|
|
|
|
|
|
}
|