|
|
@@ -1,98 +1,569 @@
|
|
|
-// build-android.js 位于 .build/ 目录下
|
|
|
-const fs = require('fs');
|
|
|
+// build-android.js - 统一Android SDK安装和构建脚本
|
|
|
+const fs = require('fs');
|
|
|
const path = require('path');
|
|
|
+const https = require('https');
|
|
|
+const http = require('http');
|
|
|
const { execSync } = require('child_process');
|
|
|
+const { URL } = require('url');
|
|
|
+const os = require('os');
|
|
|
const { rmSync } = require('fs');
|
|
|
-const { exec } = require('cordova');
|
|
|
+
|
|
|
+// 配置 - 跨平台设置
|
|
|
+const isWindows = process.platform === 'win32';
|
|
|
+const isMac = process.platform === 'darwin';
|
|
|
+
|
|
|
+// 根据平台选择相应的 ZIP URL
|
|
|
+const platformZipMap = {
|
|
|
+ win32: 'commandlinetools-win-11076708_latest.zip',
|
|
|
+ linux: 'commandlinetools-linux-11076708_latest.zip',
|
|
|
+ darwin: 'commandlinetools-mac-11076708_latest.zip'
|
|
|
+};
|
|
|
+
|
|
|
+// 按优先级确定 SDK 安装路径:环境变量 > 平台默认路径
|
|
|
+const defaultSdkRoot = isWindows
|
|
|
+ ? path.join(process.env.APPDATA, 'Android', 'Sdk')
|
|
|
+ : path.join(os.homedir(), 'android-sdk');
|
|
|
+
|
|
|
+const SDK_ROOT = process.env.ANDROID_SDK_ROOT || process.env.ANDROID_HOME || defaultSdkRoot;
|
|
|
+const SDK_URL = `https://dl.google.com/android/repository/${platformZipMap[process.platform] || platformZipMap.linux}`;
|
|
|
+
|
|
|
+const CMDLINE_TOOLS_DIR = path.join(SDK_ROOT, 'cmdline-tools', 'latest');
|
|
|
+const TEMP_ZIP = path.join(process.env.TEMP || (isWindows ? process.env.TMP : '/tmp'), 'cmdtools.zip');
|
|
|
+const VERSION_FILE = path.join(SDK_ROOT, '.sdk-version');
|
|
|
+const EXPECTED_VERSION = 'platforms-35-buildtools-35.0.0';
|
|
|
+
|
|
|
+// 从环境变量获取代理设置
|
|
|
+const PROXY = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
|
|
|
|
|
|
// 读取 package.json 获取版本号
|
|
|
const packageJson = require(path.join(__dirname, '..', 'package.json'));
|
|
|
|
|
|
// 计算各路径
|
|
|
-const rootDir = path.join(__dirname, '..'); // 项目根目录
|
|
|
+const rootDir = path.join(__dirname, '..'); // 项目根目录
|
|
|
const cordovaPrjDir = path.join(__dirname, 'dros'); // .build/dros
|
|
|
const srcDir = path.join(rootDir, 'dist', 'h5'); // ../dist/h5
|
|
|
const dstDir = path.join(cordovaPrjDir, 'www');
|
|
|
|
|
|
-rmSync('.build/dros', { recursive: true, force: true });
|
|
|
-// 做一些环境检查
|
|
|
-execSync('npx cordova --version', { stdio: 'inherit' });
|
|
|
+console.log('🚀 Android SDK 安装和构建统一脚本启动...');
|
|
|
+console.log(`📦 SDK Root: ${SDK_ROOT}`);
|
|
|
+if (PROXY) {
|
|
|
+ console.log(`🌐 使用代理: ${PROXY}`);
|
|
|
+}
|
|
|
+
|
|
|
+// 检查 SDK 是否已安装且版本正确
|
|
|
+function isSdkInstalled() {
|
|
|
+ console.log('🔍 检测 Android SDK 安装状态...');
|
|
|
+
|
|
|
+ const sdkManagerExe = isWindows ? 'sdkmanager.bat' : 'sdkmanager';
|
|
|
+ const sdkManagerPath = path.join(CMDLINE_TOOLS_DIR, 'bin', sdkManagerExe);
|
|
|
+ const platformPath = path.join(SDK_ROOT, 'platforms', 'android-35');
|
|
|
+ const buildToolsPath = path.join(SDK_ROOT, 'build-tools', '35.0.0');
|
|
|
+
|
|
|
+ // 检查关键文件是否存在
|
|
|
+ console.log(' 📂 检查 sdkmanager...');
|
|
|
+ if (!fs.existsSync(sdkManagerPath)) {
|
|
|
+ console.log(' ❌ sdkmanager 不存在');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ console.log(' ✅ sdkmanager 存在');
|
|
|
+
|
|
|
+ console.log(' 📂 检查 platforms;android-35...');
|
|
|
+ if (!fs.existsSync(platformPath)) {
|
|
|
+ console.log(' ❌ platforms;android-35 不存在');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ console.log(' ✅ platforms;android-35 存在');
|
|
|
+
|
|
|
+ console.log(' 📂 检查 build-tools;35.0.0...');
|
|
|
+ if (!fs.existsSync(buildToolsPath)) {
|
|
|
+ console.log(' ❌ build-tools;35.0.0 不存在');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ console.log(' ✅ build-tools;35.0.0 存在');
|
|
|
+
|
|
|
+ // 验证版本标记文件
|
|
|
+ console.log(' 📝 检查版本标记...');
|
|
|
+ if (fs.existsSync(VERSION_FILE)) {
|
|
|
+ const installedVersion = fs.readFileSync(VERSION_FILE, 'utf8').trim();
|
|
|
+ console.log(` 已安装版本: ${installedVersion}`);
|
|
|
+ console.log(` 期望版本: ${EXPECTED_VERSION}`);
|
|
|
+
|
|
|
+ if (installedVersion === EXPECTED_VERSION) {
|
|
|
+ console.log(' ✅ 版本匹配');
|
|
|
+ return true;
|
|
|
+ } else {
|
|
|
+ console.log(' ⚠️ 版本不匹配,需要重新安装');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ console.log(' ⚠️ 版本标记文件不存在');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 下载文件(支持代理)
|
|
|
+function downloadFile(url, dest) {
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ console.log(`📥 下载: ${url}`);
|
|
|
+
|
|
|
+ const file = fs.createWriteStream(dest);
|
|
|
+ const urlObj = new URL(url);
|
|
|
+
|
|
|
+ let requestLib = https;
|
|
|
+ let options = {
|
|
|
+ hostname: urlObj.hostname,
|
|
|
+ path: urlObj.pathname + urlObj.search,
|
|
|
+ method: 'GET',
|
|
|
+ headers: {
|
|
|
+ 'User-Agent': 'Node.js'
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 如果有代理,使用代理设置
|
|
|
+ if (PROXY) {
|
|
|
+ const proxyUrl = new URL(PROXY);
|
|
|
+ requestLib = proxyUrl.protocol === 'https:' ? https : http;
|
|
|
+ options = {
|
|
|
+ hostname: proxyUrl.hostname,
|
|
|
+ port: proxyUrl.port,
|
|
|
+ path: url, // 使用完整 URL 作为路径
|
|
|
+ method: 'GET',
|
|
|
+ headers: {
|
|
|
+ 'Host': urlObj.hostname,
|
|
|
+ 'User-Agent': 'Node.js'
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ const request = requestLib.get(options, (response) => {
|
|
|
+ // 处理重定向
|
|
|
+ if (response.statusCode === 301 || response.statusCode === 302) {
|
|
|
+ const redirectUrl = response.headers.location;
|
|
|
+ console.log(`🔀 重定向到: ${redirectUrl}`);
|
|
|
+ file.close();
|
|
|
+ fs.unlinkSync(dest);
|
|
|
+ return downloadFile(redirectUrl, dest).then(resolve).catch(reject);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (response.statusCode !== 200) {
|
|
|
+ reject(new Error(`下载失败,状态码: ${response.statusCode}`));
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const totalSize = parseInt(response.headers['content-length'], 10);
|
|
|
+ let downloadedSize = 0;
|
|
|
+
|
|
|
+ response.on('data', (chunk) => {
|
|
|
+ downloadedSize += chunk.length;
|
|
|
+ const percent = ((downloadedSize / totalSize) * 100).toFixed(2);
|
|
|
+ process.stdout.write(`\r下载进度: ${percent}% (${downloadedSize}/${totalSize} bytes)`);
|
|
|
+ });
|
|
|
+
|
|
|
+ response.pipe(file);
|
|
|
+
|
|
|
+ file.on('finish', () => {
|
|
|
+ file.close();
|
|
|
+ console.log('\n✅ 下载完成');
|
|
|
+ resolve();
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ request.on('error', (err) => {
|
|
|
+ fs.unlinkSync(dest);
|
|
|
+ reject(err);
|
|
|
+ });
|
|
|
+
|
|
|
+ file.on('error', (err) => {
|
|
|
+ fs.unlinkSync(dest);
|
|
|
+ reject(err);
|
|
|
+ });
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+// 解压 ZIP 文件
|
|
|
+function extractZip(zipPath, destDir) {
|
|
|
+ console.log(`📂 解压到: ${destDir}`);
|
|
|
+
|
|
|
+ // 确保目标目录存在
|
|
|
+ fs.mkdirSync(destDir, { recursive: true });
|
|
|
+
|
|
|
+ // 在 Windows 上使用 PowerShell 的 Expand-Archive
|
|
|
+ if (process.platform === 'win32') {
|
|
|
+ try {
|
|
|
+ const cmd = `powershell -Command "Expand-Archive -Path '${zipPath}' -DestinationPath '${destDir}' -Force"`;
|
|
|
+ execSync(cmd, { stdio: 'inherit' });
|
|
|
+ console.log('✅ 解压完成');
|
|
|
+ } catch (error) {
|
|
|
+ throw new Error(`解压失败: ${error.message}`);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // Linux 使用 unzip
|
|
|
+ try {
|
|
|
+ execSync(`unzip -q "${zipPath}" -d "${destDir}"`, { stdio: 'inherit' });
|
|
|
+ console.log('✅ 解压完成');
|
|
|
+ } catch (error) {
|
|
|
+ throw new Error(`解压失败: ${error.message}`);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
-// 1. 在项目根目录执行 cordova create
|
|
|
-execSync('npx cordova create .build/dros zskk.dros dros', { cwd: rootDir, stdio: 'inherit' });
|
|
|
+// 设置环境变量(写入 GITHUB_ENV)
|
|
|
+function setGitHubEnv(name, value) {
|
|
|
+ const envFile = process.env.GITHUB_ENV;
|
|
|
+ if (envFile) {
|
|
|
+ fs.appendFileSync(envFile, `${name}=${value}\n`);
|
|
|
+ console.log(`✅ 设置环境变量: ${name}=${value}`);
|
|
|
+ } else {
|
|
|
+ console.log(`⚠️ GITHUB_ENV 未定义,跳过设置: ${name}=${value}`);
|
|
|
+ }
|
|
|
+}
|
|
|
|
|
|
-// 2. 在 .build/dros 中执行 cordova platform add android
|
|
|
-execSync('npx cordova platform add android', { cwd: cordovaPrjDir, stdio: 'inherit' });
|
|
|
+// 安装 Gradle 到 PATH
|
|
|
+async function installGradleToPath() {
|
|
|
+ const gradleVersion = '9.0.0'; // 使用稳定版本,可根据需要更新
|
|
|
+ const gradleUrl = `https://mirrors.cloud.tencent.com/gradle/gradle-${gradleVersion}-bin.zip`;
|
|
|
+ const gradleInstallDir = isWindows
|
|
|
+ ? path.join(process.env.USERPROFILE, 'gradle')
|
|
|
+ : path.join(os.homedir(), 'gradle');
|
|
|
+ const gradleBin = path.join(gradleInstallDir, `gradle-${gradleVersion}`, 'bin');
|
|
|
|
|
|
-// 3. 复制 dist/h5 → .build/dros/www
|
|
|
-fs.rmSync(dstDir, { recursive: true, force: true });
|
|
|
-fs.mkdirSync(dstDir, { recursive: true });
|
|
|
+ // 检查 Gradle 是否已存在
|
|
|
+ const gradleExe = isWindows ? 'gradle.bat' : 'gradle';
|
|
|
+ const fullGradleExe = path.join(gradleBin, gradleExe);
|
|
|
|
|
|
-function copy(src, dst) {
|
|
|
- const stat = fs.statSync(src);
|
|
|
- if (stat.isDirectory()) {
|
|
|
- fs.mkdirSync(dst, { recursive: true });
|
|
|
- for (const entry of fs.readdirSync(src)) {
|
|
|
- copy(path.join(src, entry), path.join(dst, entry));
|
|
|
+ if (fs.existsSync(fullGradleExe)) {
|
|
|
+ console.log('✅ Gradle 已安装');
|
|
|
+
|
|
|
+ // 添加到 PATH(即使已安装也要确保在PATH中)
|
|
|
+ const pathEnvFile = process.env.GITHUB_PATH;
|
|
|
+ if (pathEnvFile) {
|
|
|
+ fs.appendFileSync(pathEnvFile, `${gradleBin}\n`);
|
|
|
+ console.log(`✅ Gradle 已添加到 PATH: ${gradleBin}`);
|
|
|
}
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`📦 下载 Gradle ${gradleVersion}...`);
|
|
|
+ await downloadFile(gradleUrl, TEMP_ZIP);
|
|
|
+
|
|
|
+ console.log(`📂 解压 Gradle 到: ${gradleInstallDir}`);
|
|
|
+ extractZip(TEMP_ZIP, gradleInstallDir);
|
|
|
+
|
|
|
+ // 添加到 PATH
|
|
|
+ const pathEnvFile = process.env.GITHUB_PATH;
|
|
|
+ if (pathEnvFile) {
|
|
|
+ fs.appendFileSync(pathEnvFile, `${gradleBin}\n`);
|
|
|
+ console.log(`✅ Gradle 已添加到 PATH: ${gradleBin}`);
|
|
|
} else {
|
|
|
- fs.copyFileSync(src, dst);
|
|
|
+ console.log(`⚠️ 请手动将 ${gradleBin} 添加到 PATH`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 测试 Gradle
|
|
|
+ try {
|
|
|
+ execSync(`"${fullGradleExe}" --version`, { stdio: 'pipe' });
|
|
|
+ console.log('✅ Gradle 安装成功');
|
|
|
+ } catch (error) {
|
|
|
+ console.warn('⚠️ Gradle 测试失败:', error.message);
|
|
|
}
|
|
|
}
|
|
|
-copy(srcDir, dstDir);
|
|
|
-// 4. 安装白名单插件
|
|
|
-// execSync('npx cordova plugin add cordova-plugin-whitelist', { cwd: cordovaPrjDir, stdio: 'inherit' });
|
|
|
-// 5. 复制预配置好的config.xml
|
|
|
-fs.copyFileSync(path.join(__dirname, 'config.xml'), path.join(cordovaPrjDir, 'config.xml'));
|
|
|
-// 6. 在 .build/dros 中执行 cordova build android
|
|
|
-execSync('npx cordova build android --verbose', { cwd: cordovaPrjDir, stdio: 'inherit' });
|
|
|
-// 7. 重命名apk文件
|
|
|
-const version = packageJson.version;
|
|
|
-const pkg = 'dros';
|
|
|
-const src = path.join(__dirname, 'dros/platforms/android/app/build/outputs/apk/debug/app-debug.apk');
|
|
|
-const dst = path.join(__dirname, `dros/platforms/android/app/build/outputs/apk/debug/${pkg}-v${version}.apk`);
|
|
|
-
|
|
|
-fs.copyFileSync(src, dst);
|
|
|
-console.log(`✅ 已生成 ${dst}`);
|
|
|
-
|
|
|
-// 安装到连接的安卓设备(CI 环境跳过)
|
|
|
-if (process.env.CI !== 'true') {
|
|
|
+
|
|
|
+// 运行 sdkmanager
|
|
|
+function runSdkManager(args) {
|
|
|
+ const sdkManagerExe = isWindows ? 'sdkmanager.bat' : 'sdkmanager';
|
|
|
+ const sdkManagerPath = path.join(CMDLINE_TOOLS_DIR, 'bin', sdkManagerExe);
|
|
|
+
|
|
|
+ if (!fs.existsSync(sdkManagerPath)) {
|
|
|
+ throw new Error(`sdkmanager 不存在: ${sdkManagerPath}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`🔧 运行: sdkmanager ${args.join(' ')}`);
|
|
|
+
|
|
|
try {
|
|
|
- execSync(`adb install "${dst}"`, { stdio: 'inherit' });
|
|
|
- console.log(`✅ 已安装到安卓设备 ${dst}`);
|
|
|
+ // 分离包名和参数
|
|
|
+ const packages = args.filter(arg => !arg.startsWith('--'));
|
|
|
+ const flags = args.filter(arg => arg.startsWith('--'));
|
|
|
+
|
|
|
+ // 只对包名加引号,参数不加引号
|
|
|
+ const quotedPackages = packages.map(pkg => `"${pkg}"`).join(' ');
|
|
|
+ const cmd = `"${sdkManagerPath}" ${quotedPackages} ${flags.join(' ')}`;
|
|
|
+
|
|
|
+ console.log(`执行命令: ${cmd}`);
|
|
|
+
|
|
|
+ // 根据平台设置不同的 shell 选项
|
|
|
+ const execOptions = isWindows
|
|
|
+ ? { stdio: 'inherit', shell: 'cmd.exe' }
|
|
|
+ : { stdio: 'inherit' };
|
|
|
+
|
|
|
+ execSync(cmd, execOptions);
|
|
|
+ console.log('✅ sdkmanager 执行成功');
|
|
|
} catch (error) {
|
|
|
- console.warn('⚠️ ADB 安装失败,可能没有连接设备:', error.message);
|
|
|
+ throw new Error(`sdkmanager 执行失败: ${error.message}`);
|
|
|
}
|
|
|
-} else {
|
|
|
- console.log('ℹ️ CI 环境,跳过 ADB 安装步骤');
|
|
|
}
|
|
|
|
|
|
-// 8. 部署到服务器(可选)
|
|
|
-(async () => {
|
|
|
- if (process.env.DEPLOY_ANDROID === 'true') {
|
|
|
- console.log('\n📤 开始部署到服务器...');
|
|
|
- console.log('='.repeat(50));
|
|
|
-
|
|
|
+// 安装和配置 Android SDK
|
|
|
+async function setupAndroidSdk() {
|
|
|
+ console.log('🔧 检查 Gradle 安装...');
|
|
|
+ await installGradleToPath();
|
|
|
+
|
|
|
+ // 0. 检查 SDK 是否已安装
|
|
|
+ if (isSdkInstalled()) {
|
|
|
+ console.log('\n✅ Android SDK 已安装且版本正确,跳过安装流程');
|
|
|
+ console.log(`📦 SDK Root: ${SDK_ROOT}`);
|
|
|
+
|
|
|
+ // 仍然需要设置环境变量(GitHub Actions 每次运行都需要)
|
|
|
+ console.log('\n⚙️ 设置环境变量...');
|
|
|
+ setGitHubEnv('ANDROID_HOME', SDK_ROOT);
|
|
|
+ setGitHubEnv('ANDROID_SDK_ROOT', SDK_ROOT);
|
|
|
+
|
|
|
+ console.log('\n🎉 Android SDK 环境变量设置完成!');
|
|
|
+ console.log('⏱️ 耗时: ~1-2秒(本地缓存加速)');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('\n📦 未检测到有效的 Android SDK,开始完整安装流程...');
|
|
|
+
|
|
|
+ // 1. 下载 SDK
|
|
|
+ await downloadFile(SDK_URL, TEMP_ZIP);
|
|
|
+
|
|
|
+ // 2. 解压到临时目录
|
|
|
+ const tempExtractDir = path.join(SDK_ROOT, 'cmdline-tools', 'temp');
|
|
|
+ extractZip(TEMP_ZIP, tempExtractDir);
|
|
|
+
|
|
|
+ // 3. 移动内部的 cmdline-tools 到 latest(ZIP 内部包含 cmdline-tools 目录)
|
|
|
+ const extractedCmdlineTools = path.join(tempExtractDir, 'cmdline-tools');
|
|
|
+ if (fs.existsSync(extractedCmdlineTools)) {
|
|
|
+ console.log('📦 重新组织目录结构...');
|
|
|
+ // 如果 latest 目录已存在,先删除
|
|
|
+ if (fs.existsSync(CMDLINE_TOOLS_DIR)) {
|
|
|
+ fs.rmSync(CMDLINE_TOOLS_DIR, { recursive: true, force: true });
|
|
|
+ }
|
|
|
+ fs.renameSync(extractedCmdlineTools, CMDLINE_TOOLS_DIR);
|
|
|
+ // 删除临时目录
|
|
|
+ fs.rmSync(tempExtractDir, { recursive: true, force: true });
|
|
|
+ console.log('✅ 目录结构已调整');
|
|
|
+ } else {
|
|
|
+ throw new Error(`解压后未找到 cmdline-tools 目录: ${extractedCmdlineTools}`);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 设置环境变量
|
|
|
+ setGitHubEnv('ANDROID_HOME', SDK_ROOT);
|
|
|
+ setGitHubEnv('ANDROID_SDK_ROOT', SDK_ROOT);
|
|
|
+
|
|
|
+ // 5. 先接受 licenses(使用输入重定向文件)
|
|
|
+ console.log('📝 接受 SDK licenses...');
|
|
|
+ const sdkManagerExe = isWindows ? 'sdkmanager.bat' : 'sdkmanager';
|
|
|
+ const sdkManagerPath = path.join(CMDLINE_TOOLS_DIR, 'bin', sdkManagerExe);
|
|
|
+
|
|
|
+ // 创建包含多个 y 的临时文件(每个 y 一行)
|
|
|
+ const tempInputFileDir = isWindows ? process.env.TEMP || process.env.TMP : '/tmp';
|
|
|
+ const tempInputFile = path.join(tempInputFileDir, 'sdk-licenses-input.txt');
|
|
|
+ // 根据日志显示有 7 个许可证,我们用 20 个 y 确保足够
|
|
|
+ fs.writeFileSync(tempInputFile, 'y\n'.repeat(20));
|
|
|
+
|
|
|
+ // 使用输入重定向 < 而不是管道 |
|
|
|
+ const licenseCmd = `"${sdkManagerPath}" --licenses --sdk_root=${SDK_ROOT} < "${tempInputFile}"`;
|
|
|
+
|
|
|
+ // 根据平台设置不同的 shell 选项
|
|
|
+ const licenseExecOptions = isWindows
|
|
|
+ ? { stdio: 'inherit', shell: 'cmd.exe' }
|
|
|
+ : { stdio: 'inherit' };
|
|
|
+
|
|
|
+ execSync(licenseCmd, licenseExecOptions);
|
|
|
+
|
|
|
+ console.log('✅ licenses 接受完成');
|
|
|
+
|
|
|
+ // 清理临时文件
|
|
|
+ if (fs.existsSync(tempInputFile)) {
|
|
|
+ fs.unlinkSync(tempInputFile);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 然后安装 platforms 和 build-tools
|
|
|
+ console.log('📦 安装 Android SDK 组件...');
|
|
|
+ runSdkManager([
|
|
|
+ 'platforms;android-35',
|
|
|
+ 'build-tools;35.0.0',
|
|
|
+ `--sdk_root=${SDK_ROOT}`
|
|
|
+ ]);
|
|
|
+
|
|
|
+ console.log('🎉 Android SDK 安装完成!');
|
|
|
+
|
|
|
+ // 7. 创建版本标记文件
|
|
|
+ console.log('📝 创建版本标记文件...');
|
|
|
+ fs.writeFileSync(VERSION_FILE, EXPECTED_VERSION, 'utf8');
|
|
|
+ console.log(`✅ 版本标记已创建: ${EXPECTED_VERSION}`);
|
|
|
+
|
|
|
+ // 清理临时文件
|
|
|
+ if (fs.existsSync(TEMP_ZIP)) {
|
|
|
+ fs.unlinkSync(TEMP_ZIP);
|
|
|
+ console.log('🧹 已清理临时文件');
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 配置 Gradle Wrapper 使用我们的 Gradle 版本
|
|
|
+function configureGradleWrapper() {
|
|
|
+ const gradleWrapperPropsPath = path.join(cordovaPrjDir, 'platforms', 'android', 'gradle', 'wrapper', 'gradle-wrapper.properties');
|
|
|
+
|
|
|
+ if (!fs.existsSync(gradleWrapperPropsPath)) {
|
|
|
+ console.log('⚠️ Gradle wrapper 配置文件不存在,跳过配置');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log('🔧 配置 Gradle wrapper 使用 Gradle 9.0.0...');
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 读取现有的配置文件
|
|
|
+ let content = fs.readFileSync(gradleWrapperPropsPath, 'utf8');
|
|
|
+
|
|
|
+ // 替换 distributionUrl 为我们的 Gradle 版本
|
|
|
+ const oldUrlPattern = /distributionUrl=.*/;
|
|
|
+ const newUrl = 'distributionUrl=https\\://services.gradle.org/distributions/gradle-9.0.0-bin.zip';
|
|
|
+
|
|
|
+ if (oldUrlPattern.test(content)) {
|
|
|
+ content = content.replace(oldUrlPattern, newUrl);
|
|
|
+ fs.writeFileSync(gradleWrapperPropsPath, content, 'utf8');
|
|
|
+ console.log('✅ Gradle wrapper 已配置为使用 Gradle 9.0.0');
|
|
|
+ } else {
|
|
|
+ console.log('⚠️ 未找到 distributionUrl 配置,添加新配置');
|
|
|
+ content += `\n${newUrl}\n`;
|
|
|
+ fs.writeFileSync(gradleWrapperPropsPath, content, 'utf8');
|
|
|
+ console.log('✅ 已添加 Gradle 9.0.0 配置');
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.warn('⚠️ 配置 Gradle wrapper 失败:', error.message);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 配置 Android 构建环境变量
|
|
|
+function getAndroidEnv() {
|
|
|
+ // 计算 Gradle bin 路径
|
|
|
+ const gradleVersion = '9.0.0';
|
|
|
+ const gradleBin = isWindows
|
|
|
+ ? path.join(process.env.USERPROFILE || '', 'gradle', `gradle-${gradleVersion}`, 'bin')
|
|
|
+ : path.join(process.env.HOME || '/root', 'gradle', `gradle-${gradleVersion}`, 'bin');
|
|
|
+
|
|
|
+ return {
|
|
|
+ ...process.env,
|
|
|
+ // 使用配置的SDK路径
|
|
|
+ ANDROID_HOME: SDK_ROOT,
|
|
|
+ ANDROID_SDK_ROOT: SDK_ROOT,
|
|
|
+ // 确保 Java 路径正确
|
|
|
+ JAVA_HOME: process.env.JAVA_HOME,
|
|
|
+ // 将 Gradle 添加到 PATH 前缀,确保 Cordova 子进程能找到它
|
|
|
+ PATH: `${gradleBin}${path.delimiter}${process.env.PATH}`
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+// 构建 Android APK
|
|
|
+function buildAndroidApk() {
|
|
|
+ const androidEnv = getAndroidEnv();
|
|
|
+
|
|
|
+ rmSync('.build/dros', { recursive: true, force: true });
|
|
|
+ // 做一些环境检查
|
|
|
+ execSync('npx cordova --version', { stdio: 'inherit', env: androidEnv });
|
|
|
+
|
|
|
+ // 1. 在项目根目录执行 cordova create
|
|
|
+ execSync('npx cordova create .build/dros zskk.dros dros', { cwd: rootDir, stdio: 'inherit', env: androidEnv });
|
|
|
+
|
|
|
+ // 2. 在 .build/dros 中执行 cordova platform add android
|
|
|
+ execSync('npx cordova platform add android', { cwd: cordovaPrjDir, stdio: 'inherit', env: androidEnv });
|
|
|
+
|
|
|
+ // 2.5. 配置 Gradle wrapper 使用我们的 Gradle 版本
|
|
|
+ configureGradleWrapper();
|
|
|
+
|
|
|
+ // 3. 复制 dist/h5 → .build/dros/www
|
|
|
+ fs.rmSync(dstDir, { recursive: true, force: true });
|
|
|
+ fs.mkdirSync(dstDir, { recursive: true });
|
|
|
+
|
|
|
+ function copy(src, dst) {
|
|
|
+ const stat = fs.statSync(src);
|
|
|
+ if (stat.isDirectory()) {
|
|
|
+ fs.mkdirSync(dst, { recursive: true });
|
|
|
+ for (const entry of fs.readdirSync(src)) {
|
|
|
+ copy(path.join(src, entry), path.join(dst, entry));
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ fs.copyFileSync(src, dst);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ copy(srcDir, dstDir);
|
|
|
+
|
|
|
+ // 4. 安装白名单插件
|
|
|
+ // execSync('npx cordova plugin add cordova-plugin-whitelist', { cwd: cordovaPrjDir, stdio: 'inherit' });
|
|
|
+
|
|
|
+ // 5. 复制预配置好的config.xml
|
|
|
+ fs.copyFileSync(path.join(__dirname, 'config.xml'), path.join(cordovaPrjDir, 'config.xml'));
|
|
|
+
|
|
|
+ // 6. 在 .build/dros 中执行 cordova build android
|
|
|
+ execSync('npx cordova build android --verbose', { cwd: cordovaPrjDir, stdio: 'inherit', env: androidEnv });
|
|
|
+
|
|
|
+ // 7. 重命名apk文件
|
|
|
+ const version = packageJson.version;
|
|
|
+ const pkg = 'dros';
|
|
|
+ const src = path.join(__dirname, 'dros/platforms/android/app/build/outputs/apk/debug/app-debug.apk');
|
|
|
+ const dst = path.join(__dirname, `dros/platforms/android/app/build/outputs/apk/debug/${pkg}-v${version}.apk`);
|
|
|
+
|
|
|
+ fs.copyFileSync(src, dst);
|
|
|
+ console.log(`✅ 已生成 ${dst}`);
|
|
|
+
|
|
|
+ // 安装到连接的安卓设备(CI 环境跳过)
|
|
|
+ if (process.env.CI !== 'true') {
|
|
|
try {
|
|
|
- const { deployAndroidToServer } = require('./deploy-android-to-server');
|
|
|
-
|
|
|
- await deployAndroidToServer({
|
|
|
- privateKey: process.env.DEPLOY_KEY,
|
|
|
- host: process.env.DEPLOY_HOST,
|
|
|
- username: process.env.DEPLOY_USER,
|
|
|
- remotePath: process.env.DEPLOY_PATH,
|
|
|
- apkPath: dst,
|
|
|
- appVersion: version
|
|
|
- });
|
|
|
-
|
|
|
- console.log('='.repeat(50));
|
|
|
- console.log('✅ 部署完成!');
|
|
|
+ execSync(`adb install "${dst}"`, { stdio: 'inherit', env: androidEnv });
|
|
|
+ console.log(`✅ 已安装到安卓设备 ${dst}`);
|
|
|
} catch (error) {
|
|
|
- console.error('='.repeat(50));
|
|
|
- console.error('❌ 部署失败:', error.message);
|
|
|
- process.exit(1);
|
|
|
+ console.warn('⚠️ ADB 安装失败,可能没有连接设备:', error.message);
|
|
|
}
|
|
|
} else {
|
|
|
- console.log('\n💡 提示: 设置 DEPLOY_ANDROID=true 可自动部署到服务器');
|
|
|
+ console.log('ℹ️ CI 环境,跳过 ADB 安装步骤');
|
|
|
}
|
|
|
-})();
|
|
|
+
|
|
|
+ // 8. 部署到服务器(可选)
|
|
|
+ (async () => {
|
|
|
+ if (process.env.DEPLOY_ANDROID === 'true') {
|
|
|
+ console.log('\n📤 开始部署到服务器...');
|
|
|
+ console.log('='.repeat(50));
|
|
|
+
|
|
|
+ try {
|
|
|
+ const { deployAndroidToServer } = require('./deploy-android-to-server');
|
|
|
+
|
|
|
+ await deployAndroidToServer({
|
|
|
+ privateKey: process.env.DEPLOY_KEY,
|
|
|
+ host: process.env.DEPLOY_HOST,
|
|
|
+ username: process.env.DEPLOY_USER,
|
|
|
+ remotePath: process.env.DEPLOY_PATH,
|
|
|
+ apkPath: dst,
|
|
|
+ appVersion: version
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log('='.repeat(50));
|
|
|
+ console.log('✅ 部署完成!');
|
|
|
+ } catch (error) {
|
|
|
+ console.error('='.repeat(50));
|
|
|
+ console.error('❌ 部署失败:', error.message);
|
|
|
+ process.exit(1);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ console.log('\n💡 提示: 设置 DEPLOY_ANDROID=true 可自动部署到服务器');
|
|
|
+ }
|
|
|
+ })();
|
|
|
+}
|
|
|
+
|
|
|
+// 主函数
|
|
|
+async function main() {
|
|
|
+ try {
|
|
|
+ // 1. 安装和配置 Android SDK
|
|
|
+ await setupAndroidSdk();
|
|
|
+
|
|
|
+ // 2. 构建 Android APK
|
|
|
+ console.log('\n🏗️ 开始构建 Android APK...');
|
|
|
+ buildAndroidApk();
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('❌ 错误:', error.message);
|
|
|
+ process.exit(1);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 运行
|
|
|
+main();
|