Typora插件项目中的自动升级功能解析
痛点:插件升级的困扰
作为Typora用户,你是否曾经遇到过这样的困扰:
- 发现新版本插件发布,但手动下载安装过程繁琐
- 担心升级过程中丢失自定义配置和插件
- 网络环境限制导致无法直接从GitHub下载更新
- 希望插件能够自动检测并提示新版本
Typora插件项目的自动升级功能正是为了解决这些痛点而生,它提供了智能、安全、便捷的升级体验。
升级功能架构解析
核心类结构
自动升级功能主要由三个核心类构成:
升级流程详解
自动升级功能遵循严谨的六步流程:
1. 准备工作阶段
prepare = async () => {
console.log("[1/6] prepare: ensure work dir");
this.pkgFsExtra.ensureDir(this.workDir);
await this.chmod();
}
此阶段创建临时工作目录并确保插件目录有足够的权限。
2. 版本检查机制
版本检查是升级流程的核心,通过对比GitHub Release API返回的最新版本信息和本地版本文件来判断是否需要更新:
checkNeedUpdate = async (url = this.latestReleaseUrl) => {
console.log("[2/6] check if update is needed");
const _getLatestVersion = async () => {
const resp = await this.utils.fetch(url, this.requestOption);
return resp.json()
}
const _getCurrentVersion = async () => {
try {
const exist = await this.utils.existPath(this.versionFile)
if (exist) {
return this.pkgFsExtra.readJson(this.versionFile);
}
} catch (e) {
console.debug("not exist version.json");
}
}
this.latestVersionInfo = await _getLatestVersion();
this.currentVersionInfo = await _getCurrentVersion();
if (!this.currentVersionInfo) return true;
const result = this.utils.compareVersion(
this.latestVersionInfo.tag_name,
this.currentVersionInfo.tag_name
);
return result !== 0
}
3. 文件保护策略
升级过程中最重要的保护机制是排除用户自定义文件:
excludeFiles = async () => {
console.log("[5/6] exclude files");
const oldDir = this.utils.joinPath(this.customPluginDir);
const newDir = this.pkgPath.join(this.unzipDir, this.customPluginDir);
// 对比新旧插件目录,确保自定义插件不被覆盖
const oldFds = await this.pkgFsExtra.readdir(oldDir);
const newFds = await this.pkgFsExtra.readdir(newDir);
const excludeFds = new Set([...newFds])
oldFds.forEach(name => {
const exclude = excludeFds.has(name) ||
(this.pkgPath.extname(name) === ".js") &&
excludeFds.has(name.substring(0, name.lastIndexOf(".")))
if (!exclude) {
const path = this.pkgPath.join(this.customPluginDir, name)
this.exclude.push(path)
}
})
// 保护用户配置文件和个人文件
for (const file of this.exclude) {
const oldPath = this.utils.joinPath(file);
const newPath = this.pkgPath.join(this.unzipDir, file);
const exists = await this.utils.existPath(oldPath);
if (exists) {
await this.pkgFsExtra.copy(oldPath, newPath);
}
}
}
保护的文件包括:
./plugin/global/user_space- 用户空间文件./plugin/global/user_styles- 用户样式文件./plugin/global/settings/settings.user.toml- 用户设置./plugin/global/settings/custom_plugin.user.toml- 自定义插件设置- 用户自定义插件目录中的所有非标准插件
配置选项详解
自动升级功能提供了丰富的配置选项:
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
AUTO_UPDATE | 布尔 | true | 是否启用自动更新 |
START_UPDATE_INTERVAL | 数字 | 60000 | 启动后首次检查更新的延迟时间(毫秒) |
UPDATE_LOOP_INTERVAL | 数字 | 3600000 | 自动检查更新的间隔时间(毫秒) |
NETWORK_REQUEST_TIMEOUT | 数字 | 30000 | 网络请求超时时间(毫秒) |
PROXY | 字符串 | "" | 网络中转服务地址 |
配置示例
[updater]
AUTO_UPDATE = true
START_UPDATE_INTERVAL = 30000 # 30秒后开始检查
UPDATE_LOOP_INTERVAL = 7200000 # 每2小时检查一次
NETWORK_REQUEST_TIMEOUT = 15000 # 15秒超时
PROXY = "http://127.0.0.1:7890" # 使用本地网络服务
网络服务支持机制
考虑到不同网络环境,升级功能内置了完善的网络服务支持:
自动网络服务检测
class ProxyGetter {
getProxy = () => {
if (File.isLinux) {
return this.getLinuxProxy()
} else if (File.isWin) {
return this.getWindowsProxy()
} else if (File.isMac) {
return this.getMacProxy()
} else {
return ""
}
}
getWindowsProxy = () => this._getProxy(
`reg query "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings" | findstr /i "ProxyEnable proxyserver"`,
stdout => {
const match = stdout.match(/ProxyEnable.+?0x(?<enable>\d)\r\n.+?ProxyServer\s+REG_SZ\s+(?<proxy>.*)/i)
return (match && match.groups && match.groups.enable === "1")
? match.groups.proxy
: null
}
)
}
手动网络服务设置
用户也可以通过界面手动设置网络服务:
call = async (action, meta) => {
if (this.config.PROXY) {
await this.manualUpdate()
return
}
const proxy = await this.getDefaultProxy()
const op = {
title: this.pluginName,
schema: [
{ fields: [{ type: "hint", hintHeader, hintDetail }] },
{ fields: [{ type: "text", key: "proxy", label, placeholder: "http://127.0.0.1:7890" }] },
],
data: { proxy },
}
const { response, data } = await this.utils.formDialog.modal(op)
if (response === 1) {
await this.manualUpdate(data.proxy)
}
}
升级模式对比
自动升级功能支持两种升级模式:
静默升级模式
silentUpdate = async proxy => {
console.log("start silent update...");
const updater = await this.getUpdater(proxy);
await updater.run();
}
特点:
- 后台自动运行
- 无用户界面干扰
- 适合定期自动更新
手动升级模式
manualUpdate = async proxy => {
const timeout = Math.max(this.config.NETWORK_REQUEST_TIMEOUT, 30 * 1000)
const close = this.utils.notification.show(I18N.pleaseWait)
const updater = await this.getUpdater(proxy, timeout)
const { state, info } = await updater.runWithProgressBar(timeout)
close()
// 显示升级结果
let msg, msgType, detail
if (state === "UPDATED") {
msg = I18N.success
msgType = "success"
detail = JSON.stringify(info, null, "\t")
} else if (state === "NO_NEED") {
msg = I18N.noNeed
msgType = "success"
} else {
msg = I18N.failed
msgType = "error"
detail = state.stack || I18N.unknownError
}
this.utils.notification.show(msg, msgType, 10000)
}
特点:
- 显示进度条
- 提供详细的升级结果
- 支持用户交互
错误处理与恢复机制
升级功能具备完善的错误处理:
网络错误处理
try {
const resp = await this.utils.fetch(url, this.requestOption);
return resp.buffer()
} catch (error) {
console.error("Download failed:", error);
throw new Error(`Download failed: ${error.message}`);
}
文件操作错误处理
syncDir = async () => {
console.log("[6/6] sync dir");
const src = this.pkgPath.join(this.unzipDir, this.pluginDir);
const dst = this.utils.joinPath(this.pluginDir);
await this.pkgFsExtra.emptyDir(dst);
await this.pkgFsExtra.copy(src, dst);
await this.pkgFsExtra.emptyDir(this.workDir);
if (this.latestVersionInfo) {
await this.pkgFsExtra.writeJson(this.versionFile, this.latestVersionInfo);
}
}
状态码说明
| 状态码 | 含义 | 处理方式 |
|---|---|---|
UPDATED | 升级成功 | 显示成功信息,更新版本文件 |
NO_NEED | 无需升级 | 显示当前已是最新版本 |
Error | 升级失败 | 显示错误详情,保留原有文件 |
最佳实践指南
1. 配置建议
# 推荐配置
[updater]
AUTO_UPDATE = true
START_UPDATE_INTERVAL = 60000 # 1分钟后开始检查
UPDATE_LOOP_INTERVAL = 86400000 # 每天检查一次
NETWORK_REQUEST_TIMEOUT = 10000 # 10秒超时
2. 网络环境优化
如果遇到下载速度慢的问题,可以:
- 设置网络服务:在插件配置中设置合适的网络服务地址
- 调整超时时间:根据网络状况适当增加超时时间
- 手动下载:如果自动升级失败,可以从发布页面手动下载
3. 故障排除
常见问题及解决方案:
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 升级失败 | 网络连接问题 | 检查网络设置,配置网络服务 |
| 文件权限错误 | 目录权限不足 | 以管理员权限运行Typora |
| 版本冲突 | 自定义插件冲突 | 检查自定义插件兼容性 |
技术亮点总结
- 智能版本检测:基于GitHub Release API的版本比较机制
- 安全升级:完善的用户文件保护策略,确保自定义配置不丢失
- 网络适应性:内置网络服务支持和超时机制,适应各种网络环境
- 用户体验优化:提供静默和手动两种升级模式,满足不同需求
- 错误恢复:完善的错误处理机制,确保升级失败时系统稳定性
通过这套自动升级系统,Typora插件项目为用户提供了安全、便捷、可靠的升级体验,大大降低了维护成本,让用户能够专注于创作本身。
未来展望
随着项目的不断发展,自动升级功能还将继续优化:
- 增量更新:支持差分更新,减少下载流量
- 多源支持:除了GitHub,支持更多更新源
- 回滚机制:提供版本回滚功能
- 更新预览:支持查看更新日志和变更内容
自动升级功能是Typora插件项目持续进化的重要保障,它让插件生态保持活力,为用户带来更好的使用体验。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



