desktop-service-sync.mjs 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. import { join, resolve } from 'node:path'
  2. import { homedir } from 'node:os'
  3. import { createHash } from 'node:crypto'
  4. import { mkdirSync } from 'node:fs'
  5. import { LaunchAgentService, synchronizeDesktop, stopDesktopService } from './lib/service-update.mjs'
  6. import { migrateManagedDataHome } from './lib/managed-data-migration.mjs'
  7. // Losing the App's progress pipe must not interrupt a durable service transaction.
  8. process.stdout.on('error', error => { if (!['EPIPE', 'EIO'].includes(error.code)) process.exitCode = 1 })
  9. let input = ''
  10. for await (const bytes of process.stdin) { input += bytes; if (input.length > 32768) throw new Error('同步参数过大') }
  11. try {
  12. const options = JSON.parse(input)
  13. if (options.force !== undefined && typeof options.force !== 'boolean') throw new Error('强制覆盖参数无效')
  14. const home = resolve(options.home), port = Number(options.port)
  15. if (!Number.isInteger(port) || port < 1 || port > 65535) throw new Error('本机服务端口无效')
  16. if (options.action === 'migrate') {
  17. if (!options.fixture) await migrateManagedDataHome({ home, port, onProgress: message => process.stdout.write(JSON.stringify({ message }) + '\n') })
  18. process.stdout.write(JSON.stringify({ result: { action: 'migrated' } }) + '\n')
  19. process.exit(0)
  20. }
  21. mkdirSync(home, { recursive: true, mode: 0o700 })
  22. // Fixture mode isolates *all* service/update state, including launchd labels.
  23. const fixture = options.fixture === true
  24. const releaseRoot = fixture ? join(home, 'updates', 'fixture-releases') : join(homedir(), '.local', 'share', 'hermes-yaoyao')
  25. const label = fixture ? `cn.samien.yaoyao.sync-test.${createHash('sha256').update(home).digest('hex').slice(0, 16)}` : 'com.samien.hermes-yaoyao'
  26. const driver = new LaunchAgentService({ home, port, releaseRoot, label,
  27. plistPath: fixture ? join(home, 'updates', 'fixture-service.plist') : undefined,
  28. environment: options.environment,
  29. })
  30. if(options.action!==undefined&&!['sync','stop'].includes(options.action))throw new Error('本机服务操作无效')
  31. const onProgress = message => process.stdout.write(JSON.stringify({ message }) + '\n')
  32. const result = options.action==='stop'
  33. ? await stopDesktopService({home,driver,onProgress})
  34. : await synchronizeDesktop({ home, runtimeRoot: resolve(options.runtimeRoot), releaseRoot, driver, onProgress, force: options.force === true })
  35. const current = result.current
  36. process.stdout.write(JSON.stringify({ result: { action: result.action, current: current && {
  37. version: current.version, commit: current.commit, buildNumber: current.buildNumber, artifactDigest: current.artifactDigest,
  38. } } }) + '\n')
  39. } catch (error) {
  40. process.stdout.write(JSON.stringify({ error: error.message, code: error.code }) + '\n'); process.exitCode = 1
  41. }