Skip to content

Commit 4b25a84

Browse files
feat:add github release automation and app updater
1 parent eec7235 commit 4b25a84

21 files changed

Lines changed: 1075 additions & 6 deletions

.github/workflows/ci.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: ci
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- main
8+
9+
jobs:
10+
verify:
11+
runs-on: macos-latest
12+
13+
steps:
14+
- uses: actions/checkout@v4
15+
16+
- name: Setup Node.js
17+
uses: actions/setup-node@v4
18+
with:
19+
node-version: 22
20+
cache: npm
21+
22+
- name: Setup Rust
23+
uses: dtolnay/rust-toolchain@stable
24+
25+
- name: Cache Rust artifacts
26+
uses: swatinem/rust-cache@v2
27+
with:
28+
workspaces: src-tauri -> target
29+
30+
- name: Install dependencies
31+
run: npm ci
32+
33+
- name: Check version sync
34+
run: node scripts/ci/check-version-sync.mjs
35+
36+
- name: Run frontend tests
37+
run: node --test tests/*.test.ts
38+
39+
- name: Run Rust tests
40+
run: cargo test --manifest-path src-tauri/Cargo.toml
41+
42+
- name: Build frontend
43+
run: npm run build

.github/workflows/release.yml

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
name: release
2+
3+
on:
4+
workflow_dispatch:
5+
push:
6+
branches:
7+
- main
8+
9+
concurrency:
10+
group: release-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
preflight:
15+
runs-on: macos-latest
16+
outputs:
17+
version: ${{ steps.app_version.outputs.version }}
18+
19+
steps:
20+
- uses: actions/checkout@v4
21+
22+
- name: Setup Node.js
23+
uses: actions/setup-node@v4
24+
with:
25+
node-version: 22
26+
cache: npm
27+
28+
- name: Setup Rust
29+
uses: dtolnay/rust-toolchain@stable
30+
31+
- name: Cache Rust artifacts
32+
uses: swatinem/rust-cache@v2
33+
with:
34+
workspaces: src-tauri -> target
35+
36+
- name: Install dependencies
37+
run: npm ci
38+
39+
- name: Check version sync
40+
run: node scripts/ci/check-version-sync.mjs
41+
42+
- id: app_version
43+
name: Read app version
44+
run: echo "version=$(node scripts/ci/read-app-version.mjs)" >> "$GITHUB_OUTPUT"
45+
46+
- name: Ensure release version is new
47+
env:
48+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
49+
APP_VERSION: ${{ steps.app_version.outputs.version }}
50+
run: |
51+
if gh release view "v${APP_VERSION}" --repo "${GITHUB_REPOSITORY}" >/dev/null 2>&1; then
52+
echo "::error title=Version already released::v${APP_VERSION} 已存在。请先递增 package.json、src-tauri/Cargo.toml 和 src-tauri/tauri.conf.json 的版本号。"
53+
exit 1
54+
fi
55+
56+
- name: Run frontend tests
57+
run: node --test tests/*.test.ts
58+
59+
- name: Run Rust tests
60+
run: cargo test --manifest-path src-tauri/Cargo.toml
61+
62+
- name: Build frontend
63+
run: npm run build
64+
65+
publish:
66+
needs: preflight
67+
permissions:
68+
contents: write
69+
strategy:
70+
fail-fast: false
71+
matrix:
72+
include:
73+
- target: aarch64-apple-darwin
74+
- target: x86_64-apple-darwin
75+
runs-on: macos-latest
76+
77+
steps:
78+
- uses: actions/checkout@v4
79+
80+
- name: Setup Node.js
81+
uses: actions/setup-node@v4
82+
with:
83+
node-version: 22
84+
cache: npm
85+
86+
- name: Setup Rust
87+
uses: dtolnay/rust-toolchain@stable
88+
with:
89+
targets: aarch64-apple-darwin,x86_64-apple-darwin
90+
91+
- name: Cache Rust artifacts
92+
uses: swatinem/rust-cache@v2
93+
with:
94+
workspaces: src-tauri -> target
95+
96+
- name: Install dependencies
97+
run: npm ci
98+
99+
- uses: tauri-apps/tauri-action@v1
100+
env:
101+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
102+
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
103+
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
104+
with:
105+
tagName: v__VERSION__
106+
releaseName: AI Usage v__VERSION__
107+
releaseBody: |
108+
Automated release for AI Usage.
109+
110+
See the attached assets for the macOS installers and updater bundles.
111+
releaseCommitish: ${{ github.sha }}
112+
releaseDraft: false
113+
prerelease: false
114+
generateReleaseNotes: true
115+
uploadUpdaterJson: true
116+
args: --target ${{ matrix.target }}

package-lock.json

Lines changed: 20 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
"dev": "vite",
88
"build": "tsc && vite build",
99
"package:mac": "sh scripts/package-app.sh",
10+
"test:node": "node --test tests/*.test.ts",
11+
"test:rust": "cargo test --manifest-path src-tauri/Cargo.toml",
12+
"version:check": "node scripts/ci/check-version-sync.mjs",
1013
"preview": "vite preview",
1114
"tauri": "tauri"
1215
},
@@ -16,6 +19,8 @@
1619
"@tauri-apps/api": "^2.8.0",
1720
"@tauri-apps/plugin-dialog": "^2.7.0",
1821
"@tauri-apps/plugin-notification": "^2.3.3",
22+
"@tauri-apps/plugin-process": "^2.3.1",
23+
"@tauri-apps/plugin-updater": "^2.10.1",
1924
"class-variance-authority": "^0.7.1",
2025
"clsx": "^2.1.1",
2126
"lucide-react": "^1.9.0",

scripts/ci/check-version-sync.mjs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import assert from "node:assert/strict";
2+
import { readFile } from "node:fs/promises";
3+
4+
const [packageJsonSource, cargoTomlSource, tauriConfigSource] = await Promise.all([
5+
readFile(new URL("../../package.json", import.meta.url), "utf8"),
6+
readFile(new URL("../../src-tauri/Cargo.toml", import.meta.url), "utf8"),
7+
readFile(new URL("../../src-tauri/tauri.conf.json", import.meta.url), "utf8"),
8+
]);
9+
10+
const packageVersion = JSON.parse(packageJsonSource).version;
11+
const cargoVersion = cargoTomlSource.match(/^version\s*=\s*"([^"]+)"/m)?.[1];
12+
const tauriVersion = JSON.parse(tauriConfigSource).version;
13+
14+
assert.equal(typeof packageVersion, "string", "package.json 缺少 version");
15+
assert.equal(typeof cargoVersion, "string", "src-tauri/Cargo.toml 缺少 version");
16+
assert.equal(typeof tauriVersion, "string", "src-tauri/tauri.conf.json 缺少 version");
17+
assert.equal(packageVersion, cargoVersion, "package.json 与 src-tauri/Cargo.toml 版本不一致");
18+
assert.equal(packageVersion, tauriVersion, "package.json 与 src-tauri/tauri.conf.json 版本不一致");
19+
20+
process.stdout.write(`Version OK: ${packageVersion}\n`);

scripts/ci/read-app-version.mjs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { readFile } from "node:fs/promises";
2+
3+
const tauriConfig = JSON.parse(await readFile(new URL("../../src-tauri/tauri.conf.json", import.meta.url), "utf8"));
4+
5+
if (typeof tauriConfig.version !== "string" || !tauriConfig.version.trim()) {
6+
throw new Error("src-tauri/tauri.conf.json 缺少合法 version");
7+
}
8+
9+
process.stdout.write(tauriConfig.version.trim());

0 commit comments

Comments
 (0)