123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- // setup-android-sdk.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 SDK_URL = 'https://dl.google.com/android/repository/commandlinetools-win-11076708_latest.zip';
- const SDK_ROOT = 'C:\\android-sdk';
- const CMDLINE_TOOLS_DIR = path.join(SDK_ROOT, 'cmdline-tools', 'latest');
- const TEMP_ZIP = path.join(process.env.TEMP || '/tmp', 'cmdtools.zip');
- // 从环境变量获取代理设置
- const PROXY = process.env.HTTPS_PROXY || process.env.HTTP_PROXY;
- console.log('🚀 开始安装 Android SDK...');
- console.log(`📦 SDK Root: ${SDK_ROOT}`);
- if (PROXY) {
- console.log(`🌐 使用代理: ${PROXY}`);
- }
- // 下载文件(支持代理)
- 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}`);
- }
- }
- }
- // 设置环境变量(写入 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}`);
- }
- }
- // 运行 sdkmanager
- function runSdkManager(args) {
- const sdkManagerPath = path.join(CMDLINE_TOOLS_DIR, 'bin', 'sdkmanager.bat');
-
- if (!fs.existsSync(sdkManagerPath)) {
- throw new Error(`sdkmanager 不存在: ${sdkManagerPath}`);
- }
-
- console.log(`🔧 运行: sdkmanager ${args.join(' ')}`);
-
- try {
- // 构建完整的命令
- const cmd = `"${sdkManagerPath}" ${args.map(arg => `"${arg}"`).join(' ')}`;
- execSync(cmd, {
- stdio: 'inherit',
- shell: 'cmd.exe'
- });
- console.log('✅ sdkmanager 执行成功');
- } catch (error) {
- throw new Error(`sdkmanager 执行失败: ${error.message}`);
- }
- }
- // 主函数
- async function main() {
- try {
- // 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);
-
- // 4. 安装 platforms 和 build-tools
- runSdkManager([
- '--install',
- 'platforms;android-35',
- 'build-tools;35.0.0',
- `--sdk_root=${SDK_ROOT}`
- ]);
-
- // 5. 接受 licenses(使用 echo y)
- console.log('📝 接受 SDK licenses...');
- const sdkManagerPath = path.join(CMDLINE_TOOLS_DIR, 'bin', 'sdkmanager.bat');
- const licenseCmd = `echo y | "${sdkManagerPath}" "--licenses" "--sdk_root=${SDK_ROOT}"`;
- execSync(licenseCmd, {
- stdio: 'inherit',
- shell: 'cmd.exe'
- });
-
- console.log('🎉 Android SDK 安装完成!');
-
- // 清理临时文件
- if (fs.existsSync(TEMP_ZIP)) {
- fs.unlinkSync(TEMP_ZIP);
- console.log('🧹 已清理临时文件');
- }
-
- } catch (error) {
- console.error('❌ 错误:', error.message);
- process.exit(1);
- }
- }
- // 运行
- main();
|