perf: 优化客户端升级

This commit is contained in:
kuaifan 2024-11-14 12:33:18 +08:00
parent e574e728d4
commit fa42194d15
2 changed files with 153 additions and 21 deletions

166
electron/build.js vendored
View File

@ -4,6 +4,7 @@ const path = require('path')
const inquirer = require('inquirer'); const inquirer = require('inquirer');
const child_process = require('child_process'); const child_process = require('child_process');
const ora = require('ora'); const ora = require('ora');
const yauzl = require('yauzl');
const axios = require('axios'); const axios = require('axios');
const FormData =require('form-data'); const FormData =require('form-data');
const utils = require('./utils'); const utils = require('./utils');
@ -19,6 +20,136 @@ const packageFile = path.resolve(__dirname, "package.json");
const packageBakFile = path.resolve(__dirname, "package-bak.json"); const packageBakFile = path.resolve(__dirname, "package-bak.json");
const platforms = ["build-mac", "build-win"]; const platforms = ["build-mac", "build-win"];
/**
* 检测并下载更新器
*/
async function detectAndDownloadUpdater() {
const updaterDir = path.resolve(__dirname, "updater");
// 创建updater目录
if (!fs.existsSync(updaterDir)) {
fs.mkdirSync(updaterDir, { recursive: true });
}
try {
// 获取最新release
const spinner = ora('Fetching latest updater release...').start();
const response = await axios.get('https://api.github.com/repos/kuaifan/dootask-updater/releases/latest', {
headers: GH_TOKEN ? { 'Authorization': `token ${GH_TOKEN}` } : {}
});
if (!response.data || !response.data.assets) {
spinner.fail('Failed to fetch updater release info');
return;
}
// 过滤出binary开头的zip文件
const assets = response.data.assets.filter(asset =>
asset.name.startsWith('binary_') && asset.name.endsWith('.zip')
);
spinner.succeed('Found updater release files');
// 下载并解压每个文件
for (const asset of assets) {
const fileName = asset.name;
// 解析平台和架构信息 (binary_0.1.0_linux-x86_64.zip => linux/x86_64)
const match = fileName.match(/binary_[\d.]+_(.+)-(.+)\.zip$/);
if (!match) continue;
let [, platform, arch] = match;
// 平台名称映射
const platformMap = {
'macos': 'mac',
'windows': 'win'
};
platform = platformMap[platform] || platform;
// 架构名称映射
const archMap = {
'x86_64': 'x64'
};
arch = archMap[arch] || arch;
const targetDir = path.join(updaterDir, platform, arch);
const zipPath = path.join(updaterDir, fileName);
// 检查是否已经下载过
if (fs.existsSync(targetDir)) {
continue;
}
// 创建目标目录
fs.mkdirSync(targetDir, { recursive: true });
// 下载文件
const downloadSpinner = ora(`Downloading ${fileName}...`).start();
try {
const writer = fs.createWriteStream(zipPath);
const response = await axios({
url: asset.browser_download_url,
method: 'GET',
responseType: 'stream',
headers: GH_TOKEN ? { 'Authorization': `token ${GH_TOKEN}` } : {}
});
response.data.pipe(writer);
await new Promise((resolve, reject) => {
writer.on('finish', resolve);
writer.on('error', reject);
});
// 解压文件
downloadSpinner.text = `Extracting ${fileName}...`;
await new Promise((resolve, reject) => {
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
if (err) reject(err);
zipfile.readEntry();
zipfile.on('entry', (entry) => {
if (/\/$/.test(entry.fileName)) {
zipfile.readEntry();
} else {
zipfile.openReadStream(entry, (err, readStream) => {
if (err) reject(err);
const outputPath = path.join(targetDir, path.basename(entry.fileName));
const writer = fs.createWriteStream(outputPath);
readStream.pipe(writer);
writer.on('finish', () => {
zipfile.readEntry();
});
});
}
});
zipfile.on('end', resolve);
});
});
// 删除zip文件
fs.unlinkSync(zipPath);
downloadSpinner.succeed(`Downloaded and extracted ${fileName}`);
} catch (error) {
downloadSpinner.fail(`Failed to download ${fileName}: ${error.message}`);
// 清理失败的下载
if (fs.existsSync(zipPath)) {
fs.unlinkSync(zipPath);
}
if (fs.existsSync(targetDir)) {
fs.rmdirSync(targetDir, { recursive: true });
}
}
}
} catch (error) {
console.error('Failed to check updater:', error.message);
}
}
/** /**
* 克隆 Drawio * 克隆 Drawio
* @param systemInfo * @param systemInfo
@ -268,7 +399,7 @@ function genericPublish({url, key, version, output}) {
* 生成配置编译应用 * 生成配置编译应用
* @param data * @param data
*/ */
function startBuild(data) { async function startBuild(data) {
const {platform, publish, release, notarize} = data.configure const {platform, publish, release, notarize} = data.configure
// system info // system info
const systemInfo = { const systemInfo = {
@ -293,7 +424,8 @@ function startBuild(data) {
console.log("Notarize: " + (notarize ? 'Yes' : 'No')); console.log("Notarize: " + (notarize ? 'Yes' : 'No'));
// drawio // drawio
cloneDrawio(systemInfo) cloneDrawio(systemInfo)
// todo: download updater // detect and download updater
await detectAndDownloadUpdater()
} }
// language // language
fse.copySync(path.resolve(__dirname, "../public/language"), path.resolve(electronDir, "language")) fse.copySync(path.resolve(__dirname, "../public/language"), path.resolve(electronDir, "language"))
@ -443,7 +575,7 @@ if (["dev"].includes(argv[2])) {
release: true, release: true,
notarize: false, notarize: false,
} }
}, false, false) })
} else if (["android-upload"].includes(argv[2])) { } else if (["android-upload"].includes(argv[2])) {
config.app.forEach(({publish}) => { config.app.forEach(({publish}) => {
if (publish.provider === 'generic') { if (publish.provider === 'generic') {
@ -454,17 +586,17 @@ if (["dev"].includes(argv[2])) {
// 自动编译 // 自动编译
platforms.filter(p => { platforms.filter(p => {
return argv[2] === "all" || p.indexOf(argv[2]) !== -1 return argv[2] === "all" || p.indexOf(argv[2]) !== -1
}).forEach(platform => { }).forEach(async platform => {
config.app.forEach(data => { for (const data of config.app) {
data.configure = { data.configure = {
platform, platform,
publish: true, publish: true,
release: true, release: true,
notarize: false, notarize: false,
} };
startBuild(data) await startBuild(data);
}) }
}) });
} else { } else {
// 手动编译(默认) // 手动编译(默认)
const questions = [ const questions = [
@ -520,7 +652,7 @@ if (["dev"].includes(argv[2])) {
}] }]
} }
]; ];
inquirer.prompt(questions).then(answers => { inquirer.prompt(questions).then(async answers => {
if (answers.publish === true) { if (answers.publish === true) {
if (!DP_KEY && (!GH_TOKEN || !utils.strExists(GH_REPOSITORY, "/"))) { if (!DP_KEY && (!GH_TOKEN || !utils.strExists(GH_REPOSITORY, "/"))) {
console.error("Missing Deploy Key or GitHub Token and Repository!"); console.error("Missing Deploy Key or GitHub Token and Repository!");
@ -533,12 +665,12 @@ if (["dev"].includes(argv[2])) {
process.exit() process.exit()
} }
} }
answers.platforms.forEach(platform => { for (const platform of answers.platforms) {
config.app.forEach(data => { for (const data of config.app) {
data.configure = answers data.configure = answers;
data.configure.platform = platform data.configure.platform = platform;
startBuild(data) await startBuild(data);
}) }
}); }
}); });
} }

View File

@ -49,7 +49,8 @@
"electron-updater": "^6.1.8", "electron-updater": "^6.1.8",
"fs-extra": "^11.2.0", "fs-extra": "^11.2.0",
"pdf-lib": "^1.17.1", "pdf-lib": "^1.17.1",
"request": "^2.88.2" "request": "^2.88.2",
"yauzl": "^2.10.0"
}, },
"trayIcon": { "trayIcon": {
"dev": { "dev": {
@ -71,7 +72,6 @@
"files": [ "files": [
"render/**/*", "render/**/*",
"public/**/*", "public/**/*",
"updater/**/*",
"electron-menu.js", "electron-menu.js",
"electron-preload.js", "electron-preload.js",
"electron.js", "electron.js",
@ -79,8 +79,8 @@
], ],
"extraFiles": [ "extraFiles": [
{ {
"from": "updater", "from": "updater/${os}/${arch}",
"to": "updater", "to": "Resources/Updater",
"filter": ["**/*"] "filter": ["**/*"]
} }
], ],