Skip to content

Commit ef94737

Browse files
authored
Merge pull request #67 from qianmoQ/dev-25.0.5
feat: 支持拖拽调整窗口
2 parents b0063da + 42c46d6 commit ef94737

File tree

11 files changed

+957
-48
lines changed

11 files changed

+957
-48
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,4 @@ go.mod
3030
.RData
3131
.Rhistory
3232
program
33+
.env

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@tauri-apps/cli": "^2",
4545
"@types/lodash-es": "^4.17.12",
4646
"@vitejs/plugin-vue": "^5.2.1",
47+
"ali-oss": "^6.23.0",
4748
"autoprefixer": "^10.4.21",
4849
"postcss": "^8.5.6",
4950
"tailwindcss": "^4.1.11",

scripts/sync-clojure-versions.js

Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
#!/usr/bin/env node
2+
3+
/**
4+
* 同步 Clojure 版本到阿里云 OSS
5+
*
6+
* 功能:
7+
* 1. 从 GitHub API 获取 Clojure 所有版本
8+
* 2. 下载版本文件
9+
* 3. 使用阿里云官方 ali-oss SDK 上传到 OSS /global/plugins/clojure/ 目录
10+
* 4. 生成 metadata.json 文件
11+
*
12+
* 使用方法:
13+
* node scripts/sync-clojure-versions.js
14+
*
15+
* 环境变量(可以在 .env 文件中配置):
16+
* - OSS_REGION: 阿里云 OSS 区域
17+
* - OSS_ACCESS_KEY_ID: 阿里云访问密钥 ID
18+
* - OSS_ACCESS_KEY_SECRET: 阿里云访问密钥 Secret
19+
* - OSS_BUCKET: OSS Bucket 名称
20+
* - CDN_DOMAIN: 自定义 CDN 域名(可选)
21+
*/
22+
23+
import https from 'https';
24+
import http from 'http';
25+
import fs from 'fs';
26+
import path from 'path';
27+
import crypto from 'crypto';
28+
import { fileURLToPath } from 'url';
29+
import OSS from 'ali-oss';
30+
31+
// ES 模块中获取 __dirname
32+
const __filename = fileURLToPath(import.meta.url);
33+
const __dirname = path.dirname(__filename);
34+
35+
// 加载 .env 文件
36+
function loadEnv() {
37+
const envPath = path.join(__dirname, '..', '.env');
38+
if (fs.existsSync(envPath)) {
39+
const envContent = fs.readFileSync(envPath, 'utf8');
40+
let loadedCount = 0;
41+
42+
envContent.split('\n').forEach(line => {
43+
line = line.trim();
44+
// 跳过注释和空行
45+
if (!line || line.startsWith('#')) return;
46+
47+
const match = line.match(/^([^=]+)=(.*)$/);
48+
if (match) {
49+
const key = match[1].trim();
50+
let value = match[2].trim();
51+
// 移除引号
52+
value = value.replace(/^["']|["']$/g, '');
53+
// 只在环境变量不存在时设置
54+
if (!process.env[key]) {
55+
process.env[key] = value;
56+
loadedCount++;
57+
}
58+
}
59+
});
60+
61+
console.log(`✓ 已从 .env 文件加载 ${loadedCount} 个环境变量\n`);
62+
} else {
63+
console.log('⚠ 未找到 .env 文件,将使用环境变量或默认值\n');
64+
}
65+
}
66+
67+
// 获取配置(在 loadEnv 之后调用)
68+
function getConfig() {
69+
return {
70+
ossRegion: process.env.OSS_REGION || 'oss-cn-hangzhou',
71+
ossAccessKeyId: process.env.OSS_ACCESS_KEY_ID,
72+
ossAccessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
73+
ossBucket: process.env.OSS_BUCKET,
74+
cdnDomain: process.env.CDN_DOMAIN, // 自定义 CDN 域名(可选)
75+
githubRepo: 'clojure/brew-install',
76+
ossPrefix: 'global/plugins/clojure/',
77+
tempDir: path.join(__dirname, '.temp-clojure'),
78+
// 支持的平台
79+
platforms: ['macos-aarch64', 'macos-x86_64', 'linux-aarch64', 'linux-x86_64', 'windows-x86_64']
80+
};
81+
}
82+
83+
// 全局配置变量
84+
let CONFIG;
85+
86+
// 验证配置
87+
function validateConfig() {
88+
const missing = [];
89+
if (!CONFIG.ossAccessKeyId) missing.push('OSS_ACCESS_KEY_ID');
90+
if (!CONFIG.ossAccessKeySecret) missing.push('OSS_ACCESS_KEY_SECRET');
91+
if (!CONFIG.ossBucket) missing.push('OSS_BUCKET');
92+
93+
if (missing.length > 0) {
94+
console.error('错误: 请设置以下环境变量:');
95+
missing.forEach(key => console.error(` - ${key}`));
96+
console.error('\n提示: 可以在 .env 文件中配置这些变量');
97+
console.error('示例: cp .env.example .env');
98+
process.exit(1);
99+
}
100+
101+
// 显示配置信息(隐藏敏感信息)
102+
console.log('配置信息:');
103+
console.log(` OSS Region: ${CONFIG.ossRegion}`);
104+
console.log(` OSS Bucket: ${CONFIG.ossBucket}`);
105+
console.log(` CDN Domain: ${CONFIG.cdnDomain || '未配置 (使用默认 OSS 域名)'}`);
106+
console.log('');
107+
}
108+
109+
// 创建临时目录
110+
function ensureTempDir() {
111+
if (!fs.existsSync(CONFIG.tempDir)) {
112+
fs.mkdirSync(CONFIG.tempDir, { recursive: true });
113+
}
114+
}
115+
116+
// 清理临时目录
117+
function cleanupTempDir() {
118+
if (fs.existsSync(CONFIG.tempDir)) {
119+
fs.rmSync(CONFIG.tempDir, { recursive: true, force: true });
120+
}
121+
}
122+
123+
// HTTP(S) GET 请求
124+
function httpGet(url, isJson = true) {
125+
return new Promise((resolve, reject) => {
126+
const client = url.startsWith('https') ? https : http;
127+
const options = {
128+
headers: {
129+
'User-Agent': 'CodeForge-Sync-Script'
130+
}
131+
};
132+
133+
client.get(url, options, (res) => {
134+
if (res.statusCode === 302 || res.statusCode === 301) {
135+
// 处理重定向
136+
return httpGet(res.headers.location, isJson).then(resolve).catch(reject);
137+
}
138+
139+
if (res.statusCode !== 200) {
140+
reject(new Error(`HTTP ${res.statusCode}: ${url}`));
141+
return;
142+
}
143+
144+
const chunks = [];
145+
res.on('data', chunk => chunks.push(chunk));
146+
res.on('end', () => {
147+
const data = Buffer.concat(chunks);
148+
if (isJson) {
149+
try {
150+
resolve(JSON.parse(data.toString()));
151+
} catch (e) {
152+
reject(new Error(`JSON 解析失败: ${e.message}`));
153+
}
154+
} else {
155+
resolve(data);
156+
}
157+
});
158+
}).on('error', reject);
159+
});
160+
}
161+
162+
// 下载文件
163+
async function downloadFile(url, destPath) {
164+
console.log(` 下载: ${url}`);
165+
const data = await httpGet(url, false);
166+
fs.writeFileSync(destPath, data);
167+
return destPath;
168+
}
169+
170+
// 获取 GitHub releases
171+
async function getGitHubReleases() {
172+
console.log('正在获取 Clojure 版本列表...');
173+
const url = `https://api.github.com/repos/${CONFIG.githubRepo}/releases?per_page=100`;
174+
return await httpGet(url);
175+
}
176+
177+
// 计算文件 MD5
178+
function calculateMD5(filePath) {
179+
const buffer = fs.readFileSync(filePath);
180+
return crypto.createHash('md5').update(buffer).digest('hex');
181+
}
182+
183+
// 获取文件大小
184+
function getFileSize(filePath) {
185+
const stats = fs.statSync(filePath);
186+
return stats.size;
187+
}
188+
189+
// 创建 OSS 客户端
190+
function createOSSClient() {
191+
return new OSS({
192+
region: CONFIG.ossRegion,
193+
accessKeyId: CONFIG.ossAccessKeyId,
194+
accessKeySecret: CONFIG.ossAccessKeySecret,
195+
bucket: CONFIG.ossBucket
196+
});
197+
}
198+
199+
// 上传文件到 OSS
200+
async function uploadToOSS(client, localPath, ossPath) {
201+
try {
202+
await client.put(ossPath, localPath);
203+
console.log(` ✓ 上传成功: ${ossPath}`);
204+
} catch (error) {
205+
throw new Error(`上传失败: ${error.message}`);
206+
}
207+
}
208+
209+
// 上传 metadata.json 到 OSS
210+
async function uploadMetadata(client, metadata) {
211+
try {
212+
const metadataJson = JSON.stringify(metadata, null, 2);
213+
const buffer = Buffer.from(metadataJson, 'utf8');
214+
const ossPath = `${CONFIG.ossPrefix}metadata.json`;
215+
216+
await client.put(ossPath, buffer);
217+
console.log(`✓ metadata.json 上传成功`);
218+
} catch (error) {
219+
throw new Error(`上传 metadata 失败: ${error.message}`);
220+
}
221+
}
222+
223+
// 主函数
224+
async function main() {
225+
try {
226+
console.log('=== Clojure 版本同步工具 ===\n');
227+
228+
// 加载 .env 文件
229+
loadEnv();
230+
231+
// 初始化配置
232+
CONFIG = getConfig();
233+
234+
// 验证配置
235+
validateConfig();
236+
237+
// 创建 OSS 客户端
238+
const ossClient = createOSSClient();
239+
240+
// 创建临时目录
241+
ensureTempDir();
242+
243+
// 获取 releases
244+
const releases = await getGitHubReleases();
245+
console.log(`找到 ${releases.length} 个版本\n`);
246+
247+
const metadata = {
248+
language: 'clojure',
249+
last_updated: new Date().toISOString(),
250+
releases: []
251+
};
252+
253+
// 处理每个版本
254+
for (const release of releases) {
255+
const version = release.tag_name;
256+
console.log(`处理版本: ${version}`);
257+
258+
// 查找 tar.gz 资源
259+
const asset = release.assets.find(a =>
260+
a.name.endsWith('.tar.gz') && a.name.includes('clojure-tools')
261+
);
262+
263+
if (!asset) {
264+
console.log(` ⚠ 跳过: 未找到 tar.gz 文件`);
265+
continue;
266+
}
267+
268+
try {
269+
// 下载文件
270+
const fileName = asset.name;
271+
const localPath = path.join(CONFIG.tempDir, fileName);
272+
await downloadFile(asset.browser_download_url, localPath);
273+
274+
// 计算文件信息
275+
const fileSize = getFileSize(localPath);
276+
const md5 = calculateMD5(localPath);
277+
278+
// 上传到 OSS
279+
const ossPath = `${CONFIG.ossPrefix}${fileName}`;
280+
await uploadToOSS(ossClient, localPath, ossPath);
281+
282+
// 添加到 metadata
283+
// 使用自定义 CDN 域名或默认 OSS 域名
284+
const cdnUrl = CONFIG.cdnDomain
285+
? `${CONFIG.cdnDomain}/${ossPath}`
286+
: `https://${CONFIG.ossBucket}.${CONFIG.ossRegion}.aliyuncs.com/${ossPath}`;
287+
288+
metadata.releases.push({
289+
version: version.replace(/^v/, ''),
290+
display_name: `Clojure ${version}`,
291+
published_at: release.published_at,
292+
download_url: cdnUrl,
293+
github_url: asset.browser_download_url,
294+
file_name: fileName,
295+
size: fileSize,
296+
md5: md5,
297+
supported_platforms: CONFIG.platforms
298+
});
299+
300+
console.log(` ✓ 版本 ${version} 处理完成\n`);
301+
302+
// 删除本地文件
303+
fs.unlinkSync(localPath);
304+
} catch (error) {
305+
console.error(` ✗ 处理版本 ${version} 失败: ${error.message}\n`);
306+
}
307+
}
308+
309+
// 按版本号排序(最新的在前)
310+
metadata.releases.sort((a, b) => {
311+
const versionA = a.version.split('.').map(Number);
312+
const versionB = b.version.split('.').map(Number);
313+
for (let i = 0; i < Math.max(versionA.length, versionB.length); i++) {
314+
const numA = versionA[i] || 0;
315+
const numB = versionB[i] || 0;
316+
if (numA !== numB) return numB - numA;
317+
}
318+
return 0;
319+
});
320+
321+
console.log('\n上传 metadata.json...');
322+
await uploadMetadata(ossClient, metadata);
323+
324+
console.log(`\n✓ 同步完成!共处理 ${metadata.releases.length} 个版本`);
325+
326+
// 输出 metadata URL
327+
const metadataUrl = CONFIG.cdnDomain
328+
? `${CONFIG.cdnDomain}/${CONFIG.ossPrefix}metadata.json`
329+
: `https://${CONFIG.ossBucket}.${CONFIG.ossRegion}.aliyuncs.com/${CONFIG.ossPrefix}metadata.json`;
330+
console.log(`\nmetadata URL: ${metadataUrl}`);
331+
332+
} catch (error) {
333+
console.error('\n✗ 错误:', error.message);
334+
process.exit(1);
335+
} finally {
336+
// 清理临时目录
337+
cleanupTempDir();
338+
}
339+
}
340+
341+
// 运行
342+
main();

0 commit comments

Comments
 (0)