diff --git a/.github/workflows/generate-signing-key.yml b/.github/workflows/generate-signing-key.yml new file mode 100644 index 0000000..9483377 --- /dev/null +++ b/.github/workflows/generate-signing-key.yml @@ -0,0 +1,36 @@ +name: Generate Signing Key + +on: + workflow_dispatch: + +jobs: + generate: + name: Generate Tauri updater signing key + runs-on: ubuntu-latest + steps: + - name: Install Tauri CLI + run: cargo install tauri-cli --version "^2" + + - name: Generate signing key pair + run: | + cargo tauri signer generate -w ~/.tauri/rbitt.key + echo "" + echo "============================================" + echo " SETUP INSTRUCTIONS" + echo "============================================" + echo "" + echo "1. Copy the PRIVATE KEY below and add it as a GitHub repository secret:" + echo " Secret name: TAURI_SIGNING_PRIVATE_KEY" + echo "" + cat ~/.tauri/rbitt.key + echo "" + echo "" + echo "2. If you set a password, add it as another secret:" + echo " Secret name: TAURI_SIGNING_PRIVATE_KEY_PASSWORD" + echo "" + echo "3. Copy the PUBLIC KEY below and paste it into:" + echo " src-tauri/tauri.conf.json -> plugins.updater.pubkey" + echo "" + cat ~/.tauri/rbitt.key.pub + echo "" + echo "============================================" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index eeb5d8e..7088813 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,9 +55,12 @@ jobs: uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} with: tagName: ${{ github.ref_name }} releaseName: "RBitt ${{ github.ref_name }}" releaseBody: "See the assets below to download and install RBitt." - releaseDraft: true + releaseDraft: false prerelease: false + updaterJsonPreferNsis: true diff --git a/package.json b/package.json index 8b82e0f..6db3c49 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,8 @@ "@tauri-apps/plugin-dialog": "^2.4.2", "@tauri-apps/plugin-fs": "^2.4.4", "@tauri-apps/plugin-opener": "^2", + "@tauri-apps/plugin-process": "^2", + "@tauri-apps/plugin-updater": "^2", "gsap": "^3.13.0", "react": "^18.3.1", "react-dom": "^18.3.1" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 9da89ee..be21d06 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -45,6 +45,8 @@ tauri = { version = "2", features = [] } tauri-plugin-opener = "2" tauri-plugin-dialog = "2" tauri-plugin-fs = "2" +tauri-plugin-process = "2" +tauri-plugin-updater = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { workspace = true } diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 2495958..830738a 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -9,6 +9,8 @@ "opener:default", "dialog:default", "fs:default", - "fs:allow-read-file" + "fs:allow-read-file", + "process:allow-restart", + "updater:default" ] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 723ea4b..9ee4891 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1429,6 +1429,8 @@ pub fn run() { .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_fs::init()) + .plugin(tauri_plugin_process::init()) + .plugin(tauri_plugin_updater::Builder::new().build()) .manage(AppState::default()) .invoke_handler(tauri::generate_handler![ // Core diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 6cc31b1..3a7c61c 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -23,6 +23,17 @@ "csp": null } }, + "plugins": { + "updater": { + "pubkey": "", + "endpoints": [ + "https://github.com/jspwrd/rbitt/releases/latest/download/latest.json" + ], + "windows": { + "installMode": "passive" + } + } + }, "bundle": { "active": true, "targets": "all", diff --git a/src/App.tsx b/src/App.tsx index 512f6f9..9b00762 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -4,6 +4,7 @@ import { open } from "@tauri-apps/plugin-dialog"; import { readFile } from "@tauri-apps/plugin-fs"; import "./App.css"; import { useTheme } from "./hooks"; +import { checkForUpdates } from "./updater"; import { Icons, @@ -107,6 +108,10 @@ function App() { autoInit(); }, []); + useEffect(() => { + checkForUpdates(); + }, []); + const refreshTorrents = useCallback(async () => { if (!engineInitialized) return; try { diff --git a/src/updater.ts b/src/updater.ts new file mode 100644 index 0000000..22ad844 --- /dev/null +++ b/src/updater.ts @@ -0,0 +1,38 @@ +import { check } from "@tauri-apps/plugin-updater"; +import { relaunch } from "@tauri-apps/plugin-process"; + +export async function checkForUpdates(interactive: boolean = false) { + try { + const update = await check(); + if (update) { + console.log( + `Update available: ${update.version} (current date: ${update.date})` + ); + + let downloaded = 0; + let contentLength = 0; + + await update.downloadAndInstall((event) => { + switch (event.event) { + case "Started": + contentLength = event.data.contentLength ?? 0; + console.log(`Download started, size: ${contentLength}`); + break; + case "Progress": + downloaded += event.data.chunkLength; + console.log(`Downloaded ${downloaded}/${contentLength}`); + break; + case "Finished": + console.log("Download finished"); + break; + } + }); + + await relaunch(); + } else if (interactive) { + console.log("No update available"); + } + } catch (error) { + console.error("Failed to check for updates:", error); + } +}