Skip to content

Commit 7808fa9

Browse files
committed
feat: updater
1 parent b54f47a commit 7808fa9

File tree

9 files changed

+336
-7
lines changed

9 files changed

+336
-7
lines changed

.github/workflows/release.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ jobs:
7171
uses: tauri-apps/tauri-action@v0
7272
env:
7373
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
74-
# TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
75-
# TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
74+
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
75+
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
7676
with:
7777
tagName: ${{ github.ref_name }} # This only works if your workflow triggers on new tags.
7878
releaseName: "Tauri Demo v__VERSION__" # 自定义 release 名称,__VERSION__ 将自动填写为版本信息

.github/workflows/updater.yml

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
name: Updater CI
2+
run-name: Release update.json
3+
4+
on:
5+
release:
6+
types: [published]
7+
workflow_dispatch:
8+
9+
jobs:
10+
release-update:
11+
permissions:
12+
contents: write
13+
runs-on: ubuntu-latest
14+
steps:
15+
- name: Checkout repository
16+
uses: actions/checkout@v3
17+
18+
- name: Install pnpm
19+
uses: pnpm/action-setup@v2
20+
id: pnpm-install
21+
with:
22+
version: 7
23+
run_install: false
24+
25+
- name: Get pnpm store directory
26+
id: pnpm-cache
27+
shell: bash
28+
run: |
29+
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
30+
31+
- uses: actions/cache@v3
32+
name: Setup pnpm cache
33+
with:
34+
path: ${{ steps.pnpm-cache.outputs.STORE_PATH }}
35+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
36+
restore-keys: |
37+
${{ runner.os }}-pnpm-store-
38+
39+
- name: Install frontend dependencies
40+
run: pnpm install
41+
42+
- name: Release updater file
43+
run: pnpm run updater
44+
env:
45+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

README.md

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,31 @@
1-
#tauri-demo
1+
# tauri-demo
2+
23
一个 tauri 的 demo,来探索 tauri 的各种功能
34

4-
1. 应用的构建
5-
使用 github action 来进行跨平台的编译与构建, 详细见 '.github/workflows/.release.yml' 文件
5+
## 各种 feature
6+
7+
### 开发模式下忽略文件
8+
9+
开发模式下,**src-tauri** 目录下的任何文件变更都会引起应用的重新构建,**@tauri-apps/cli** v1.1.0 版本以上, 支持了两种忽略方式,这里使用其中一种, **--no-watch**, 即运行时加上 --no-watch 的参数,例如 **cargo tauri dev --no-watch** 或者 **pnpm tauri dev --no-watch**.
10+
11+
### 应用的构建
12+
13+
使用 github action 来进行跨平台的编译与构建, 详细见 **.github/workflows/.release.yml** 文件
14+
15+
todo
16+
17+
- [ ] relase 出来的包名不太理想,希望可以带平台名称
18+
- [ ] github action 感觉速度偏慢, 希望找到更好的支持跨平台编译打包的 ci 方案
19+
20+
### 应用的更新
21+
22+
> Tauri offers a built-in updater for the MSI (Windows), AppImage (Linux) and App bundle (macOS) distribution formats.
23+
24+
Tauri为MSI(Windows)、AppImage(Linux)和App bundle(macOS)发布格式提供了一个内置的更新器。
25+
26+
内置的更新器有一个前面机制来保证安全更新应用。
27+
生成密码
28+
29+
```
30+
pnpm tauri signer generate -w ~/.tauri/tauri-demo.key
31+
```

UPDATELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
## v0.0.6
2+
3+
### Features
4+
5+
- some feature in v0.0.6
6+
7+
## v0.0.1
8+
9+
### Features
10+
11+
- some feature
12+
- another feature
13+
14+
### Bug Fixes
15+
16+
- some bugs
17+
- another bugs

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
"dev": "vite",
88
"build": "vue-tsc --noEmit && vite build",
99
"preview": "vite preview",
10-
"tauri": "tauri",
10+
"tauri": "tauri --no-watch",
11+
"updater": "node scripts/updater.mjs",
12+
"publish": "node scripts/publish.mjs",
1113
"test": "vitest",
1214
"test-once": "npx vitest run"
1315
},
@@ -16,10 +18,13 @@
1618
"vue": "^3.2.45"
1719
},
1820
"devDependencies": {
21+
"@actions/github": "^5.1.1",
1922
"@tauri-apps/cli": "^1.2.3",
2023
"@types/node": "^18.7.10",
2124
"@vitejs/plugin-vue": "^4.0.0",
2225
"@vitest/ui": "^0.30.1",
26+
"fs-extra": "^11.1.1",
27+
"node-fetch": "^3.3.1",
2328
"typescript": "^4.9.5",
2429
"vite": "^4.2.1",
2530
"vitest": "^0.30.1",

scripts/publish.mjs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import fs from "fs-extra";
2+
import { createRequire } from "module";
3+
import { execSync } from "child_process";
4+
import { resolveUpdateLog } from "./updatelog.mjs";
5+
6+
const require = createRequire(import.meta.url);
7+
8+
// 自动更新版本,添加对应 tag 并推送至仓库
9+
async function resolvePublish() {
10+
const flag = process.argv[2] ?? "patch";
11+
const packageJson = require("../package.json");
12+
const tauriJson = require("../src-tauri/tauri.conf.json");
13+
14+
let [a, b, c] = packageJson.version.split(".").map(Number);
15+
16+
if (flag === "major") {
17+
a += 1;
18+
b = 0;
19+
c = 0;
20+
} else if (flag === "minor") {
21+
b += 1;
22+
c = 0;
23+
} else if (flag === "patch") {
24+
c += 1;
25+
} else throw new Error(`invalid flag "${flag}"`);
26+
27+
const nextVersion = `${a}.${b}.${c}`;
28+
packageJson.version = nextVersion;
29+
tauriJson.package.version = nextVersion;
30+
31+
// 发布更新前先写更新日志
32+
const nextTag = `v${nextVersion}`;
33+
await resolveUpdateLog(nextTag);
34+
35+
await fs.writeFile(
36+
"./package.json",
37+
JSON.stringify(packageJson, undefined, 2)
38+
);
39+
await fs.writeFile(
40+
"./src-tauri/tauri.conf.json",
41+
JSON.stringify(tauriJson, undefined, 2)
42+
);
43+
44+
execSync("git add ./package.json");
45+
execSync("git add ./src-tauri/tauri.conf.json");
46+
execSync(`git commit -m "release v${nextVersion}"`);
47+
execSync(`git tag -a v${nextVersion} -m "v${nextVersion}"`);
48+
execSync(`git push`);
49+
execSync(`git push origin v${nextVersion}`);
50+
console.log(`Publish Successfully...`);
51+
}
52+
53+
resolvePublish();

scripts/update.mjs

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import fetch from "node-fetch";
2+
import { getOctokit, context } from "@actions/github";
3+
import { resolveUpdateLog } from "./updatelog.mjs";
4+
5+
const UPDATE_TAG_NAME = "updater";
6+
const UPDATE_JSON_FILE = "update.json";
7+
8+
/// 生成 update.json 文件并更新 github updater release 中的文件
9+
async function resolveUpdater() {
10+
if (process.env.GITHUB_TOKEN === undefined) {
11+
throw new Error("GITHUB_TOKEN is required");
12+
}
13+
14+
const options = { owner: context.repo.owner, repo: context.repo.repo };
15+
const github = getOctokit(process.env.GITHUB_TOKEN);
16+
17+
const { data: tags } = await github.rest.repos.listTags({
18+
...options,
19+
per_page: 10,
20+
page: 1,
21+
});
22+
23+
const tag = tags.find((t) => t.name.startsWith("v"));
24+
25+
console.log(tag);
26+
27+
const { data: latestRelease } = await github.rest.repos.getReleaseByTag({
28+
...options,
29+
tag: tag.name,
30+
});
31+
32+
// 根据需要选择需更新的平台,应与编译脚本平台选择对应
33+
const updateData = {
34+
version: tag.name,
35+
notes: await resolveUpdateLog(tag.name),
36+
pub_date: new Date().toISOString(),
37+
platforms: {
38+
// comment out as needed
39+
"windows-x86_64": { signature: "", url: "" },
40+
// "darwin-aarch64": { signature: "", url: "" },
41+
"darwin-x86_64": { signature: "", url: "" },
42+
"linux-x86_64": { signature: "", url: "" },
43+
},
44+
};
45+
46+
const promises = latestRelease.assets.map(async (asset) => {
47+
const { name, browser_download_url } = asset;
48+
49+
// windows-x86_64 url
50+
if (name.endsWith(".msi.zip")) {
51+
updateData.platforms["windows-x86_64"].url = browser_download_url;
52+
}
53+
54+
// windows-x86_64 signature
55+
if (name.endsWith(".msi.zip.sig")) {
56+
const sig = await getSignature(browser_download_url);
57+
updateData.platforms["windows-x86_64"].signature = sig;
58+
}
59+
60+
// darwin-x86_64 url (macos intel)
61+
if (name.endsWith(".app.tar.gz") && !name.includes("aarch")) {
62+
updateData.platforms["darwin-x86_64"].url = browser_download_url;
63+
}
64+
// darwin-x86_64 signature (macos intel)
65+
if (name.endsWith(".app.tar.gz.sig") && !name.includes("aarch")) {
66+
const sig = await getSignature(browser_download_url);
67+
updateData.platforms["darwin-x86_64"].signature = sig;
68+
}
69+
70+
// darwin-aarch64 url (macos silicon)
71+
if (name.endsWith("aarch64.app.tar.gz")) {
72+
updateData.platforms["darwin-aarch64"].url = browser_download_url;
73+
}
74+
75+
// darwin-aarch64 signature (macos silicon)
76+
if (name.endsWith("aarch64.app.tar.gz.sig")) {
77+
const sig = await getSignature(browser_download_url);
78+
updateData.platforms["darwin-aarch64"].signature = sig;
79+
}
80+
81+
// linux-x86_64 url
82+
if (name.endsWith(".AppImage.tar.gz")) {
83+
updateData.platforms["linux-x86_64"].url = browser_download_url;
84+
}
85+
// linux-x86_64 signature
86+
if (name.endsWith(".AppImage.tar.gz.sig")) {
87+
const sig = await getSignature(browser_download_url);
88+
updateData.platforms["linux-x86_64"].signature = sig;
89+
}
90+
});
91+
92+
await Promise.allSettled(promises);
93+
console.log(updateData);
94+
95+
Object.entries(updateData.platforms).forEach(([key, value]) => {
96+
if (!value.url) {
97+
console.log(`[Error]: failed to parse release for "${key}"`);
98+
delete updateData.platforms[key];
99+
}
100+
});
101+
102+
// 更新 update.json 文件
103+
const { data: updateRelease } = await github.rest.repos.getReleaseByTag({
104+
...options,
105+
tag: UPDATE_TAG_NAME,
106+
});
107+
108+
for (let asset of updateRelease.assets) {
109+
if (asset.name === UPDATE_JSON_FILE) {
110+
await github.rest.repos.deleteReleaseAsset({
111+
...options,
112+
asset_id: asset.id,
113+
});
114+
}
115+
}
116+
117+
await github.rest.repos.uploadReleaseAsset({
118+
...options,
119+
release_id: updateRelease.id,
120+
name: UPDATE_JSON_FILE,
121+
data: JSON.stringify(updateData, null, 2),
122+
});
123+
}
124+
125+
async function getSignature(url) {
126+
const response = await fetch(url, {
127+
method: "GET",
128+
headers: { "Content-Type": "application/octet-stream" },
129+
});
130+
131+
return response.text();
132+
}
133+
134+
resolveUpdater().catch(console.error);

scripts/updatelog.mjs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import fs from "fs-extra";
2+
import path from "path";
3+
4+
const UPDATE_LOG = "UPDATELOG.md";
5+
6+
// 解析 UPDATELOG.md 文件,获取更新信息
7+
export async function resolveUpdateLog(tag) {
8+
const cwd = process.cwd();
9+
10+
const reTitle = /^## v[\d\.]+/;
11+
const reEnd = /^---/;
12+
13+
const file = path.join(cwd, UPDATE_LOG);
14+
15+
if (!(await fs.pathExists(file))) {
16+
throw new Error("could not found UPDATELOG.md");
17+
}
18+
19+
const data = await fs.readFile(file).then((d) => d.toString("utf8"));
20+
21+
const map = {};
22+
let p = "";
23+
24+
data.split("\n").forEach((line) => {
25+
if (reTitle.test(line)) {
26+
p = line.slice(3).trim();
27+
if (!map[p]) {
28+
map[p] = [];
29+
} else {
30+
throw new Error(`Tag ${p} dup`);
31+
}
32+
} else if (reEnd.test(line)) {
33+
p = "";
34+
} else if (p) {
35+
map[p].push(line);
36+
}
37+
});
38+
39+
if (!map[tag]) {
40+
throw new Error(`could not found "${tag}" in UPDATELOG.md`);
41+
}
42+
43+
return map[tag].join("\n").trim();
44+
}

src-tauri/tauri.conf.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,12 @@
3838
"csp": null
3939
},
4040
"updater": {
41-
"active": false
41+
"active": true,
42+
"dialog": true,
43+
"endpoints": [
44+
"https://github.com/screw-coding/tauri-app/releases/latest"
45+
],
46+
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDY4NEZCOUJFQkMxNzE2QzEKUldUQkZoZTh2cmxQYUJ4bjNFWk9xeGJwRHdqbERVVDhqaFRQSzZMN3VLSmtuNDh6RXFQRUZ1YUsK"
4247
},
4348
"windows": [
4449
{

0 commit comments

Comments
 (0)