diff --git a/.gitignore b/.gitignore index 87c437b..b9042c4 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,4 @@ go.mod .RData .Rhistory program +.env diff --git a/package.json b/package.json index 71932d2..f9cec88 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@tauri-apps/cli": "^2", "@types/lodash-es": "^4.17.12", "@vitejs/plugin-vue": "^5.2.1", + "ali-oss": "^6.23.0", "autoprefixer": "^10.4.21", "postcss": "^8.5.6", "tailwindcss": "^4.1.11", diff --git a/scripts/sync-clojure-versions.js b/scripts/sync-clojure-versions.js new file mode 100755 index 0000000..e517aba --- /dev/null +++ b/scripts/sync-clojure-versions.js @@ -0,0 +1,342 @@ +#!/usr/bin/env node + +/** + * 同步 Clojure 版本到阿里云 OSS + * + * 功能: + * 1. 从 GitHub API 获取 Clojure 所有版本 + * 2. 下载版本文件 + * 3. 使用阿里云官方 ali-oss SDK 上传到 OSS /global/plugins/clojure/ 目录 + * 4. 生成 metadata.json 文件 + * + * 使用方法: + * node scripts/sync-clojure-versions.js + * + * 环境变量(可以在 .env 文件中配置): + * - OSS_REGION: 阿里云 OSS 区域 + * - OSS_ACCESS_KEY_ID: 阿里云访问密钥 ID + * - OSS_ACCESS_KEY_SECRET: 阿里云访问密钥 Secret + * - OSS_BUCKET: OSS Bucket 名称 + * - CDN_DOMAIN: 自定义 CDN 域名(可选) + */ + +import https from 'https'; +import http from 'http'; +import fs from 'fs'; +import path from 'path'; +import crypto from 'crypto'; +import { fileURLToPath } from 'url'; +import OSS from 'ali-oss'; + +// ES 模块中获取 __dirname +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// 加载 .env 文件 +function loadEnv() { + const envPath = path.join(__dirname, '..', '.env'); + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf8'); + let loadedCount = 0; + + envContent.split('\n').forEach(line => { + line = line.trim(); + // 跳过注释和空行 + if (!line || line.startsWith('#')) return; + + const match = line.match(/^([^=]+)=(.*)$/); + if (match) { + const key = match[1].trim(); + let value = match[2].trim(); + // 移除引号 + value = value.replace(/^["']|["']$/g, ''); + // 只在环境变量不存在时设置 + if (!process.env[key]) { + process.env[key] = value; + loadedCount++; + } + } + }); + + console.log(`✓ 已从 .env 文件加载 ${loadedCount} 个环境变量\n`); + } else { + console.log('⚠ 未找到 .env 文件,将使用环境变量或默认值\n'); + } +} + +// 获取配置(在 loadEnv 之后调用) +function getConfig() { + return { + ossRegion: process.env.OSS_REGION || 'oss-cn-hangzhou', + ossAccessKeyId: process.env.OSS_ACCESS_KEY_ID, + ossAccessKeySecret: process.env.OSS_ACCESS_KEY_SECRET, + ossBucket: process.env.OSS_BUCKET, + cdnDomain: process.env.CDN_DOMAIN, // 自定义 CDN 域名(可选) + githubRepo: 'clojure/brew-install', + ossPrefix: 'global/plugins/clojure/', + tempDir: path.join(__dirname, '.temp-clojure'), + // 支持的平台 + platforms: ['macos-aarch64', 'macos-x86_64', 'linux-aarch64', 'linux-x86_64', 'windows-x86_64'] + }; +} + +// 全局配置变量 +let CONFIG; + +// 验证配置 +function validateConfig() { + const missing = []; + if (!CONFIG.ossAccessKeyId) missing.push('OSS_ACCESS_KEY_ID'); + if (!CONFIG.ossAccessKeySecret) missing.push('OSS_ACCESS_KEY_SECRET'); + if (!CONFIG.ossBucket) missing.push('OSS_BUCKET'); + + if (missing.length > 0) { + console.error('错误: 请设置以下环境变量:'); + missing.forEach(key => console.error(` - ${key}`)); + console.error('\n提示: 可以在 .env 文件中配置这些变量'); + console.error('示例: cp .env.example .env'); + process.exit(1); + } + + // 显示配置信息(隐藏敏感信息) + console.log('配置信息:'); + console.log(` OSS Region: ${CONFIG.ossRegion}`); + console.log(` OSS Bucket: ${CONFIG.ossBucket}`); + console.log(` CDN Domain: ${CONFIG.cdnDomain || '未配置 (使用默认 OSS 域名)'}`); + console.log(''); +} + +// 创建临时目录 +function ensureTempDir() { + if (!fs.existsSync(CONFIG.tempDir)) { + fs.mkdirSync(CONFIG.tempDir, { recursive: true }); + } +} + +// 清理临时目录 +function cleanupTempDir() { + if (fs.existsSync(CONFIG.tempDir)) { + fs.rmSync(CONFIG.tempDir, { recursive: true, force: true }); + } +} + +// HTTP(S) GET 请求 +function httpGet(url, isJson = true) { + return new Promise((resolve, reject) => { + const client = url.startsWith('https') ? https : http; + const options = { + headers: { + 'User-Agent': 'CodeForge-Sync-Script' + } + }; + + client.get(url, options, (res) => { + if (res.statusCode === 302 || res.statusCode === 301) { + // 处理重定向 + return httpGet(res.headers.location, isJson).then(resolve).catch(reject); + } + + if (res.statusCode !== 200) { + reject(new Error(`HTTP ${res.statusCode}: ${url}`)); + return; + } + + const chunks = []; + res.on('data', chunk => chunks.push(chunk)); + res.on('end', () => { + const data = Buffer.concat(chunks); + if (isJson) { + try { + resolve(JSON.parse(data.toString())); + } catch (e) { + reject(new Error(`JSON 解析失败: ${e.message}`)); + } + } else { + resolve(data); + } + }); + }).on('error', reject); + }); +} + +// 下载文件 +async function downloadFile(url, destPath) { + console.log(` 下载: ${url}`); + const data = await httpGet(url, false); + fs.writeFileSync(destPath, data); + return destPath; +} + +// 获取 GitHub releases +async function getGitHubReleases() { + console.log('正在获取 Clojure 版本列表...'); + const url = `https://api.github.com/repos/${CONFIG.githubRepo}/releases?per_page=100`; + return await httpGet(url); +} + +// 计算文件 MD5 +function calculateMD5(filePath) { + const buffer = fs.readFileSync(filePath); + return crypto.createHash('md5').update(buffer).digest('hex'); +} + +// 获取文件大小 +function getFileSize(filePath) { + const stats = fs.statSync(filePath); + return stats.size; +} + +// 创建 OSS 客户端 +function createOSSClient() { + return new OSS({ + region: CONFIG.ossRegion, + accessKeyId: CONFIG.ossAccessKeyId, + accessKeySecret: CONFIG.ossAccessKeySecret, + bucket: CONFIG.ossBucket + }); +} + +// 上传文件到 OSS +async function uploadToOSS(client, localPath, ossPath) { + try { + await client.put(ossPath, localPath); + console.log(` ✓ 上传成功: ${ossPath}`); + } catch (error) { + throw new Error(`上传失败: ${error.message}`); + } +} + +// 上传 metadata.json 到 OSS +async function uploadMetadata(client, metadata) { + try { + const metadataJson = JSON.stringify(metadata, null, 2); + const buffer = Buffer.from(metadataJson, 'utf8'); + const ossPath = `${CONFIG.ossPrefix}metadata.json`; + + await client.put(ossPath, buffer); + console.log(`✓ metadata.json 上传成功`); + } catch (error) { + throw new Error(`上传 metadata 失败: ${error.message}`); + } +} + +// 主函数 +async function main() { + try { + console.log('=== Clojure 版本同步工具 ===\n'); + + // 加载 .env 文件 + loadEnv(); + + // 初始化配置 + CONFIG = getConfig(); + + // 验证配置 + validateConfig(); + + // 创建 OSS 客户端 + const ossClient = createOSSClient(); + + // 创建临时目录 + ensureTempDir(); + + // 获取 releases + const releases = await getGitHubReleases(); + console.log(`找到 ${releases.length} 个版本\n`); + + const metadata = { + language: 'clojure', + last_updated: new Date().toISOString(), + releases: [] + }; + + // 处理每个版本 + for (const release of releases) { + const version = release.tag_name; + console.log(`处理版本: ${version}`); + + // 查找 tar.gz 资源 + const asset = release.assets.find(a => + a.name.endsWith('.tar.gz') && a.name.includes('clojure-tools') + ); + + if (!asset) { + console.log(` ⚠ 跳过: 未找到 tar.gz 文件`); + continue; + } + + try { + // 下载文件 + const fileName = asset.name; + const localPath = path.join(CONFIG.tempDir, fileName); + await downloadFile(asset.browser_download_url, localPath); + + // 计算文件信息 + const fileSize = getFileSize(localPath); + const md5 = calculateMD5(localPath); + + // 上传到 OSS + const ossPath = `${CONFIG.ossPrefix}${fileName}`; + await uploadToOSS(ossClient, localPath, ossPath); + + // 添加到 metadata + // 使用自定义 CDN 域名或默认 OSS 域名 + const cdnUrl = CONFIG.cdnDomain + ? `${CONFIG.cdnDomain}/${ossPath}` + : `https://${CONFIG.ossBucket}.${CONFIG.ossRegion}.aliyuncs.com/${ossPath}`; + + metadata.releases.push({ + version: version.replace(/^v/, ''), + display_name: `Clojure ${version}`, + published_at: release.published_at, + download_url: cdnUrl, + github_url: asset.browser_download_url, + file_name: fileName, + size: fileSize, + md5: md5, + supported_platforms: CONFIG.platforms + }); + + console.log(` ✓ 版本 ${version} 处理完成\n`); + + // 删除本地文件 + fs.unlinkSync(localPath); + } catch (error) { + console.error(` ✗ 处理版本 ${version} 失败: ${error.message}\n`); + } + } + + // 按版本号排序(最新的在前) + metadata.releases.sort((a, b) => { + const versionA = a.version.split('.').map(Number); + const versionB = b.version.split('.').map(Number); + for (let i = 0; i < Math.max(versionA.length, versionB.length); i++) { + const numA = versionA[i] || 0; + const numB = versionB[i] || 0; + if (numA !== numB) return numB - numA; + } + return 0; + }); + + console.log('\n上传 metadata.json...'); + await uploadMetadata(ossClient, metadata); + + console.log(`\n✓ 同步完成!共处理 ${metadata.releases.length} 个版本`); + + // 输出 metadata URL + const metadataUrl = CONFIG.cdnDomain + ? `${CONFIG.cdnDomain}/${CONFIG.ossPrefix}metadata.json` + : `https://${CONFIG.ossBucket}.${CONFIG.ossRegion}.aliyuncs.com/${CONFIG.ossPrefix}metadata.json`; + console.log(`\nmetadata URL: ${metadataUrl}`); + + } catch (error) { + console.error('\n✗ 错误:', error.message); + process.exit(1); + } finally { + // 清理临时目录 + cleanupTempDir(); + } +} + +// 运行 +main(); diff --git a/scripts/sync-scala-versions.js b/scripts/sync-scala-versions.js new file mode 100755 index 0000000..9657e20 --- /dev/null +++ b/scripts/sync-scala-versions.js @@ -0,0 +1,346 @@ +#!/usr/bin/env node + +/** + * 同步 Scala 版本到阿里云 OSS + * + * 功能: + * 1. 从 GitHub API 获取 Scala 所有版本 + * 2. 下载版本文件 + * 3. 使用阿里云官方 ali-oss SDK 上传到 OSS /global/plugins/scala/ 目录 + * 4. 生成 metadata.json 文件 + * + * 使用方法: + * node scripts/sync-scala-versions.js + * + * 环境变量(可以在 .env 文件中配置): + * - OSS_REGION: 阿里云 OSS 区域 + * - OSS_ACCESS_KEY_ID: 阿里云访问密钥 ID + * - OSS_ACCESS_KEY_SECRET: 阿里云访问密钥 Secret + * - OSS_BUCKET: OSS Bucket 名称 + * - CDN_DOMAIN: 自定义 CDN 域名(可选) + */ + +import https from 'https'; +import http from 'http'; +import fs from 'fs'; +import path from 'path'; +import crypto from 'crypto'; +import { fileURLToPath } from 'url'; +import OSS from 'ali-oss'; + +// ES 模块中获取 __dirname +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// 加载 .env 文件 +function loadEnv() { + const envPath = path.join(__dirname, '..', '.env'); + if (fs.existsSync(envPath)) { + const envContent = fs.readFileSync(envPath, 'utf8'); + let loadedCount = 0; + + envContent.split('\n').forEach(line => { + line = line.trim(); + if (!line || line.startsWith('#')) return; + + const match = line.match(/^([^=]+)=(.*)$/); + if (match) { + const key = match[1].trim(); + let value = match[2].trim(); + value = value.replace(/^["']|["']$/g, ''); + if (!process.env[key]) { + process.env[key] = value; + loadedCount++; + } + } + }); + + console.log(`✓ 已从 .env 文件加载 ${loadedCount} 个环境变量\n`); + } else { + console.log('⚠ 未找到 .env 文件,将使用环境变量或默认值\n'); + } +} + +// 获取配置 +function getConfig() { + return { + ossRegion: process.env.OSS_REGION || 'oss-cn-hangzhou', + ossAccessKeyId: process.env.OSS_ACCESS_KEY_ID, + ossAccessKeySecret: process.env.OSS_ACCESS_KEY_SECRET, + ossBucket: process.env.OSS_BUCKET, + cdnDomain: process.env.CDN_DOMAIN, + githubRepo: 'lampepfl/dotty', + ossPrefix: 'global/plugins/scala/', + tempDir: path.join(__dirname, '.temp-scala'), + platformMap: { + 'aarch64-apple-darwin': 'macos-aarch64', + 'x86_64-apple-darwin': 'macos-x86_64', + 'aarch64-pc-linux': 'linux-aarch64', + 'x86_64-pc-linux': 'linux-x86_64', + 'x86_64-pc-win32': 'windows-x86_64' + } + }; +} + +let CONFIG; + +// 验证配置 +function validateConfig() { + const missing = []; + if (!CONFIG.ossAccessKeyId) missing.push('OSS_ACCESS_KEY_ID'); + if (!CONFIG.ossAccessKeySecret) missing.push('OSS_ACCESS_KEY_SECRET'); + if (!CONFIG.ossBucket) missing.push('OSS_BUCKET'); + + if (missing.length > 0) { + console.error('错误: 请设置以下环境变量:'); + missing.forEach(key => console.error(` - ${key}`)); + console.error('\n提示: 可以在 .env 文件中配置这些变量'); + console.error('示例: cp .env.example .env'); + process.exit(1); + } + + console.log('配置信息:'); + console.log(` OSS Region: ${CONFIG.ossRegion}`); + console.log(` OSS Bucket: ${CONFIG.ossBucket}`); + console.log(` CDN Domain: ${CONFIG.cdnDomain || '未配置 (使用默认 OSS 域名)'}`); + console.log(''); +} + +// 创建临时目录 +function ensureTempDir() { + if (!fs.existsSync(CONFIG.tempDir)) { + fs.mkdirSync(CONFIG.tempDir, { recursive: true }); + } +} + +// 清理临时目录 +function cleanupTempDir() { + if (fs.existsSync(CONFIG.tempDir)) { + fs.rmSync(CONFIG.tempDir, { recursive: true, force: true }); + } +} + +// HTTP(S) GET 请求 +function httpGet(url, isJson = true) { + return new Promise((resolve, reject) => { + const client = url.startsWith('https') ? https : http; + const options = { + headers: { + 'User-Agent': 'CodeForge-Sync-Script' + } + }; + + client.get(url, options, (res) => { + if (res.statusCode === 302 || res.statusCode === 301) { + return httpGet(res.headers.location, isJson).then(resolve).catch(reject); + } + + if (res.statusCode !== 200) { + reject(new Error(`HTTP ${res.statusCode}: ${url}`)); + return; + } + + const chunks = []; + res.on('data', chunk => chunks.push(chunk)); + res.on('end', () => { + const data = Buffer.concat(chunks); + if (isJson) { + try { + resolve(JSON.parse(data.toString())); + } catch (e) { + reject(new Error(`JSON 解析失败: ${e.message}`)); + } + } else { + resolve(data); + } + }); + }).on('error', reject); + }); +} + +// 下载文件 +async function downloadFile(url, destPath) { + console.log(` 下载: ${url}`); + const data = await httpGet(url, false); + fs.writeFileSync(destPath, data); + return destPath; +} + +// 获取 GitHub releases +async function getGitHubReleases() { + console.log('正在获取 Scala 版本列表...'); + const url = `https://api.github.com/repos/${CONFIG.githubRepo}/releases?per_page=10`; + return await httpGet(url); +} + +// 计算文件 MD5 +function calculateMD5(filePath) { + const buffer = fs.readFileSync(filePath); + return crypto.createHash('md5').update(buffer).digest('hex'); +} + +// 获取文件大小 +function getFileSize(filePath) { + const stats = fs.statSync(filePath); + return stats.size; +} + +// 创建 OSS 客户端 +function createOSSClient() { + return new OSS({ + region: CONFIG.ossRegion, + accessKeyId: CONFIG.ossAccessKeyId, + accessKeySecret: CONFIG.ossAccessKeySecret, + bucket: CONFIG.ossBucket + }); +} + +// 上传文件到 OSS +async function uploadToOSS(client, localPath, ossPath) { + try { + await client.put(ossPath, localPath); + console.log(` ✓ 上传成功: ${ossPath}`); + } catch (error) { + throw new Error(`上传失败: ${error.message}`); + } +} + +// 上传 metadata.json 到 OSS +async function uploadMetadata(client, metadata) { + try { + const metadataJson = JSON.stringify(metadata, null, 2); + const buffer = Buffer.from(metadataJson, 'utf8'); + const ossPath = `${CONFIG.ossPrefix}metadata.json`; + + await client.put(ossPath, buffer); + console.log(`✓ metadata.json 上传成功`); + } catch (error) { + throw new Error(`上传 metadata 失败: ${error.message}`); + } +} + +// 主函数 +async function main() { + try { + console.log('=== Scala 版本同步工具 ===\n'); + + loadEnv(); + + CONFIG = getConfig(); + + validateConfig(); + + const ossClient = createOSSClient(); + + ensureTempDir(); + + const releases = await getGitHubReleases(); + console.log(`找到 ${releases.length} 个版本\n`); + + const metadata = { + language: 'scala', + last_updated: new Date().toISOString(), + releases: [] + }; + + for (const release of releases) { + const version = release.tag_name; + console.log(`处理版本: ${version}`); + + const scalaAssets = release.assets.filter(a => + a.name.endsWith('.tar.gz') && a.name.includes('scala3') + ); + + if (scalaAssets.length === 0) { + console.log(` ⚠ 跳过: 未找到 tar.gz 文件`); + continue; + } + + const versionClean = version.replace(/^v/, ''); + const platformAssets = {}; + + for (const asset of scalaAssets) { + const platformMatch = asset.name.match(/scala3-[\d.]+-(?:RC\d+-)?([^.]+)\.tar\.gz/); + if (platformMatch) { + const githubPlatform = platformMatch[1]; + const mappedPlatform = CONFIG.platformMap[githubPlatform]; + if (mappedPlatform && !platformAssets[mappedPlatform]) { + platformAssets[mappedPlatform] = asset; + } + } + } + + let processedCount = 0; + + for (const [platform, asset] of Object.entries(platformAssets)) { + try { + const fileName = asset.name; + const localPath = path.join(CONFIG.tempDir, fileName); + await downloadFile(asset.browser_download_url, localPath); + + const fileSize = getFileSize(localPath); + const md5 = calculateMD5(localPath); + + const ossPath = `${CONFIG.ossPrefix}${versionClean}/${fileName}`; + await uploadToOSS(ossClient, localPath, ossPath); + + const cdnUrl = CONFIG.cdnDomain + ? `${CONFIG.cdnDomain}/${ossPath}` + : `https://${CONFIG.ossBucket}.${CONFIG.ossRegion}.aliyuncs.com/${ossPath}`; + + metadata.releases.push({ + version: versionClean, + display_name: `Scala ${version}`, + published_at: release.published_at, + download_url: cdnUrl, + github_url: asset.browser_download_url, + file_name: fileName, + size: fileSize, + md5: md5, + supported_platforms: [platform] + }); + + processedCount++; + fs.unlinkSync(localPath); + } catch (error) { + console.error(` ✗ 处理文件 ${asset.name} 失败: ${error.message}`); + } + } + + if (processedCount > 0) { + console.log(` ✓ 版本 ${version} 处理完成 (${processedCount} 个平台)\n`); + } else { + console.log(` ⚠ 版本 ${version} 没有可用的平台文件\n`); + } + } + + metadata.releases.sort((a, b) => { + const versionA = a.version.split('.').map(Number); + const versionB = b.version.split('.').map(Number); + for (let i = 0; i < Math.max(versionA.length, versionB.length); i++) { + const numA = versionA[i] || 0; + const numB = versionB[i] || 0; + if (numA !== numB) return numB - numA; + } + return 0; + }); + + console.log('\n上传 metadata.json...'); + await uploadMetadata(ossClient, metadata); + + console.log(`\n✓ 同步完成!共处理 ${metadata.releases.length} 个版本`); + + const metadataUrl = CONFIG.cdnDomain + ? `${CONFIG.cdnDomain}/${CONFIG.ossPrefix}metadata.json` + : `https://${CONFIG.ossBucket}.${CONFIG.ossRegion}.aliyuncs.com/${CONFIG.ossPrefix}metadata.json`; + console.log(`\nmetadata URL: ${metadataUrl}`); + + } catch (error) { + console.error('\n✗ 错误:', error.message); + process.exit(1); + } finally { + cleanupTempDir(); + } +} + +main(); diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index b850fe0..31cc22e 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -59,8 +59,8 @@ impl Default for AppConfig { space_dot_omission: Some(false), }), environment_mirror: Some(EnvironmentMirrorConfig { - enabled: Some(true), - base_url: Some("http://cdn.global.devlive.top".to_string()), + enabled: Some(false), + base_url: Some("https://cdn.global.devlive.top".to_string()), fallback_enabled: Some(false), }), } @@ -129,8 +129,8 @@ impl ConfigManager { // 检查并设置 environment_mirror 默认配置 if config.environment_mirror.is_none() { config.environment_mirror = Some(EnvironmentMirrorConfig { - enabled: Some(true), - base_url: Some("http://cdn.global.devlive.top".to_string()), + enabled: Some(false), + base_url: Some("https://cdn.global.devlive.top".to_string()), fallback_enabled: Some(false), }); println!("读取配置 -> 添加默认 environment_mirror 配置"); @@ -242,8 +242,8 @@ impl ConfigManager { space_dot_omission: Some(false), }), environment_mirror: Some(EnvironmentMirrorConfig { - enabled: Some(true), - base_url: Some("http://cdn.global.devlive.top".to_string()), + enabled: Some(false), + base_url: Some("https://cdn.global.devlive.top".to_string()), fallback_enabled: Some(false), }), } diff --git a/src-tauri/src/env_providers/clojure.rs b/src-tauri/src/env_providers/clojure.rs index 470e041..5d81d0e 100644 --- a/src-tauri/src/env_providers/clojure.rs +++ b/src-tauri/src/env_providers/clojure.rs @@ -130,11 +130,13 @@ impl ClojureEnvironmentProvider { let mut versions = Vec::new(); for release in metadata.releases { - // 检查是否支持当前平台 - if !release + // 检查是否支持当前平台(支持 macos-aarch64、macos-x86_64 等格式) + let is_supported = release .supported_platforms - .contains(¤t_platform.to_string()) - { + .iter() + .any(|p| p == current_platform || p.starts_with(&format!("{}-", current_platform))); + + if !is_supported { continue; } diff --git a/src-tauri/src/env_providers/scala.rs b/src-tauri/src/env_providers/scala.rs index 3680506..987eb29 100644 --- a/src-tauri/src/env_providers/scala.rs +++ b/src-tauri/src/env_providers/scala.rs @@ -251,14 +251,52 @@ impl ScalaEnvironmentProvider { false } + // 获取当前系统平台 + fn get_current_platform() -> &'static str { + if cfg!(target_os = "macos") { + if cfg!(target_arch = "aarch64") { + "macos-aarch64" + } else { + "macos-x86_64" + } + } else if cfg!(target_os = "linux") { + if cfg!(target_arch = "aarch64") { + "linux-aarch64" + } else { + "linux-x86_64" + } + } else if cfg!(target_os = "windows") { + "windows-x86_64" + } else { + "unknown" + } + } + // 将 CDN metadata 转换为 EnvironmentVersion 列表 fn parse_metadata_to_versions( &self, metadata: Metadata, ) -> Result, String> { + let current_platform = Self::get_current_platform(); let mut versions = Vec::new(); + let mut seen_versions = std::collections::HashSet::new(); for release in metadata.releases { + // 检查是否支持当前平台 + let is_supported = release + .supported_platforms + .iter() + .any(|p| p == current_platform || p.starts_with(&format!("{}-", current_platform))); + + if !is_supported { + continue; + } + + // 避免重复版本(每个版本号只保留一个) + if !seen_versions.insert(release.version.clone()) { + continue; + } + let version = release.version.clone(); let is_installed = self.is_version_installed(&version); @@ -293,6 +331,10 @@ impl ScalaEnvironmentProvider { }); } + if versions.is_empty() { + return Err(format!("没有找到支持 {} 平台的版本", current_platform)); + } + Ok(versions) } diff --git a/src/App.vue b/src/App.vue index 9e7c588..4499914 100644 --- a/src/App.vue +++ b/src/App.vue @@ -12,43 +12,49 @@ @load-example="loadExample"> -
- -
-
-
- -

{{ getLanguageDisplayName(currentLanguage) }} 代码编辑器

+
+ +