Electron 代码签名和公证

12 天前(已编辑)
/ , , ,
33

阅读此文章之前,你可能需要首先阅读以下的文章才能更好的理解上下文。

Electron 代码签名和公证

本文的方式仅限用于 macOS

最近尝试入门了下 Electron 开发,写了一个可以自动匹配弹幕的动漫播放器,期间遇到挺多坑的,写篇文章来记录一下。

代码签名和公证

想要在 macOS 上运行一个桌面端应用,那就必须对它代码进行签名,否则是无法打开,会出现如下错误。

错误提示

错误提示

Apple Developer 注册

想要进行代码签名就得花 688 元去注册苹果开发者,这里注册也比较玄学,注册过程中运气不好就容易出现 联系我们以继续流程的弹窗,这我在用 MacBook 注册时候出现过一次,然后换 iPhone 上注册就没有这个问题了。

注册到最后一步,付完费用之后,你会发现还是没办法使用,打开 Developer APP 账户页面里面,会显示一个灰色的现在注册按钮,然后显示将很快收到相关邮件,打开邮箱会发现两封名为你的订阅确认Apple 提供的收据的邮件,但这个其实并不是上文 Apple 所提到的相关邮件。这里不用慌张,这其实就是 Apple 正在审核的意思,我是晚上注册的,等隔天早上 9 点之后,就会收到一份 欢迎加入 Apple Developer Program的邮件,这才表明注册成功了。

代码签名

关于具体如何生成和上传证书,网上相关教程有很多,这里就不展开说明了。之后就是把 Developer ID certificates 的私钥 .p12 文件,配置到终端环境变量中,填写 CSC_LINKCSC_KEY_PASSWORD

这里 Electron 打包我选择使用 electron-builder ,执行 electron-vite build && electron-builder --mac --publish never 的时候,它会自动读取上方配置好的两个环境变量从而就行签名,无需进行额外配置。

下面是我的 electron-builder.yml

appId: com.suemor.Marchen
productName: Marchen
directories:
  buildResources: build
files:
  - '!**/.vscode/*'
  - '!src/*'
  - '!electron.vite.config.{js,ts,mjs,cjs}'
  - '!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}'
  - '!{.env,.env.*,.npmrc,pnpm-lock.yaml}'
  - '!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}'
asarUnpack:
  - resources/**
win:
  executableName: Marchen
nsis:
  artifactName: ${productName}-${version}-setup.${ext}
  shortcutName: ${productName}
  uninstallDisplayName: ${productName}
  createDesktopShortcut: always
  allowToChangeInstallationDirectory: true
  oneClick: false
mac:
  entitlementsInherit: build/entitlements.mac.plist
  extendInfo:
    - NSCameraUsageDescription: Application requests access to the device's camera.
    - NSMicrophoneUsageDescription: Application requests access to the device's microphone.
    - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder.
    - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder.
  notarize: false
  target:
    - target: dmg
      arch:
        - arm64
        - x64
    - target: zip
      arch:
        - arm64
        - x64
dmg:
  artifactName: ${productName}-${version}-${arch}.${ext}
linux:
  target:
    - target: AppImage
      arch:
        - arm64
        - x64
  maintainer: github.com/suemor233
  category: Utility
appImage:
  artifactName: ${productName}-${version}-${arch}.${ext}
npmRebuild: false
publish:
  provider: github
  owner: marchen-dev
  repo: MarchenPlay
afterSign: scripts/notarize.js
releaseInfo:
  releaseNotes: |
    本次更新:
      可以切换动漫内嵌字幕和手动导入字幕
      新增视频播放器设置功能
      可以对历史记录动漫进行删除和从新识别弹幕库

公证

代码签完名之后,我们需要把打包后的程序上传给苹果,来确保我们程序的安全性和没有被其他人篡改。

这里我们安装 @electron/notarizedotenv这两个包, 然后创建一个 scripts/notarize.js文件,写法如下,他可以读取我们 .env 里面的相关变量,然后进行公证。

import { notarize } from '@electron/notarize'
import { config } from 'dotenv'

config()

export default async function notarizing(context) {
  if (context.electronPlatformName !== 'darwin') {
    return
  }

  const appBundleId = process.env.APPLE_APP_BUNDLE_ID // 随便填一个,类似 com.suemor.Marchen
  const appleId = process.env.APPLE_ID // 你的 Apple ID 
  const appleIdPassword = process.env.APPLE_APP_SPECIFIC_PASSWORD // https://account.apple.com/account/manage 然后点击 App 专用密码
  const teamId = process.env.APPLE_TEAM_ID // https://developer.apple.com/account 里面 会员资格详细信息 卡片里面,有个 团队 ID

  if (!appBundleId || !appleId || !appleIdPassword || !teamId) {
    return
  }

  const appName = context.packager.appInfo.productFilename
  const appPath = `${context.appOutDir}/${appName}.app`
  // eslint-disable-next-line no-console
  console.log('Notarizing app:', appPath)

  await notarize({
    appPath,
    appBundleId,
    appleId,
    appleIdPassword,
    teamId,
  })
}

之后在 electron-builder.yml 里面导入这个路径 afterSign: scripts/notarize.js

然后执行"build:mac": "electron-vite build && electron-builder --mac --publish never",就可以完成公证了。

这里公证速度一般都挺慢的,因为要把你的应用上传到苹果服务器上去,取决于你宽带的上传速度,得耐心等待。另外我看别人说公证成功之后收到 Apple Developer 的相关邮件,不清楚什么原因,我从来没有收到过。

那么如何校验我们这个应用是否公证成功了呢?安装完 App 之后,我们只要去终端执行下方的命令,显示The validate action worked!就代表成功了。

stapler validate /Applications/xxxx.app

配置 GitHub Actions

为了方便发布版本,我们可能会利用 Github Actions 进行自动化打包,为了 Github Actions 能够在打包过程中进行签名和公证,如下图所示,我们需要把上文所配置的环境变量放到 Github Repository secrets 里面。

CSC_LINK // 填写 base64
CSC_KEY_PASSWORD
APPLE_ID
APPLE_APP_SPECIFIC_PASSWORD
APPLE_TEAM_ID
APPLE_APP_BUNDLE_ID
 Github Repository secrets

Github Repository secrets

这里有个麻烦的地方,就是 CSC_LINK 这个字段的填写。之前我们在配置终端环境变量的时候,是直接使用绝对路径的方式来链接到 .p12 文件的,但这里的 Repository secrets 是不支持上传文件的,所以得把之前 Developer ID certificates 的私钥 .p12 文件转换为 base64 然后复制到 Repository secrets 的 CSC_LINK 的变量里面去。

base64 -i xxxx.p12 | pbcopy

下方是我完整的 release.yml

name: Build/release Electron app

on:
  push:
    tags:
      - v*.*.*

jobs:
  release:
    runs-on: ${{ matrix.os }}

    strategy:
      matrix:
        os: [ubuntu-latest, macos-latest, windows-latest]
        node-version: [20.x]

    steps:
      - name: Checkout code
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          lfs: true

      - name: Checkout LFS objects
        run: git lfs checkout

      - name: Setup pnpm
        uses: pnpm/action-setup@v4.0.0

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v4
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'pnpm'

      - name: Install Dependencies
        run: pnpm install

      - name: Install snapcraft
        if: matrix.os == 'ubuntu-latest'
        run: sudo snap install snapcraft --classic

      - name: Build for Linux
        if: matrix.os == 'ubuntu-latest'
        run: pnpm run build:linux

      - name: Build for macOS
        if: matrix.os == 'macos-latest'
        run: pnpm run build:mac
        env:
          APPLE_ID: ${{ secrets.APPLE_ID }}
          APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
          APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
          APPLE_APP_BUNDLE_ID: ${{ secrets.APPLE_APP_BUNDLE_ID }}
          CSC_LINK: ${{ secrets.CSC_LINK }}
          CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
          GH_TOKEN: ${{ secrets.GH_TOKEN }}

      - name: Build for Windows
        if: matrix.os == 'windows-latest'
        run: pnpm run build:win

      - name: Generate Changelog
        if: github.event_name == 'push'
        run: npx @suemor/changelogithub
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

      - name: Create Release
        uses: softprops/action-gh-release@v2
        with:
          draft: false
          prerelease: true
          files: |
            dist/*.exe
            dist/*.zip
            dist/*.dmg
            dist/*.AppImage
            dist/*.snap
            dist/*.deb
            dist/*.rpm
            dist/*.tar.gz
            dist/latest*.yml
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

下期预告

下篇文章准备来讲一下如何打包 FFmpeg 到 Electron 里面,这里面的坑还是挺多的,特别是利用 Github Actions 打包的时候,得实现根据不同的平台打包不同 FFmpeg 才行,比如用 arm64 mac 打包 x64 的应用,如何正确打包 x64 版本的 FFmpeg。

使用社交账号登录

  • Loading...
  • Loading...
  • Loading...
  • Loading...
  • Loading...