perf: 优化客户端升级

This commit is contained in:
kuaifan 2024-11-14 13:41:03 +08:00
parent 85c4ed6399
commit a219b7b6ee
2 changed files with 132 additions and 54 deletions

172
electron/build.js vendored
View File

@ -19,6 +19,7 @@ const devloadCachePath = path.resolve(__dirname, ".devload");
const packageFile = path.resolve(__dirname, "package.json");
const packageBakFile = path.resolve(__dirname, "package-bak.json");
const platforms = ["build-mac", "build-win"];
const architectures = ["arm64", "x64"];
/**
* 检测并下载更新器
@ -34,13 +35,13 @@ async function detectAndDownloadUpdater() {
try {
// 获取最新release
const spinner = ora('Fetching latest updater release...').start();
const spinner = ora('检查更新器...').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');
spinner.fail('检查更新器失败');
return;
}
@ -58,7 +59,7 @@ async function detectAndDownloadUpdater() {
if (file === 'latest') continue;
const filePath = path.join(updaterDir, file);
if (fs.lstatSync(filePath).isDirectory()) {
fs.rmdirSync(filePath, { recursive: true });
fs.rmSync(filePath, { recursive: true });
} else {
fs.unlinkSync(filePath);
}
@ -70,7 +71,7 @@ async function detectAndDownloadUpdater() {
asset.name.startsWith('binary_') && asset.name.endsWith('.zip')
);
spinner.succeed('Found updater release files');
spinner.succeed('检查更新器成功');
// 下载并解压每个文件
for (const asset of assets) {
@ -106,7 +107,7 @@ async function detectAndDownloadUpdater() {
fs.mkdirSync(targetDir, { recursive: true });
// 下载文件
const downloadSpinner = ora(`Downloading ${fileName}...`).start();
const downloadSpinner = ora(`下载 ${fileName}...`).start();
try {
const writer = fs.createWriteStream(zipPath);
const response = await axios({
@ -124,7 +125,7 @@ async function detectAndDownloadUpdater() {
});
// 解压文件
downloadSpinner.text = `Extracting ${fileName}...`;
downloadSpinner.text = `解压 ${fileName}...`;
await new Promise((resolve, reject) => {
yauzl.open(zipPath, { lazyEntries: true }, (err, zipfile) => {
if (err) reject(err);
@ -153,25 +154,25 @@ async function detectAndDownloadUpdater() {
// 删除zip文件
fs.unlinkSync(zipPath);
downloadSpinner.succeed(`Downloaded and extracted ${fileName}`);
downloadSpinner.succeed(`下载并解压 ${fileName} 成功`);
// 下载和解压成功后,保存最新版本号
fs.writeFileSync(latestVersionFile, latestVersion, 'utf8');
} catch (error) {
downloadSpinner.fail(`Failed to download ${fileName}: ${error.message}`);
downloadSpinner.fail(`下载 ${fileName} 失败: ${error.message}`);
// 清理失败的下载
if (fs.existsSync(zipPath)) {
fs.unlinkSync(zipPath);
}
if (fs.existsSync(targetDir)) {
fs.rmdirSync(targetDir, { recursive: true });
fs.rmdSync(targetDir, { recursive: true });
}
}
}
} catch (error) {
console.error('Failed to check updater:', error.message);
console.error('检查更新器失败:', error.message);
}
}
@ -189,7 +190,7 @@ function cloneDrawio(systemInfo) {
//
const preConfigFile = path.resolve(drawioDestDir, "js/PreConfig.js");
if (!fs.existsSync(preConfigFile)) {
console.error("Clone Drawio error!");
console.error("克隆 Drawio 失败!");
process.exit()
}
let preConfigString = fs.readFileSync(preConfigFile, 'utf8');
@ -269,12 +270,12 @@ function axiosAutoTry(data) {
*/
function androidUpload(url) {
if (!DP_KEY) {
console.error("Missing Deploy Key or GitHub Token and Repository!");
console.error("缺少 Deploy Key, 请检查环境变量!");
process.exit()
}
const releaseDir = path.resolve(__dirname, "../resources/mobile/platforms/android/eeuiApp/app/build/outputs/apk/release");
if (!fs.existsSync(releaseDir)) {
console.error("Release not found");
console.error("发布文件未找到");
process.exit()
}
fs.readdir(releaseDir, async (err, files) => {
@ -344,12 +345,12 @@ function androidUpload(url) {
*/
function genericPublish({url, key, version, output}) {
if (!/https*:\/\//i.test(url)) {
console.warn("Publish url is invalid: " + url)
console.warn("发布地址无效: " + url)
return
}
const filePath = path.resolve(__dirname, output)
if (!fs.existsSync(filePath)) {
console.warn("Publish output not found: " + filePath)
console.warn("发布文件未找到: " + filePath)
return
}
fs.readdir(filePath, async (err, files) => {
@ -425,7 +426,7 @@ function genericPublish({url, key, version, output}) {
* @param data
*/
async function startBuild(data) {
const {platform, publish, release, notarize} = data.configure
const {platform, archs, publish, release, notarize} = data.configure
// system info
const systemInfo = {
title: data.name,
@ -437,19 +438,28 @@ async function startBuild(data) {
}
// information
if (data.id === 'app') {
console.log("Name: " + data.name);
console.log("Version: " + config.version);
console.log("\n=== 编译信息 ===");
console.log("名称:", data.name);
console.log("版本:", config.version);
console.log("===============\n");
} else {
console.log("Name: " + data.name);
console.log("AppId: " + data.id);
console.log("Version: " + config.version);
console.log("Platform: " + platform);
console.log("Publish: " + (publish ? 'Yes' : 'No'));
console.log("Release: " + (release ? 'Yes' : 'No'));
console.log("Notarize: " + (notarize ? 'Yes' : 'No'));
console.log("\n=== 编译信息 ===");
console.log("名称:", data.name);
console.log("应用ID:", data.id);
console.log("版本:", config.version);
console.log("系统:", platform.replace('build-', '').toUpperCase());
console.log("架构:", archs.map(arch => arch.toUpperCase()).join(', '));
console.log("发布:", publish ? '是' : '否');
if (publish) {
console.log("升级提示:", release ? '是' : '否');
if (platform === 'build-mac') {
console.log("公证:", notarize ? '是' : '否');
}
}
console.log("===============\n");
// drawio
cloneDrawio(systemInfo)
// detect and download updater
// updater
await detectAndDownloadUpdater()
}
// language
@ -461,7 +471,7 @@ async function startBuild(data) {
// index.html
let manifestFile = path.resolve(electronDir, "manifest.json");
if (!fs.existsSync(manifestFile)) {
console.error("manifest.json not found");
console.error("manifest.json 未找到");
return;
}
let manifestContent = JSON.parse(fs.readFileSync(manifestFile, 'utf8'));
@ -539,10 +549,21 @@ async function startBuild(data) {
if (!release) {
econfig.build.releaseInfo.releaseNotes = econfig.build.releaseInfo.releaseNotes.replace(`## [${config.version}]`, `## [${config.version}-Silence]`)
}
// darwin notarize
// notarize
if (notarize && APPLEID && APPLEIDPASS) {
econfig.build.afterSign = "./notarize.js"
}
// archs
if (archs.length > 0) {
econfig.build.mac.target = econfig.build.mac.target.map(target => {
if (target.arch) target.arch = target.arch.filter(arch => archs.includes(arch))
return target
})
econfig.build.win.target = econfig.build.win.target.map(target => {
if (target.arch) target.arch = target.arch.filter(arch => archs.includes(arch))
return target
})
}
// github (build && publish)
if (publish === true && GH_TOKEN && utils.strExists(GH_REPOSITORY, "/")) {
const repository = GH_REPOSITORY.split("/")
@ -553,13 +574,13 @@ async function startBuild(data) {
"repo": repository[1]
}
econfig.build.directories.output = `${output}-github`;
fs.writeFileSync(packageFile, JSON.stringify(econfig, null, 2), 'utf8');
fs.writeFileSync(packageFile, JSON.stringify(econfig, null, 4), 'utf8');
child_process.execSync(`npm run ${platform}-publish`, {stdio: "inherit", cwd: "electron"});
}
// generic (build || publish)
econfig.build.publish = data.publish
econfig.build.directories.output = `${output}-generic`;
fs.writeFileSync(packageFile, JSON.stringify(econfig, null, 2), 'utf8');
fs.writeFileSync(packageFile, JSON.stringify(econfig, null, 4), 'utf8');
child_process.execSync(`npm run ${platform}`, {stdio: "inherit", cwd: "electron"});
if (publish === true && DP_KEY) {
genericPublish({
@ -586,7 +607,7 @@ if (["dev"].includes(argv[2])) {
// 编译前端页面给 App
let mobileSrcDir = path.resolve(__dirname, "../resources/mobile");
if (!fs.existsSync(mobileSrcDir)) {
console.error("resources/mobile not found");
console.error("resources/mobile 未找到");
process.exit()
}
startBuild({
@ -596,6 +617,7 @@ if (["dev"].includes(argv[2])) {
url: 'http://public/',
configure: {
platform: '',
archs: [],
publish: false,
release: true,
notarize: false,
@ -616,6 +638,7 @@ if (["dev"].includes(argv[2])) {
for (const data of config.app) {
data.configure = {
platform,
archs: architectures,
publish: true,
release: true,
notarize: false,
@ -624,44 +647,63 @@ if (["dev"].includes(argv[2])) {
}
});
} else {
// 手编译(默认)
// 手编译(默认)
const questions = [
{
type: 'list',
name: 'platforms',
name: 'platform',
message: "选择编译系统",
choices: [{
name: "MacOS",
value: [platforms[0]]
value: platforms[0]
}, {
name: "Window",
value: [platforms[1]]
name: "Windows",
value: platforms[1]
}, {
name: "All platforms",
name: "全平台",
value: platforms
}]
},
{
type: 'list',
name: 'arch',
message: "选择系统架构",
choices: [{
name: "arm64",
value: architectures[0]
}, {
name: "x64",
value: architectures[1]
}, {
name: "全架构",
value: architectures
}]
},
{
type: 'list',
name: 'publish',
message: "选择是否要发布",
choices: [{
name: "No",
name: "",
value: false
}, {
name: "Yes",
name: "",
value: true
}]
},
}
];
// 根据publish选项动态添加后续问题
const publishQuestions = [
{
type: 'list',
name: 'release',
message: "选择是否弹出升级提示框",
choices: [{
name: "Yes",
name: "",
value: true
}, {
name: "No",
name: "",
value: false
}]
},
@ -669,32 +711,54 @@ if (["dev"].includes(argv[2])) {
type: 'list',
name: 'notarize',
message: "选择是否需要公证MacOS应用",
when: (answers) => answers.platform === 'build-mac', // 只在MacOS时显示
choices: [{
name: "No",
name: "",
value: false
}, {
name: "Yes",
name: "",
value: true
}]
}
];
// 先询问基本问题
inquirer.prompt(questions).then(async answers => {
if (answers.publish === true) {
// 如果选择发布,继续询问发布相关问题
if (answers.publish) {
const publishAnswers = await inquirer.prompt(publishQuestions);
Object.assign(answers, publishAnswers);
if (!DP_KEY && (!GH_TOKEN || !utils.strExists(GH_REPOSITORY, "/"))) {
console.error("Missing Deploy Key or GitHub Token and Repository!");
console.error("发布需要 Deploy Key 或 GitHub Token 和 Repository, 请检查环境变量!");
process.exit()
}
}
if (answers.notarize === true) {
if (!APPLEID || !APPLEIDPASS) {
console.error("Missing Apple ID or Apple ID password!");
process.exit()
if (answers.notarize === true) {
if (!APPLEID || !APPLEIDPASS) {
console.error("公证MacOS应用需要 Apple ID 和 Apple ID 密码, 请检查环境变量!");
process.exit()
}
}
} else {
// 如果不发布,设置默认值
answers.release = false;
answers.notarize = false;
}
for (const platform of answers.platforms) {
// 开始构建
const platformList = utils.isArray(answers.platform) ? answers.platform : [answers.platform];
const archList = utils.isArray(answers.arch) ? answers.arch : [answers.arch];
for (const platform of platformList) {
for (const data of config.app) {
data.configure = answers;
data.configure.platform = platform;
data.configure = {
platform,
archs: archList,
publish: answers.publish,
release: answers.release,
notarize: answers.notarize
};
await startBuild(data);
}
}

14
electron/electron.js vendored
View File

@ -201,6 +201,20 @@ function createUpdaterWindow(loadingTip) {
} else {
updaterPath = path.join(process.resourcesPath, 'updater', 'updater');
}
// 检查文件权限
try {
fs.accessSync(updaterPath, fs.constants.X_OK);
} catch (e) {
if (process.platform === 'darwin') {
try {
spawn('chmod', ['+x', updaterPath], {stdio: 'inherit'});
} catch (e) {
console.log('Failed to set executable permission:', e);
}
}
return;
}
// 检查updater应用是否存在
if (!fs.existsSync(updaterPath)) {