sign-gstreamer.cjs2.6 KBView on GitHub
'use strict'

const { execSync } = require('child_process')
const path = require('path')
const fs = require('fs')

/**
 * Pre-sign GStreamer.framework dylibs before electron-builder seals the app.
 * GStreamer.framework itself has an ambiguous bundle format that codesign can't
 * handle (signIgnore skips it), but all the Mach-O binaries inside must be
 * signed with a valid Developer ID cert before notarization.
 */
exports.default = async function signGStreamer(context) {
  if (context.electronPlatformName !== 'darwin') return

  // In CI, electron-builder imports the cert into a temp keychain and sets CSC_NAME.
  // Locally, fall back to finding the Developer ID identity from the default keychain.
  let identity = process.env['CSC_NAME']
  if (!identity) {
    try {
      const ids = execSync('security find-identity -v -p codesigning', { encoding: 'utf8' })
      const match = ids.match(/"(Developer ID Application[^"]+)"/)
      identity = match ? match[1] : 'Developer ID Application: Isabelle Ilyia (2QR5XNJVRF)'
    } catch {
      identity = 'Developer ID Application: Isabelle Ilyia (2QR5XNJVRF)'
    }
  }
  const appOutDir = context.appOutDir
  const appName = context.packager.appInfo.productFilename
  const appPath = path.join(appOutDir, `${appName}.app`)

  const gstreamerFramework = path.join(
    appPath,
    'Contents/Resources/app.asar.unpacked/node_modules/@recallai/desktop-sdk/Frameworks/GStreamer.framework',
  )

  if (!fs.existsSync(gstreamerFramework)) {
    console.log('[sign-gstreamer] GStreamer.framework not found, skipping')
    return
  }

  console.log('[sign-gstreamer] signing GStreamer.framework dylibs...')

  // Find all Mach-O binaries (dylibs, .so files, and extensionless binaries like GStreamer)
  const allFiles = execSync(
    `find "${gstreamerFramework}" -type f`,
    { encoding: 'utf8' },
  ).trim().split('\n').filter(Boolean)

  const files = allFiles.filter((f) => {
    try {
      const out = execSync(`file "${f}"`, { encoding: 'utf8' })
      return out.includes('Mach-O')
    } catch {
      return false
    }
  })

  const keychain = process.env['CSC_KEYCHAIN']
  const keychainFlag = keychain ? `--keychain "${keychain}"` : ''

  for (const file of files) {
    try {
      execSync(
        `codesign --sign "${identity}" --force --timestamp --options runtime ${keychainFlag} "${file}"`,
        { stdio: 'pipe' },
      )
    } catch (e) {
      console.warn(`[sign-gstreamer] warning: could not sign ${path.basename(file)}: ${e.stderr?.toString().trim()}`)
    }
  }

  console.log(`[sign-gstreamer] signed ${files.length} binaries in GStreamer.framework`)
}