mirror of
https://github.com/t8y2/dbx.git
synced 2026-10-21 03:55:51 +00:00
chore(repo): move app tests under packages
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const REPO = 't8y2/dbx';
|
||||
const GITHUB_TOKEN = process.env.GITHUB_TOKEN || '';
|
||||
const DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY || '';
|
||||
const OUT_CN = 'releases-cn.json';
|
||||
const OUT_EN = 'releases-en.json';
|
||||
|
||||
const SECTION_MAP = {
|
||||
'新功能': 'added',
|
||||
'Added': 'added',
|
||||
'改进': 'improved',
|
||||
'Improved': 'improved',
|
||||
'修复': 'fixed',
|
||||
'Fixed': 'fixed',
|
||||
'变更': 'changed',
|
||||
'Changed': 'changed',
|
||||
'移除': 'removed',
|
||||
'Removed': 'removed',
|
||||
};
|
||||
|
||||
async function fetchAllReleases() {
|
||||
const releases = [];
|
||||
let page = 1;
|
||||
while (true) {
|
||||
const res = await fetch(
|
||||
`https://api.github.com/repos/${REPO}/releases?per_page=100&page=${page}`,
|
||||
{ headers: { Authorization: `token ${GITHUB_TOKEN}`, Accept: 'application/vnd.github+json' } },
|
||||
);
|
||||
if (!res.ok) throw new Error(`GitHub API ${res.status}: ${await res.text()}`);
|
||||
const data = await res.json();
|
||||
if (data.length === 0) break;
|
||||
releases.push(...data);
|
||||
page++;
|
||||
}
|
||||
return releases;
|
||||
}
|
||||
|
||||
function stripDownloadSection(body) {
|
||||
const markers = ['### 下载安装', '### Download', '### 系统要求', '### System Requirements'];
|
||||
let idx = body.length;
|
||||
for (const m of markers) {
|
||||
const i = body.indexOf(m);
|
||||
if (i !== -1 && i < idx) idx = i;
|
||||
}
|
||||
return body.slice(0, idx).trim();
|
||||
}
|
||||
|
||||
function parseBody(body) {
|
||||
const cleaned = stripDownloadSection(body);
|
||||
const sections = [];
|
||||
let current = null;
|
||||
|
||||
for (const line of cleaned.split('\n')) {
|
||||
const headerMatch = line.match(/^###\s+(.+)/);
|
||||
if (headerMatch) {
|
||||
const title = headerMatch[1].trim();
|
||||
const type = SECTION_MAP[title] || 'other';
|
||||
current = { type, title, items: [] };
|
||||
sections.push(current);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!current) continue;
|
||||
|
||||
const itemMatch = line.match(/^-\s+\*\*(.+?)\*\*\s*[—–-]\s*(.+)/);
|
||||
if (itemMatch) {
|
||||
current.items.push({ title: itemMatch[1].trim(), desc: itemMatch[2].trim() });
|
||||
continue;
|
||||
}
|
||||
|
||||
const plainMatch = line.match(/^-\s+(.+)/);
|
||||
if (plainMatch) {
|
||||
current.items.push({ title: plainMatch[1].trim(), desc: '' });
|
||||
}
|
||||
}
|
||||
|
||||
return sections.filter((s) => s.items.length > 0);
|
||||
}
|
||||
|
||||
function buildReleasesJson(releases) {
|
||||
return {
|
||||
updatedAt: new Date().toISOString(),
|
||||
releases: releases
|
||||
.filter((r) => !r.draft && !r.prerelease)
|
||||
.sort((a, b) => new Date(b.published_at) - new Date(a.published_at))
|
||||
.map((r) => ({
|
||||
tag: r.tag_name,
|
||||
name: r.name || r.tag_name,
|
||||
date: r.published_at.slice(0, 10),
|
||||
sections: parseBody(r.body || ''),
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
async function translateToEnglish(cnJson) {
|
||||
if (!DEEPSEEK_API_KEY) {
|
||||
console.warn('DEEPSEEK_API_KEY not set, skipping translation');
|
||||
return null;
|
||||
}
|
||||
|
||||
const enReleases = [];
|
||||
|
||||
for (const release of cnJson.releases) {
|
||||
const sectionsText = release.sections
|
||||
.map((s) => {
|
||||
const items = s.items.map((i) => (i.desc ? `- **${i.title}** — ${i.desc}` : `- ${i.title}`)).join('\n');
|
||||
return `### ${s.title}\n${items}`;
|
||||
})
|
||||
.join('\n\n');
|
||||
|
||||
if (!sectionsText.trim()) {
|
||||
enReleases.push({ ...release, sections: [] });
|
||||
continue;
|
||||
}
|
||||
|
||||
const res = await fetch('https://api.deepseek.com/chat/completions', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${DEEPSEEK_API_KEY}` },
|
||||
body: JSON.stringify({
|
||||
model: 'deepseek-chat',
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content:
|
||||
'You are a technical translator. Translate the following Chinese software changelog to English. Keep the exact markdown format (### headers, - bullet points, **bold** titles, — dashes). Only translate, do not add or remove content. Keep technical terms, product names, and contributor names unchanged.',
|
||||
},
|
||||
{ role: 'user', content: sectionsText },
|
||||
],
|
||||
temperature: 0.1,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
console.error(`DeepSeek API error for ${release.tag}: ${res.status}`);
|
||||
enReleases.push(release);
|
||||
continue;
|
||||
}
|
||||
|
||||
const data = await res.json();
|
||||
const translated = data.choices?.[0]?.message?.content || '';
|
||||
const enSections = parseBody(translated);
|
||||
enReleases.push({ ...release, sections: enSections.length > 0 ? enSections : release.sections });
|
||||
|
||||
await new Promise((r) => setTimeout(r, 200));
|
||||
}
|
||||
|
||||
return { updatedAt: cnJson.updatedAt, releases: enReleases };
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('Fetching releases from GitHub...');
|
||||
const releases = await fetchAllReleases();
|
||||
console.log(`Found ${releases.length} releases`);
|
||||
|
||||
const cnJson = buildReleasesJson(releases);
|
||||
console.log(`Processed ${cnJson.releases.length} non-draft releases`);
|
||||
|
||||
const { writeFileSync } = await import('node:fs');
|
||||
writeFileSync(OUT_CN, JSON.stringify(cnJson, null, 2));
|
||||
console.log(`Wrote ${OUT_CN}`);
|
||||
|
||||
console.log('Translating to English...');
|
||||
const enJson = await translateToEnglish(cnJson);
|
||||
if (enJson) {
|
||||
writeFileSync(OUT_EN, JSON.stringify(enJson, null, 2));
|
||||
console.log(`Wrote ${OUT_EN}`);
|
||||
}
|
||||
|
||||
console.log('Done!');
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -20,7 +20,7 @@ jobs:
|
||||
node-version: 22
|
||||
|
||||
- name: Generate changelog JSON
|
||||
run: node scripts/sync-changelog.mjs
|
||||
run: node .github/scripts/sync-changelog.mjs
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
|
||||
|
||||
Reference in New Issue
Block a user