diff --git a/electron-builder.yml b/electron-builder.yml new file mode 100644 index 0000000..9d059f0 --- /dev/null +++ b/electron-builder.yml @@ -0,0 +1,50 @@ +appId: site.codingplan.openterminal +productName: OpenTerminal + +directories: + output: release + +files: + - out/** + - package.json + +asar: true +asarUnpack: + - "**/node_modules/@lydell/node-pty/**" + - "**/node_modules/ssh2/**" + +# @lydell/node-pty ships prebuilt binaries; do not trigger a rebuild. +npmRebuild: false + +# Electron-updater only supports NSIS auto-updates (MSI has no update channel). +# The NSIS .exe + latest.yml get uploaded together; MSI is manual/enterprise only. +win: + target: + - target: msi + - target: nsis + +msi: + oneClick: false + # Fixed upgrade code keeps the upgrade chain consistent across releases. + upgradeCode: "{5ab9f79e-e4eb-4052-9df6-3af3a301ab0a}" + artifactName: ${productName}-${version}-setup.${ext} + # No build/icon.ico exists. The WiX template only emits when an icon is + # set, but the shortcut elements still reference Icon=, so MSI linking + # fails with LGHT0094. Disabling MSI shortcuts drops the dangling reference. + # NSIS still creates its shortcuts independently. + createDesktopShortcut: false + createStartMenuShortcut: false + +nsis: + oneClick: false + allowToChangeInstallationDirectory: true + perMachine: false + artifactName: ${productName}-${version}-setup.${ext} + +# Declaring a publish provider makes electron-builder emit the update-info file +# (latest.yml) into directories.output for upload. No token is stored here; the +# main agent handles feed authentication during upload. Build runs with the +# default publish policy, so nothing is uploaded from this script. +publish: + provider: generic + url: https://git.codingplan.site/api/packages/admin/generic/openterminal-update/stable/ \ No newline at end of file diff --git a/package.json b/package.json index 656a9db..6c02356 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "open-terminal", "productName": "OpenTerminal", - "version": "0.1.0", + "version": "0.7.0", "description": "Terminal with split panes, themes and fonts (XTerminal-inspired)", "main": "out/main/index.js", "author": "Bill", @@ -10,7 +10,9 @@ "dev": "electron-vite dev", "build": "electron-vite build", "preview": "electron-vite preview", - "typecheck": "tsc --noEmit -p tsconfig.node.json && tsc --noEmit -p tsconfig.web.json" + "typecheck": "tsc --noEmit -p tsconfig.node.json && tsc --noEmit -p tsconfig.web.json", + "dist": "electron-vite build && electron-builder --win msi nsis", + "dist:dir": "electron-vite build && electron-builder --win --dir" }, "dependencies": { "@lydell/node-pty": "^1.2.0-beta.15", diff --git a/src/main/index.ts b/src/main/index.ts index 5c1e36b..0fefeca 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -3,6 +3,7 @@ import { join } from 'path' import { registerIpc } from './ipc' import { killAllPtys } from './pty' import { applyStartupSystemSettings, loadSettings } from './settingsStore' +import { configureAutoUpdater } from './updater' function createWindow(): void { const win = new BrowserWindow({ @@ -54,6 +55,7 @@ app.whenReady().then(() => { // settings dialog is never opened this run. applyStartupSystemSettings(loadSettings()) createWindow() + configureAutoUpdater() app.on('activate', () => { if (BrowserWindow.getAllWindows().length === 0) createWindow() diff --git a/src/main/updater.ts b/src/main/updater.ts new file mode 100644 index 0000000..e1b7d74 --- /dev/null +++ b/src/main/updater.ts @@ -0,0 +1,54 @@ +import { app } from 'electron' +import { autoUpdater } from 'electron-updater' + +/** + * Default update feed (generic provider). The main agent uploads latest.yml and + * the NSIS installer to this stable path. + * + * KNOWN LIMITATION: electron-updater does NOT support MSI auto-updates. The + * update channel always runs through the NSIS .exe (latest.yml + .exe are + * uploaded together). MSI is manual/enterprise distribution only. + */ +const DEFAULT_FEED_URL = + 'https://git.codingplan.site/api/packages/admin/generic/openterminal-update/stable/' + +export function configureAutoUpdater(): void { + // Update checks only run in packaged builds; no-op during development. + if (!app.isPackaged) { + return + } + + const url = process.env.OT_UPDATE_URL || DEFAULT_FEED_URL + const token = process.env.OT_UPDATE_TOKEN + + autoUpdater.setFeedURL({ + provider: 'generic', + url, + ...(token ? { requestHeaders: { Authorization: `token ${token}` } } : {}) + }) + + autoUpdater.logger = console + autoUpdater.autoDownload = true + autoUpdater.autoInstallOnAppQuit = true + + // Check shortly after startup; log errors without crashing the app. + const timer = setTimeout(() => { + autoUpdater + .checkForUpdatesAndNotify() + .catch((err) => console.warn('[updater] checkForUpdates failed:', err)) + }, 5000) + timer.unref?.() +} + +/** Public entry point for later UI wiring (not connected this milestone). */ +export function checkForUpdates(): Promise { + if (!app.isPackaged) { + return Promise.resolve() + } + return autoUpdater + .checkForUpdates() + .then(() => {}) + .catch((err) => { + console.warn('[updater] checkForUpdates failed:', err) + }) +} \ No newline at end of file