mirror of
https://github.com/Tthfyth/source.git
synced 2026-03-16 14:23:19 +08:00
集成自动更新
This commit is contained in:
188
.github/workflows/publish.yml
vendored
188
.github/workflows/publish.yml
vendored
@@ -1,47 +1,167 @@
|
||||
name: Publish
|
||||
name: Build and Release
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Release version (e.g., 1.0.0)'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
# To enable auto publishing to github, update your electron publisher
|
||||
# config in package.json > "build" and remove the conditional below
|
||||
if: ${{ github.repository_owner == 'electron-react-boilerplate' }}
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest]
|
||||
|
||||
# 构建 Windows 版本
|
||||
build-windows:
|
||||
runs-on: windows-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout git repo
|
||||
uses: actions/checkout@v3
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node and NPM
|
||||
uses: actions/setup-node@v3
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
version: 8
|
||||
|
||||
- name: Install and build
|
||||
run: |
|
||||
npm install
|
||||
npm run postinstall
|
||||
npm run build
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Publish releases
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build application
|
||||
run: pnpm run build
|
||||
|
||||
- name: Package for Windows
|
||||
run: pnpm exec electron-builder --win --publish never
|
||||
|
||||
- name: Upload Windows artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: windows-build
|
||||
path: |
|
||||
release/build/*.exe
|
||||
release/build/*.exe.blockmap
|
||||
retention-days: 5
|
||||
|
||||
# 构建 macOS 版本
|
||||
build-macos:
|
||||
runs-on: macos-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build application
|
||||
run: pnpm run build
|
||||
|
||||
- name: Package for macOS
|
||||
env:
|
||||
# The APPLE_* values are used for auto updates signing
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASS }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
# macOS 签名(可选)
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
# This is used for uploading release assets to github
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
npm exec electron-builder -- --publish always --win --mac --linux
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_ID_PASS }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
run: pnpm exec electron-builder --mac --publish never
|
||||
|
||||
- name: Upload macOS artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: macos-build
|
||||
path: |
|
||||
release/build/*.dmg
|
||||
release/build/*.dmg.blockmap
|
||||
release/build/*.zip
|
||||
retention-days: 5
|
||||
|
||||
# 构建 Linux 版本
|
||||
build-linux:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build application
|
||||
run: pnpm run build
|
||||
|
||||
- name: Package for Linux
|
||||
run: pnpm exec electron-builder --linux --publish never
|
||||
|
||||
- name: Upload Linux artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: linux-build
|
||||
path: |
|
||||
release/build/*.AppImage
|
||||
release/build/*.deb
|
||||
release/build/*.rpm
|
||||
retention-days: 5
|
||||
|
||||
# 发布 Release
|
||||
release:
|
||||
needs: [build-windows, build-macos, build-linux]
|
||||
runs-on: ubuntu-latest
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
path: artifacts
|
||||
|
||||
- name: Display structure of downloaded files
|
||||
run: ls -R artifacts
|
||||
|
||||
- name: Create Release
|
||||
uses: softprops/action-gh-release@v1
|
||||
with:
|
||||
files: |
|
||||
artifacts/windows-build/*
|
||||
artifacts/macos-build/*
|
||||
artifacts/linux-build/*
|
||||
draft: false
|
||||
prerelease: false
|
||||
generate_release_notes: true
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
80
.github/workflows/test.yml
vendored
80
.github/workflows/test.yml
vendored
@@ -1,34 +1,74 @@
|
||||
name: Test
|
||||
name: CI
|
||||
|
||||
on: [push, pull_request]
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop]
|
||||
pull_request:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
version: 8
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Run ESLint
|
||||
run: pnpm run lint
|
||||
|
||||
- name: TypeScript check
|
||||
run: pnpm exec tsc --noEmit
|
||||
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
needs: lint
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
os: [macos-latest, windows-latest, ubuntu-latest]
|
||||
os: [windows-latest, macos-latest, ubuntu-latest]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- name: Check out Git repository
|
||||
uses: actions/checkout@v3
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Node.js and NPM
|
||||
uses: actions/setup-node@v3
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v2
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
version: 8
|
||||
|
||||
- name: npm install
|
||||
run: |
|
||||
npm install
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: npm test
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Build application
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
npm run package
|
||||
npm run lint
|
||||
npm exec tsc
|
||||
npm test
|
||||
run: pnpm run build
|
||||
|
||||
- name: Run tests
|
||||
run: pnpm test
|
||||
|
||||
- name: Package application
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: pnpm run package
|
||||
|
||||
22
package.json
22
package.json
@@ -12,13 +12,13 @@
|
||||
"hot",
|
||||
"reload"
|
||||
],
|
||||
"homepage": "https://github.com/electron-react-boilerplate/electron-react-boilerplate#readme",
|
||||
"homepage": "https://github.com/Tthfyth/source#readme",
|
||||
"bugs": {
|
||||
"url": "https://github.com/electron-react-boilerplate/electron-react-boilerplate/issues"
|
||||
"url": "https://github.com/Tthfyth/source/issues"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/electron-react-boilerplate/electron-react-boilerplate.git"
|
||||
"url": "git+https://github.com/Tthfyth/source.git"
|
||||
},
|
||||
"license": "MIT",
|
||||
"author": {
|
||||
@@ -204,8 +204,8 @@
|
||||
"webpack-merge": "^6.0.1"
|
||||
},
|
||||
"build": {
|
||||
"productName": "ElectronReact",
|
||||
"appId": "org.erb.ElectronReact",
|
||||
"productName": "SourceDebug",
|
||||
"appId": "com.aerowang.sourcedebug",
|
||||
"asar": true,
|
||||
"afterSign": ".erb/scripts/notarize.js",
|
||||
"asarUnpack": "**\\*.{node,dll}",
|
||||
@@ -264,8 +264,9 @@
|
||||
],
|
||||
"publish": {
|
||||
"provider": "github",
|
||||
"owner": "electron-react-boilerplate",
|
||||
"repo": "electron-react-boilerplate"
|
||||
"owner": "Tthfyth",
|
||||
"repo": "source",
|
||||
"releaseType": "release"
|
||||
}
|
||||
},
|
||||
"collective": {
|
||||
@@ -274,12 +275,7 @@
|
||||
"devEngines": {
|
||||
"runtime": {
|
||||
"name": "node",
|
||||
"version": ">=14.x",
|
||||
"onFail": "error"
|
||||
},
|
||||
"packageManager": {
|
||||
"name": "npm",
|
||||
"version": ">=7.x",
|
||||
"version": ">=18.x",
|
||||
"onFail": "error"
|
||||
}
|
||||
},
|
||||
|
||||
124
src/main/main.ts
124
src/main/main.ts
@@ -22,7 +22,78 @@ class AppUpdater {
|
||||
constructor() {
|
||||
log.transports.file.level = 'info';
|
||||
autoUpdater.logger = log;
|
||||
autoUpdater.checkForUpdatesAndNotify();
|
||||
|
||||
// 配置自动更新
|
||||
autoUpdater.autoDownload = false;
|
||||
autoUpdater.autoInstallOnAppQuit = true;
|
||||
|
||||
// 检查更新事件
|
||||
autoUpdater.on('checking-for-update', () => {
|
||||
log.info('正在检查更新...');
|
||||
this.sendStatusToWindow('checking-for-update');
|
||||
});
|
||||
|
||||
autoUpdater.on('update-available', (info) => {
|
||||
log.info('发现新版本:', info.version);
|
||||
this.sendStatusToWindow('update-available', info);
|
||||
// 显示更新对话框
|
||||
if (mainWindow) {
|
||||
dialog.showMessageBox(mainWindow, {
|
||||
type: 'info',
|
||||
title: '发现新版本',
|
||||
message: `发现新版本 ${info.version},是否立即下载?`,
|
||||
buttons: ['立即下载', '稍后提醒'],
|
||||
defaultId: 0,
|
||||
}).then(({ response }) => {
|
||||
if (response === 0) {
|
||||
autoUpdater.downloadUpdate();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
autoUpdater.on('update-not-available', (info) => {
|
||||
log.info('当前已是最新版本');
|
||||
this.sendStatusToWindow('update-not-available', info);
|
||||
});
|
||||
|
||||
autoUpdater.on('error', (err) => {
|
||||
log.error('更新错误:', err);
|
||||
this.sendStatusToWindow('error', err.message);
|
||||
});
|
||||
|
||||
autoUpdater.on('download-progress', (progressObj) => {
|
||||
log.info(`下载进度: ${progressObj.percent.toFixed(1)}%`);
|
||||
this.sendStatusToWindow('download-progress', progressObj);
|
||||
});
|
||||
|
||||
autoUpdater.on('update-downloaded', (info) => {
|
||||
log.info('更新下载完成:', info.version);
|
||||
this.sendStatusToWindow('update-downloaded', info);
|
||||
// 显示安装对话框
|
||||
if (mainWindow) {
|
||||
dialog.showMessageBox(mainWindow, {
|
||||
type: 'info',
|
||||
title: '更新已就绪',
|
||||
message: `新版本 ${info.version} 已下载完成,是否立即安装并重启?`,
|
||||
buttons: ['立即安装', '稍后安装'],
|
||||
defaultId: 0,
|
||||
}).then(({ response }) => {
|
||||
if (response === 0) {
|
||||
autoUpdater.quitAndInstall();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 启动时检查更新
|
||||
autoUpdater.checkForUpdates();
|
||||
}
|
||||
|
||||
sendStatusToWindow(status: string, data?: any) {
|
||||
if (mainWindow) {
|
||||
mainWindow.webContents.send('update-status', { status, data });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,6 +322,57 @@ ipcMain.handle('file:openFile', async () => {
|
||||
}
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// IPC 通信接口 - 应用更新
|
||||
// ============================================
|
||||
|
||||
/**
|
||||
* 手动检查更新
|
||||
*/
|
||||
ipcMain.handle('app:checkForUpdates', async () => {
|
||||
try {
|
||||
const result = await autoUpdater.checkForUpdates();
|
||||
return {
|
||||
success: true,
|
||||
updateInfo: result?.updateInfo,
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || String(error),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 下载更新
|
||||
*/
|
||||
ipcMain.handle('app:downloadUpdate', async () => {
|
||||
try {
|
||||
await autoUpdater.downloadUpdate();
|
||||
return { success: true };
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || String(error),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 安装更新并重启
|
||||
*/
|
||||
ipcMain.handle('app:quitAndInstall', () => {
|
||||
autoUpdater.quitAndInstall();
|
||||
});
|
||||
|
||||
/**
|
||||
* 获取应用版本
|
||||
*/
|
||||
ipcMain.handle('app:getVersion', () => {
|
||||
return app.getVersion();
|
||||
});
|
||||
|
||||
// ============================================
|
||||
// IPC 通信接口 - AI 服务
|
||||
// ============================================
|
||||
|
||||
@@ -192,6 +192,53 @@ const aiApiHandler = {
|
||||
|
||||
contextBridge.exposeInMainWorld('aiApi', aiApiHandler);
|
||||
|
||||
// --------- 应用更新 API ---------
|
||||
const appApiHandler = {
|
||||
/**
|
||||
* 获取应用版本
|
||||
*/
|
||||
getVersion: () => {
|
||||
return ipcRenderer.invoke('app:getVersion');
|
||||
},
|
||||
|
||||
/**
|
||||
* 检查更新
|
||||
*/
|
||||
checkForUpdates: () => {
|
||||
return ipcRenderer.invoke('app:checkForUpdates');
|
||||
},
|
||||
|
||||
/**
|
||||
* 下载更新
|
||||
*/
|
||||
downloadUpdate: () => {
|
||||
return ipcRenderer.invoke('app:downloadUpdate');
|
||||
},
|
||||
|
||||
/**
|
||||
* 安装更新并重启
|
||||
*/
|
||||
quitAndInstall: () => {
|
||||
return ipcRenderer.invoke('app:quitAndInstall');
|
||||
},
|
||||
|
||||
/**
|
||||
* 监听更新状态
|
||||
*/
|
||||
onUpdateStatus: (callback: (status: { status: string; data?: any }) => void) => {
|
||||
const subscription = (_event: IpcRendererEvent, data: { status: string; data?: any }) => {
|
||||
callback(data);
|
||||
};
|
||||
ipcRenderer.on('update-status', subscription);
|
||||
return () => {
|
||||
ipcRenderer.removeListener('update-status', subscription);
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
contextBridge.exposeInMainWorld('appApi', appApiHandler);
|
||||
|
||||
export type ElectronHandler = typeof electronHandler;
|
||||
export type DebugApiHandler = typeof debugApiHandler;
|
||||
export type AIApiHandler = typeof aiApiHandler;
|
||||
export type AppApiHandler = typeof appApiHandler;
|
||||
|
||||
Reference in New Issue
Block a user