Files
ele-HeiXiu/build/scripts/upload-oss.mjs
YoungestSongMo 337d6c1205 feat: 构建系统重构 + VCHSM 架构迁移 + 右键菜单修复 + 尺寸参数统一
## 构建、打包与分发系统重构 (build/)

- 构建系统收敛至 build/ 目录:build.mjs + electron-builder.js + 6 个脚本

- electron-builder 配置从 package.json 提取为 build/electron-builder.js

- 新增 --edition (personal/enterprise) 和 --channel (stable/beta/alpha) 参数

- OSS 路径按渠道隔离

- 新增 pre-build-check.mjs:Git/CHANGELOG/版本/环境变量校验

- 新增 bump-version.mjs:版本号管理 + CHANGELOG 自动生成

- updater.ts 发送 channel/edition/deviceFingerprint

- 新增 src/shared/utils/rollout.ts 灰度测试工具

- 新增 10 条 NPM 脚本

## VCHSM 五层架构迁移

- 7 个业务模块 + ~500 处导入路径同步

## 修复

- MediaCardContextMenu 改用共享 ContextMenu 组件

- ComboBox 尺寸参数显示统一 (size-format.ts)

## 文档

- UpdateA.md / build/README.md / README.md / .env.example 更新

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 18:41:07 +08:00

243 lines
9.0 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ============================================================
// 上传安装包 + blockmap 文件到阿里云 OSS
//
// 用法(由 build.mjs 调用):
// node build/scripts/upload-oss.mjs <platform> <version> <channel> <ossPrefix> <releaseDirName>
//
// 也支持手动调用:
// node build/scripts/upload-oss.mjs <platform> <version> [channel] [ossPrefix]
//
// 环境变量(使用 Node.js 直传时需要):
// OSS_ENDPOINT — OSS endpoint如 oss-cn-hangzhou.aliyuncs.com
// OSS_BUCKET — Bucket 名称
// OSS_ACCESS_KEY_ID — AccessKey ID
// OSS_ACCESS_KEY_SECRET — AccessKey Secret
//
// 不传 OSS 环境变量时 → 打印手动上传命令
// ============================================================
import { readFileSync, readdirSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { createHmac } from 'node:crypto';
const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, '..', '..');
// ---------- 参数 ----------
const platform = process.argv[2];
const version = process.argv[3];
const channel = process.argv[4] || 'stable';
const ossPrefix = process.argv[5] || `releases/${version}`;
const releaseDirName = process.argv[6] || version;
if (!platform || !version) {
console.error('用法: node build/scripts/upload-oss.mjs <platform> <version> [channel] [ossPrefix] [releaseDirName]');
process.exit(1);
}
// ---------- 查找安装包 ----------
const releaseDir = join(ROOT, 'release', releaseDirName);
let installerName;
try {
const all = readdirSync(releaseDir);
const exts = platform === 'win32' ? ['.exe'] : platform === 'darwin' ? ['.dmg'] : ['.AppImage'];
installerName = all.find((f) => exts.some((e) => f.endsWith(e)));
if (!installerName) {
console.error(`错误: ${releaseDir} — 未找到安装包`);
process.exit(1);
}
} catch (err) {
console.error(`错误: ${releaseDir}${err.message}`);
process.exit(1);
}
const installerPath = join(releaseDir, installerName);
// ---------- 查找 blockmap 文件 ----------
let blockmapName = null;
let blockmapPath = null;
try {
const all = readdirSync(releaseDir);
blockmapName = all.find((f) => f.endsWith('.blockmap'));
if (blockmapName) {
blockmapPath = join(releaseDir, blockmapName);
}
} catch {
// blockmap 不存在不影响主流程
}
// ---------- OSS 配置 ----------
const {
OSS_ENDPOINT,
OSS_BUCKET,
OSS_ACCESS_KEY_ID,
OSS_ACCESS_KEY_SECRET,
} = process.env;
const installerObjectKey = `${ossPrefix}/${installerName}`;
// ============================================================
// 模式 A无 OSS 配置 → 打印手动命令
// ============================================================
if (!OSS_ENDPOINT || !OSS_BUCKET || !OSS_ACCESS_KEY_ID) {
const exampleBucket = OSS_BUCKET || '<bucket>';
const exampleEndpoint = OSS_ENDPOINT || '<endpoint>';
const downloadUrl = `https://${exampleBucket}.${exampleEndpoint}/${installerObjectKey}`;
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log('📦 上传安装包 + blockmap手动');
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
console.log(`渠道: ${channel}`);
console.log(`OSS 路径: ${ossPrefix}/`);
console.log(`文件: ${installerPath}`);
console.log(`大小: ${(readFileSync(installerPath).length / 1024 / 1024).toFixed(2)} MB\n`);
if (blockmapPath) {
console.log(`Blockmap: ${blockmapPath}`);
console.log(`Blockmap 大小: ${(readFileSync(blockmapPath).length / 1024).toFixed(2)} KB\n`);
}
console.log('方式 1 — ossutil CLI推荐');
console.log(` ossutil cp "${installerPath}" oss://${exampleBucket}/${installerObjectKey}`);
if (blockmapPath) {
const blockmapKey = `${ossPrefix}/${blockmapName}`;
console.log(` ossutil cp "${blockmapPath}" oss://${exampleBucket}/${blockmapKey}`);
}
console.log('');
console.log('方式 2 — 阿里云控制台:');
console.log(` 登录 OSS 控制台 → ${exampleBucket} → 上传 → 目标路径: ${installerObjectKey}`);
if (blockmapPath) {
console.log(` 同时上传 ${blockmapName} 到相同目录`);
}
console.log('');
const blockmapUrl = blockmapPath
? `https://${exampleBucket}.${exampleEndpoint}/${ossPrefix}/${blockmapName}`
: '';
console.log('上传后执行以下命令,用实际 URL 重新生成 info');
if (blockmapUrl) {
console.log(` node build/scripts/generate-update-info.mjs ${platform} ${version} ${channel} --url "${downloadUrl}" --blockmap-url "${blockmapUrl}"`);
console.log('');
console.log('或直接发布到后端 API');
console.log(` node build/scripts/publish-release.mjs ${platform} ${version} ${channel} --url "${downloadUrl}" --blockmap-url "${blockmapUrl}"`);
} else {
console.log(` node build/scripts/generate-update-info.mjs ${platform} ${version} ${channel} --url "${downloadUrl}"`);
console.log('');
console.log('或直接发布到后端 API');
console.log(` node build/scripts/publish-release.mjs ${platform} ${version} ${channel} --url "${downloadUrl}"`);
}
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
process.exit(0);
}
// ============================================================
// 模式 BNode.js 直传 OSS安装包 + blockmap
// ============================================================
const fileBuffer = readFileSync(installerPath);
const contentType = platform === 'win32'
? 'application/vnd.microsoft.portable-executable'
: 'application/octet-stream';
// 上传安装包
const date = new Date().toUTCString();
const stringToSign = [
'PUT', '', contentType, date, `/${OSS_BUCKET}/${installerObjectKey}`,
].join('\n');
const signature = createHmac('sha1', OSS_ACCESS_KEY_SECRET)
.update(stringToSign)
.digest('base64');
const authHeader = `OSS ${OSS_ACCESS_KEY_ID}:${signature}`;
const downloadUrl = `https://${OSS_BUCKET}.${OSS_ENDPOINT}/${installerObjectKey}`;
console.log(`上传中: ${installerName} (${(fileBuffer.length / 1024 / 1024).toFixed(2)} MB)`);
console.log(`渠道: ${channel}`);
console.log(`目标: oss://${OSS_BUCKET}/${installerObjectKey}`);
try {
const response = await fetch(downloadUrl, {
method: 'PUT',
headers: {
'Content-Type': contentType,
'Content-Length': String(fileBuffer.length),
'Date': date,
'Authorization': authHeader,
},
body: fileBuffer,
});
if (!response.ok) {
const body = await response.text();
console.error(`❌ 安装包上传失败 HTTP ${response.status}: ${body}`);
process.exit(1);
}
console.log('✅ 安装包上传成功');
console.log(`下载 URL: ${downloadUrl}`);
} catch (err) {
console.error(`❌ 安装包上传失败: ${err.message}`);
process.exit(1);
}
// 上传 blockmap 文件
let blockmapUrl = null;
if (blockmapPath) {
const blockmapBuffer = readFileSync(blockmapPath);
const blockmapKey = `${ossPrefix}/${blockmapName}`;
blockmapUrl = `https://${OSS_BUCKET}.${OSS_ENDPOINT}/${blockmapKey}`;
const bmDate = new Date().toUTCString();
const bmStringToSign = [
'PUT', '', 'application/octet-stream', bmDate, `/${OSS_BUCKET}/${blockmapKey}`,
].join('\n');
const bmSignature = createHmac('sha1', OSS_ACCESS_KEY_SECRET)
.update(bmStringToSign)
.digest('base64');
console.log(`\n上传 blockmap: ${blockmapName} (${(blockmapBuffer.length / 1024).toFixed(2)} KB)`);
try {
const bmResponse = await fetch(blockmapUrl, {
method: 'PUT',
headers: {
'Content-Type': 'application/octet-stream',
'Content-Length': String(blockmapBuffer.length),
'Date': bmDate,
'Authorization': `OSS ${OSS_ACCESS_KEY_ID}:${bmSignature}`,
},
body: blockmapBuffer,
});
if (bmResponse.ok) {
console.log('✅ Blockmap 上传成功');
console.log(`Blockmap URL: ${blockmapUrl}`);
} else {
console.warn(`⚠️ Blockmap 上传失败不影响主流程HTTP ${bmResponse.status}`);
blockmapUrl = null;
}
} catch (err) {
console.warn(`⚠️ Blockmap 上传失败(不影响主流程): ${err.message}`);
blockmapUrl = null;
}
}
// 后续步骤提示
console.log('\n接下来 — 重新生成 update-info.json');
if (blockmapUrl) {
console.log(` node build/scripts/generate-update-info.mjs ${platform} ${version} ${channel} --url "${downloadUrl}" --blockmap-url "${blockmapUrl}"`);
console.log('\n或直接发布到后端 API');
console.log(` node build/scripts/publish-release.mjs ${platform} ${version} ${channel} --url "${downloadUrl}" --blockmap-url "${blockmapUrl}"`);
} else {
console.log(` node build/scripts/generate-update-info.mjs ${platform} ${version} ${channel} --url "${downloadUrl}"`);
console.log('\n或直接发布到后端 API');
console.log(` node build/scripts/publish-release.mjs ${platform} ${version} ${channel} --url "${downloadUrl}"`);
}