feat: add desktop updater and release automation
This commit is contained in:
@@ -16,6 +16,7 @@
|
||||
- 桌面工作台通过 Tauri IPC 直接调用进程内 `vnt-core`,不监听本地 API 端口
|
||||
- 可选 Web 访问:启停、端口、本机/局域网监听范围、访问令牌、打开浏览器
|
||||
- Web API 强制使用 Bearer 令牌鉴权,令牌可在桌面端重新生成
|
||||
- 关于页提供 GitHub 开源地址与更新检查;桌面端可通过 Tauri Updater 下载并安装更新
|
||||
|
||||
桌面数据存放在系统应用数据目录的 `com.vnt.desktop` 下,包括 `vnt_config`、自启动记录、`web_access.toml`、日志及 Windows 下的 `wintun.dll`。
|
||||
|
||||
@@ -45,11 +46,25 @@ cargo check -p vnt-desktop
|
||||
## 打包
|
||||
|
||||
```powershell
|
||||
$env:TAURI_SIGNING_PRIVATE_KEY=Get-Content -Raw -LiteralPath "$HOME\.tauri\vnt-desktop.key"
|
||||
$env:TAURI_SIGNING_PRIVATE_KEY_PASSWORD=""
|
||||
pnpm build:desktop
|
||||
```
|
||||
|
||||
Windows 使用虚拟网卡模式时,可能需要以管理员身份运行。
|
||||
|
||||
### 发布桌面更新
|
||||
|
||||
Tauri Updater 会从以下 GitHub Release 资源检查更新:
|
||||
|
||||
```text
|
||||
https://github.com/vnt-dev/vnt/releases/latest/download/latest.json
|
||||
```
|
||||
|
||||
更新包必须使用与 `tauri.conf.json` 内公钥匹配的私钥签名。当前密钥默认保存在 `~/.tauri/vnt-desktop.key`,请妥善备份并在 CI 中将私钥内容配置为 `TAURI_SIGNING_PRIVATE_KEY`;私钥不得提交到仓库。发布时需将 Tauri 生成的安装包、`.sig` 文件以及对应的 `latest.json` 一并上传到 GitHub Release。
|
||||
|
||||
仓库的 Release 流水线会在推送版本标签后自动构建 Windows 桌面安装包及 updater 元数据。请在 GitHub Actions Secrets 中配置 `TAURI_SIGNING_PRIVATE_KEY`;如果私钥设置了密码,同时配置 `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`。流水线还会将桌面安装包和各平台 VNT 工具包的直接下载链接写入 Release Notes。
|
||||
|
||||
## 应用图标
|
||||
|
||||
图标母版保存在 `vnt-desktop/assets/vnt-icon-master.png`。需要重新生成各平台图标时,在仓库根目录执行:
|
||||
|
||||
@@ -10,6 +10,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@tauri-apps/api": "^2.8.0",
|
||||
"@tauri-apps/plugin-process": "^2.3.1",
|
||||
"@tauri-apps/plugin-updater": "^2.10.1",
|
||||
"pinia": "catalog:",
|
||||
"vue": "catalog:",
|
||||
"vue-router": "catalog:"
|
||||
|
||||
@@ -24,3 +24,5 @@ serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
toml.workspace = true
|
||||
anyhow.workspace = true
|
||||
tauri-plugin-updater = "2"
|
||||
tauri-plugin-process = "2"
|
||||
|
||||
@@ -11,6 +11,8 @@
|
||||
"core:window:allow-unmaximize",
|
||||
"core:window:allow-is-maximized",
|
||||
"core:window:allow-start-dragging",
|
||||
"opener:allow-open-url"
|
||||
"opener:allow-open-url",
|
||||
"process:allow-restart",
|
||||
"updater:default"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -245,6 +245,8 @@ pub fn run() {
|
||||
open_web_url
|
||||
])
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_process::init())
|
||||
.plugin(tauri_plugin_updater::Builder::new().build())
|
||||
.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| {
|
||||
show_main_window(app);
|
||||
}))
|
||||
@@ -260,7 +262,7 @@ pub fn run() {
|
||||
let config_path = data_dir.join(WEB_ACCESS_CONFIG);
|
||||
let config = load_web_config(&config_path);
|
||||
save_web_config(&config_path, &config)?;
|
||||
let service = tauri::async_runtime::block_on(VntService::new(None))?;
|
||||
let service = tauri::async_runtime::block_on(VntService::new_desktop(None))?;
|
||||
let mut web = WebRuntime {
|
||||
config,
|
||||
cancellation: None,
|
||||
|
||||
@@ -25,11 +25,12 @@
|
||||
}
|
||||
],
|
||||
"security": {
|
||||
"csp": "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' asset: data:; connect-src 'self' ipc: http://ipc.localhost http://127.0.0.1:*"
|
||||
"csp": "default-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' asset: data:; connect-src 'self' ipc: http://ipc.localhost http://127.0.0.1:* https://api.github.com"
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"createUpdaterArtifacts": true,
|
||||
"targets": "all",
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
@@ -45,5 +46,16 @@
|
||||
"wix": { "language": "zh-CN" },
|
||||
"nsis": { "displayLanguageSelector": false }
|
||||
}
|
||||
},
|
||||
"plugins": {
|
||||
"updater": {
|
||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEE3MTQzQjc3NTJGQjBGRTEKUldUaEQvdFNkenNVcHdwd0pRNEhDV1lTdkMzTDZLa1V3WGllMDRoMmxBNDdobi8wclN1dmo3cFoK",
|
||||
"endpoints": [
|
||||
"https://github.com/vnt-dev/vnt/releases/latest/download/latest.json"
|
||||
],
|
||||
"windows": {
|
||||
"installMode": "passive"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { invoke } from "@tauri-apps/api/core";
|
||||
import { relaunch } from "@tauri-apps/plugin-process";
|
||||
import { check } from "@tauri-apps/plugin-updater";
|
||||
|
||||
let pendingUpdate = null;
|
||||
|
||||
globalThis.__VNT_DESKTOP__ = true;
|
||||
globalThis.__VNT_IPC_REQUEST__ = ({ method, path, body }) =>
|
||||
@@ -9,6 +13,32 @@ globalThis.__VNT_WEB_ACCESS__ = {
|
||||
generateToken: () => invoke("generate_web_token"),
|
||||
openUrl: (url) => invoke("open_web_url", { url }),
|
||||
};
|
||||
globalThis.__VNT_UPDATER__ = {
|
||||
check: async () => {
|
||||
pendingUpdate = await check();
|
||||
if (!pendingUpdate) return null;
|
||||
return {
|
||||
currentVersion: pendingUpdate.currentVersion,
|
||||
version: pendingUpdate.version,
|
||||
date: pendingUpdate.date || "",
|
||||
body: pendingUpdate.body || "",
|
||||
};
|
||||
},
|
||||
downloadAndInstall: async (onProgress) => {
|
||||
if (!pendingUpdate) throw new Error("请先检查更新");
|
||||
let downloaded = 0;
|
||||
let contentLength = 0;
|
||||
await pendingUpdate.downloadAndInstall((event) => {
|
||||
if (event.event === "Started") {
|
||||
contentLength = event.data.contentLength || 0;
|
||||
} else if (event.event === "Progress") {
|
||||
downloaded += event.data.chunkLength;
|
||||
}
|
||||
onProgress?.({ event: event.event, downloaded, contentLength });
|
||||
});
|
||||
await relaunch();
|
||||
},
|
||||
};
|
||||
|
||||
// Tauri 与 Web 端共用同一个 Vue 应用,这里只负责平台初始化。
|
||||
await import("@shared/main.js");
|
||||
|
||||
Reference in New Issue
Block a user