forked from snxraven/peardock
149 lines
4.4 KiB
JavaScript
149 lines
4.4 KiB
JavaScript
/**
|
|
* Pear OTA for peardock-client (Electron).
|
|
* Uses pear-runtime-updater + Corestore + Hyperswarm directly — NOT pear-runtime —
|
|
* so we avoid packaging bare-sidecar (~370MB) and stay fast to asar.
|
|
*/
|
|
'use strict'
|
|
|
|
const path = require('path')
|
|
const fs = require('fs')
|
|
const { app, BrowserWindow } = require('electron')
|
|
const otaConfig = require('../lib/ota-config.cjs')
|
|
|
|
/**
|
|
* @returns {Promise<{ enabled: boolean, upgrade: string|null, close: () => Promise<void> }>}
|
|
*/
|
|
async function startClientOta(opts = {}) {
|
|
const pkg = require('../package.json')
|
|
const cfg = otaConfig.buildUpdaterOptions('client', {
|
|
pkg,
|
|
dir: path.join(app.getPath('userData'), 'pear-runtime-data'),
|
|
app: getClientAppPath(),
|
|
name: otaConfig.artifactName('client'),
|
|
})
|
|
|
|
const broadcast =
|
|
opts.broadcast ||
|
|
((channel, data) => {
|
|
for (const win of BrowserWindow.getAllWindows()) {
|
|
if (!win.isDestroyed()) win.webContents.send(channel, data)
|
|
}
|
|
})
|
|
|
|
if (!cfg.enabled) {
|
|
console.log('[peardock-ota] disabled or no upgrade link')
|
|
return { enabled: false, upgrade: null, close: async () => {} }
|
|
}
|
|
|
|
if (!app.isPackaged && !process.env.PEARDOCK_OTA_FORCE) {
|
|
console.log('[peardock-ota] skipped in unpackaged dev (PEARDOCK_OTA_FORCE=1 to test)')
|
|
return { enabled: false, upgrade: cfg.upgrade, close: async () => {} }
|
|
}
|
|
|
|
fs.mkdirSync(cfg.dir, { recursive: true })
|
|
|
|
let PearRuntimeUpdater
|
|
let Corestore
|
|
let Hyperswarm
|
|
try {
|
|
PearRuntimeUpdater = require('pear-runtime-updater')
|
|
Corestore = require('corestore')
|
|
Hyperswarm = require('hyperswarm')
|
|
} catch (err) {
|
|
console.warn('[peardock-ota] deps missing:', err.message)
|
|
return { enabled: false, upgrade: cfg.upgrade, close: async () => {} }
|
|
}
|
|
|
|
const store = new Corestore(path.join(cfg.dir, 'corestore'))
|
|
await store.ready()
|
|
|
|
const updater = new PearRuntimeUpdater({
|
|
dir: cfg.dir,
|
|
version: cfg.version,
|
|
upgrade: cfg.upgrade,
|
|
name: cfg.name,
|
|
app: cfg.app,
|
|
updates: true,
|
|
store,
|
|
})
|
|
await updater.ready()
|
|
|
|
const keyPair = await store.createKeyPair('peardock-client-ota')
|
|
const swarm = new Hyperswarm({ keyPair })
|
|
swarm.on('connection', (c) => store.replicate(c))
|
|
swarm.join(updater.drive.core.discoveryKey, { client: true, server: false })
|
|
|
|
console.log(
|
|
`[peardock-ota] listening version=${cfg.version} name=${cfg.name} app=${cfg.app}`
|
|
)
|
|
|
|
updater.on('error', (err) => {
|
|
console.warn('[peardock-ota] error', err?.message || err)
|
|
broadcast('peardock:ota', { type: 'error', message: String(err?.message || err) })
|
|
})
|
|
updater.on('update-scheduled', (delay) => {
|
|
broadcast('peardock:ota', { type: 'scheduled', delayMs: delay })
|
|
})
|
|
updater.on('updating', () => {
|
|
console.log('[peardock-ota] downloading update…')
|
|
broadcast('peardock:ota', { type: 'updating' })
|
|
})
|
|
updater.on('updated', async () => {
|
|
console.log('[peardock-ota] update ready, applying…', updater.nextVersion)
|
|
broadcast('peardock:ota', { type: 'updated', nextVersion: updater.nextVersion })
|
|
try {
|
|
await updater.applyUpdate()
|
|
broadcast('peardock:ota', { type: 'applied' })
|
|
if (process.platform === 'linux' && process.env.APPIMAGE) {
|
|
app.relaunch({
|
|
execPath: process.env.APPIMAGE,
|
|
args: [
|
|
'--appimage-extract-and-run',
|
|
...process.argv.slice(1).filter((a) => a !== '--appimage-extract-and-run'),
|
|
],
|
|
})
|
|
} else {
|
|
app.relaunch()
|
|
}
|
|
app.exit(0)
|
|
} catch (err) {
|
|
console.error('[peardock-ota] apply failed', err)
|
|
broadcast('peardock:ota', { type: 'error', message: String(err.message || err) })
|
|
}
|
|
})
|
|
|
|
return {
|
|
enabled: true,
|
|
upgrade: cfg.upgrade,
|
|
close: async () => {
|
|
try {
|
|
await swarm.destroy()
|
|
} catch {
|
|
// ignore
|
|
}
|
|
try {
|
|
await updater.close()
|
|
} catch {
|
|
// ignore
|
|
}
|
|
try {
|
|
await store.close()
|
|
} catch {
|
|
// ignore
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
function getClientAppPath() {
|
|
if (process.env.PEARDOCK_OTA_APP) return process.env.PEARDOCK_OTA_APP
|
|
if (process.platform === 'linux' && process.env.APPIMAGE) return process.env.APPIMAGE
|
|
if (process.platform === 'win32') return process.execPath
|
|
if (process.platform === 'darwin' && app.isPackaged) {
|
|
return path.join(process.resourcesPath, '..', '..')
|
|
}
|
|
return process.execPath
|
|
}
|
|
|
|
module.exports = { startClientOta, getClientAppPath }
|