diff --git a/.github/workflows/desktop-release.yml b/.github/workflows/desktop-release.yml new file mode 100644 index 000000000..7119fcb6d --- /dev/null +++ b/.github/workflows/desktop-release.yml @@ -0,0 +1,78 @@ +name: Desktop Release + +on: + push: + tags: + - 'v*' + - 'beta-v*' + +permissions: + contents: write + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + include: + - os: macos-latest + platform: --mac + - os: ubuntu-latest + platform: --linux + - os: windows-latest + platform: --win + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Extract version from tag + id: version + shell: bash + run: | + TAG=${GITHUB_REF#refs/tags/} + VERSION=${TAG#beta-} + VERSION=${VERSION#v} + echo "VERSION=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + cache-dependency-path: | + surfsense_web/pnpm-lock.yaml + surfsense_desktop/pnpm-lock.yaml + + - name: Install web dependencies + run: pnpm install + working-directory: surfsense_web + + - name: Build Next.js standalone + run: pnpm build + working-directory: surfsense_web + env: + NEXT_PUBLIC_FASTAPI_BACKEND_URL: ${{ vars.NEXT_PUBLIC_FASTAPI_BACKEND_URL }} + NEXT_PUBLIC_ELECTRIC_URL: ${{ vars.NEXT_PUBLIC_ELECTRIC_URL }} + NEXT_PUBLIC_DEPLOYMENT_MODE: ${{ vars.NEXT_PUBLIC_DEPLOYMENT_MODE }} + NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE: ${{ vars.NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE }} + + - name: Install desktop dependencies + run: pnpm install + working-directory: surfsense_desktop + + - name: Build Electron + run: pnpm build + working-directory: surfsense_desktop + env: + HOSTED_FRONTEND_URL: ${{ vars.HOSTED_FRONTEND_URL }} + + - name: Package & Publish + run: pnpm exec electron-builder ${{ matrix.platform }} --config electron-builder.yml --publish always -c.extraMetadata.version=${{ steps.version.outputs.VERSION }} + working-directory: surfsense_desktop + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/surfsense_desktop/scripts/build-electron.mjs b/surfsense_desktop/scripts/build-electron.mjs index 2c59061b4..923830296 100644 --- a/surfsense_desktop/scripts/build-electron.mjs +++ b/surfsense_desktop/scripts/build-electron.mjs @@ -109,7 +109,7 @@ async function buildElectron() { minify: false, define: { 'process.env.HOSTED_FRONTEND_URL': JSON.stringify( - desktopEnv.HOSTED_FRONTEND_URL || 'https://surfsense.net' + process.env.HOSTED_FRONTEND_URL || desktopEnv.HOSTED_FRONTEND_URL || 'https://surfsense.net' ), }, }; diff --git a/surfsense_desktop/src/main.ts b/surfsense_desktop/src/main.ts index a25644cb5..e0a6c3be5 100644 --- a/surfsense_desktop/src/main.ts +++ b/surfsense_desktop/src/main.ts @@ -1,6 +1,7 @@ import { app, BrowserWindow, shell, ipcMain, session, dialog, clipboard, Menu } from 'electron'; import path from 'path'; import { getPort } from 'get-port-please'; +import { autoUpdater } from 'electron-updater'; function showErrorDialog(title: string, error: unknown): void { const err = error instanceof Error ? error : new Error(String(error)); @@ -210,6 +211,37 @@ if (process.defaultApp) { app.setAsDefaultProtocolClient(PROTOCOL); } +function setupAutoUpdater() { + if (isDev) return; + + autoUpdater.autoDownload = true; + + autoUpdater.on('update-available', (info) => { + console.log(`Update available: ${info.version}`); + }); + + autoUpdater.on('update-downloaded', (info) => { + console.log(`Update downloaded: ${info.version}`); + dialog.showMessageBox({ + type: 'info', + buttons: ['Restart', 'Later'], + defaultId: 0, + title: 'Update Ready', + message: `Version ${info.version} has been downloaded. Restart to apply the update.`, + }).then(({ response }) => { + if (response === 0) { + autoUpdater.quitAndInstall(); + } + }); + }); + + autoUpdater.on('error', (err) => { + console.error('Auto-updater error:', err); + }); + + autoUpdater.checkForUpdates(); +} + function setupMenu() { const isMac = process.platform === 'darwin'; const template: Electron.MenuItemConstructorOptions[] = [ @@ -233,6 +265,7 @@ app.whenReady().then(async () => { return; } createWindow(); + setupAutoUpdater(); // If a deep link was received before the window was ready, handle it now if (deepLinkUrl) {