超完整的Electron打包签名更新指南,这真是太酷啦!

大家好,我是多喝热水。

在踩了数不清的坑之后,终于从 0 到 1 完成了一个桌面端应用,但万万没想到,最最痛苦的还不是开发过程,而是开发完成后的打包签名阶段,这真是踩坑踩麻了!!!

ok,踩坑归踩坑,收获也是不小的,所以这篇文章会介绍一个 Electron 应用如何从 0 到 1 完成打包 & 签名 & 自动更新 等一系列流程,全程干货,建议点赞收藏!

写在前面

文章中所使用的环境如下,读者可作参考:

1)nodejs 版本:v18.19

2)electron 版本:v27.1.0

3)electron-builder 版本:v24.6.4

4)electron-rebuild 版本:v3.2.9

5)electron-notarize 版本:v1.2.2

6)electron-updater 版本:v6.1.4

7)node-abi 版本:v3.52.0

大家也可以根据自己安装的 Electron 版本来安装对应的 Node 版本,两者必须对应上,否则打包必出错,参考官方文档

一、打包

打包有一个非常重要的前提,那就是安装包和系统对应,比如我想打包MacOS的安装包,那么我就需要使用 MacOS 电脑,Windows 同理,如果你没有,那你可以考虑安装一个虚拟机来打包。

MacOS 平台打包

1)准备好electron-builder配置文件,如下,具体的参数含义可参考官方文档

js 复制代码
const path = require('path');
const fs = require('fs');

module.exports = async function () {
  function findFilesInDirectory(directoryPath, result = []) {
    const files = fs.readdirSync(directoryPath);
    files.forEach(file => {
      const filePath = path.join(directoryPath, file);
      const stats = fs.statSync(filePath);
      if (stats.isFile()) {
        result.push({from: filePath, to: '.'}); // 将主进程中用到的静态资源输出到打包后的软件根目录
      } else if (stats.isDirectory()) {
        result = findFilesInDirectory(filePath, result);
      }
    });
    return result;
  }
  const staticResources = findFilesInDirectory(path.join('packages', 'main', 'resources'));
  return {
    asar: true, // 代码加密
    productName: '【软件名称】',
    compression: 'maximum', // 压缩代码
    directories: {
      output: `dist`, // 将打包产物输出到dist文件
    },
    files: ['packages/**/dist/**'],
    extraResources: [...staticResources],
    mac: {
      target: ['dmg'],
      icon: 'buildResources/icon.icns' // 软件图标
    },
    dmg: {
      icon: 'buildResources/icon.icns',
      iconSize: 100,
    }
  };
};

2)应用中是否用到了 C / C++ 编写的依赖包?如果没有那么可忽略此步骤,如果有,那么需要使用 electron-rebuildnode_modules 预构建一次,且构建会需要用到 node-gyp确保是最新版本的 )+ Python 环境(Mac 自带的即可),运行命令如下:

bash 复制代码
electron-rebuild

3)配置 package.json 中的运行脚本

js 复制代码
"scripts": {
    "build": "npm run build:main && npm run build:preload && npm run build:renderer",
    "build:main": "cd ./packages/main && vite build", // 主进程代码打包
    "build:preload": "cd ./packages/preload && vite build", // 预渲染进程代码打包
    "build:renderer": "cd ./packages/renderer && vite build", // 渲染进程代码打包
    "compile": "rm -rf ./dist && npm cache clean --force && cross-env MODE=production npm run build && electron-builder build --config 【上面编写的配置文件名称】",
  },

4)开始打包

bash 复制代码
npm run compile

打包完成之后我们就会看到生成一个 dist 文件夹,里面包含了一个 dmg 安装包和一些 yml 文件,此时我们就完成了第一步,如果你失败了,那么大概率是网络问题,可以重新打包,必要时清理一下 npm 缓存。

Windows 平台打包

1)windows 打包命令和 MacOS 一致,不同的是 electron-builder 的配置,我们对 electron-builder 配置文件进行改造,如下:

js 复制代码
const path = require('path');
const fs = require('fs');

module.exports = async function () {
  function findFilesInDirectory(directoryPath, result = []) {
    const files = fs.readdirSync(directoryPath);
    files.forEach(file => {
      const filePath = path.join(directoryPath, file);
      const stats = fs.statSync(filePath);
      if (stats.isFile()) {
        result.push({from: filePath, to: '.'}); // 将主进程中用到的静态资源输出到打包后的软件根目录
      } else if (stats.isDirectory()) {
        result = findFilesInDirectory(filePath, result);
      }
    });
    return result;
  }
  const staticResources = findFilesInDirectory(path.join('packages', 'main', 'resources'));
  return {
    asar: true, // 代码加密
    productName: '【软件名称】',
    compression: 'maximum', // 压缩代码
    directories: {
      output: `dist`, // 将打包产物输出到dist文件
    },
    files: ['packages/**/dist/**'],
    extraResources: [...staticResources],
    nsis: {
      oneClick: false,
      perMachine: false,
      allowToChangeInstallationDirectory: true,
      deleteAppDataOnUninstall: false,
    },
    win: {
      icon: 'buildResources/icon.ico', // 软件图标
      target: [
        {
          target: 'nsis', // 使用 NSIS 进行打包
          arch: ['x64'],
        },
      ],
    },
  };
};

2)接下来的步骤与MacOS一致,这里不过多赘述,如下:

bash 复制代码
electron-rebuild

3)开始打包

bash 复制代码
npm run build

打包完成你可以在dist文件夹中看到如下文件和安装包:

此时我们已经完成了第一步,接下来我们需要对软件进行代码签名。

二、代码签名

代码签名需要哪些前置条件?

Windows 平台:购买EV证书,它可以立刻消除警告,但是这个证书非常昂贵,基本上都是 2000+的价格,且有效期只有一年

MacOS 平台:加入苹果开发者,价格是 99 美元,约为人民币 600+,同样有效期为一年

所以我们光签名就已经花费了 3000 左右,可能有些人在这里就已经劝退了,那不签名行不行?

不做代码签名会遇到哪些限制?

试想一下,你有一包辣条,但是你不知道这包辣条是哪个厂家生产的,你大概率会选择不吃!但如果辣条包装上有生产厂家和生产日期等信息,你吃起来是不是就更放心了?软件签名的道理也是如此!

1)在浏览器下载完这个软件的时候你可能会看到以下警告(图片出自网络),这样一个提示肯定是不利于传播的,如果是我,我可能会选择删除。。

2)即使下载后打开应用,你依然可能遇到如下问题(大致意思就是建议用户不要打开该软件,因为它是未知的开发者发布的)

3)在 MacOS 提示更为严重的软件损坏(直接就不让打开了,我辛辛苦苦开发的软件不让人用这还搞个毛线啊),如下:

MacOS 代码签名 & 公证

苹果签名的大致步骤拆分如下,更详细步骤移步 Electron 官网 代码签名 | Electron

1)加入 苹果开发者 (需要缴纳年费)

2)生成 & 下载签名证书,然后安装到本机, 苹果签名证书下载地址

3)打包签名后进行公证(不公证仍然会提示软件已损坏)

1. 加入苹果开发者

1)点击苹果开发者注册地址,进入登录自己的 AppleID,然后就会看到如下界面:

2)点击注册,会让下载一个软件 Apple Developer,如下图:

3)打开Apple Developer 去注册苹果开发者,如下:

4)点击立即注册,如下:

5)按要求填写信息,如下:

这里需要注意,如果是以公司的名义去注册的,那么你还需要注册一个邓白氏编码(D-U-N-S),在后续会用到。

2. 生成签名证书

1)注册完成后,我们打开本机电脑的钥匙串访问 => 证书助理 => 从证书颁发机构请求证书,如下:

2)填写邮箱,并选择存储到磁盘,如下:

3)点击继续之后就会创建一个证书,提示存储到哪里,我们先存储到桌面上

4)前往苹果开发者官网申请 Developer ID Application(需要用到我们刚才请求的证书,此步骤需要账户持有人操作

点击 Choose File,上传我们之前在自己电脑上申请的证书文件,上传完成后我们就可以得到Developer ID Application 证书,将它下载保存到桌面。

3. 安装签名证书

1)将下载到桌面的证书拖拽到钥匙串访问的证书列表中,并将 私钥 导出为 .p12 文件

2)打开终端配置环境变量,此处参考的是 electron-builder 官方文档

3)输入如下两个环境变量,对应的是 p12 文件的地址和证书对应的密码,配置完成后使用 source 命令让刚才配置的环境变量生效

4)输入 env 查询是否配置成功,如果显示了我们填写的环境变量地址,那就成功了

4. 打包 & 签名

在做完以上操作后,目前你不需要对 electron-builder 配置做任何更改,直接可以运行打包命令,在打包过程中你会看到如下 signing 正在签名的提示,如下:

验证是否签名,你可以在软件包内容中你可以看到 _CodeSignature 的文件夹,表示该应用已经被成功签名了,如下:

5. 公证

苹果平台除了签名,还需要进行公证,两者缺一不可,公证我们需要用到 electron-notarize 这个包,具体的配置如下:

1)appbundleId:在苹果开发者中注册的应用 ID

2)appPath:固定写法,照抄即可

3)appleId:苹果开发者的苹果 ID

4)appleIdPassword:临时密码生成地址,临时密码需要保存好,因为关掉后它不会再显示了

5)ascProvider:团队 ID 查看地址

6)tool:签名工具,公证工具

7)teamId:团队 ID 同 ascProvider

buildResources/notarization/notarize.js

js 复制代码
const {notarize} = require('electron-notarize');

exports.default = async function notarizing(context) {
  const {electronPlatformName, appOutDir} = context;
  if (electronPlatformName !== 'darwin') {
    return;
  }
  const appName = context.packager.appInfo.productFilename;
  // console.log(`打包后应用地址:${appOutDir}/${appName}.app`);
  return await notarize({
    appBundleId: 'com.xxx.xxx',
    appPath: `${appOutDir}/${appName}.app`, //打包后的放置app文件的命名和路径【固定写法】
    appleId: 'xxx@qq.com',
    appleIdPassword: 'xxxx', // 临时密码
    ascProvider: '团队ID',
    tool: 'notarytool', // 公证工具 固定写法
    teamId: '团队ID',
  });
};

.electron-builder.config.js

在 afterSign 选项中加入我们公证逻辑的地址,该字段表示打包完成之后进行公证

加完这些配置之后再次重新打包即可完成公证!

Windows 代码签名

windows 签名也比较简单,但是在 Windows 上花费的时间不比苹果少,这里建议大家不要去开微软开发者,如果你不打算把软件分发到微软商店的话,那开通微软开发者对签名没有什么帮助,这也是我们花费了 99 刀踩过的坑!

1. 购买签名证书

现在的签名证书都是以 U 盾的形式发放(快递邮寄,一般都是国外发货),没有数字签名了,这里我调研了很多平台,确信!建议从淘宝代理那购买 Sectigo EV 代码签名证书,比较便宜。

2. 安装签名证书

1)安装 SafeNet Authentication Client 软件,这个软件是用来安装证书的,安装地址

2)插入包含签名证书的 U盾,然后我们就会在软件左侧看到对应的证书信息

3)安装签名证书

选择安装到当前用户或本机都可以,最终我们选择安装到受信任的根证书颁发机构

4)导出签名证书 cer 文件到桌面

我们使用 Win + R 唤出如下界面,并输入 certmgr.msc

找到我们的证书所在位置

右键导出 cer 文件到桌面

5)打开签名软件的设置页,设置单点登录(因为等会打包会涉及到多次密码输入,为了简化流程)

6)初始密码太繁琐了,我们可以修改一下证书的初始密码( Token Password ),这个证书的初始密码可以在你购买时填写的邮箱中找到

7)找到我们导出的证书文件,现在是 cer 格式的,我们需要将它 重命名为 pfx 格式,因为 electron 打包只支持 pfx 格式的文件

3. 打包 & 签名

certificate.pfx 文件拖入项目根目录,在electron-builder中加入如下配置:

js 复制代码
return {
    appId: 'com.xxx.xxx',
    nsis: {
      oneClick: false,
      perMachine: false,
      allowToChangeInstallationDirectory: true,
      deleteAppDataOnUninstall: false,
    },
    win: {
      icon: 'buildResources/icon.ico',
      verifyUpdateCodeSignature: true, // 更新校验签名
      signingHashAlgorithms: ['sha256'],
      signAndEditExecutable: true,
      signDlls: false,
      publisherName: '上海xxxx有限公司',
      certificateSubjectName: '',
      rfc3161TimeStampServer: 'http://timestamp.sectigo.com',
      target: [
        {
          target: 'nsis', // 使用 NSIS 进行打包
          arch: ['x64'],
        },
      ],
      certificateFile: 'certificate.pfx', // 我们导出的证书
      certificatePassword: 'xxxxxx', // 上面我们修改的密码
    },
    mac: {
    // ...
    },
    dmg: {
    // ...
    }
  };
};

现在可以执行打包命令,打包过程中你会看到 signing 正在签名的提示,打包结束为了验证是否完成签名,你可以右键打包出来的exe产物,查看数字签名栏的信息,如果显示的是你填写的 publisherName 字段值,那么表示签名成功,如下:

三、自动更新

为什么要做自动更新?

用户下载了你的软件,如果每次都需要去官网下载更新的话,那么久而久之用户必然会觉得很烦,甚至产生卸载软件的念头,所以我们需要做的就是将用户手动去下载更新这个操作自动化,换成自动下载安装包完成更新。

如何实现自动更新?

1)我们需要用到 electron-updater 这个包,主要需要用到 autoUpdater 这个对象

js 复制代码
import { type BrowserWindow } from 'electron';
import { autoUpdater } from 'electron-updater';
import { createModalWindow } from './windows/modal';

/**
 * 自动更新检测
 */
export const checkUpdate = () => {
  // 创建设置窗口
  function updateAvailable() {
    // 创建有新版本的弹窗提示
    createModalWindow();
  }
  // 
  function updateDownloaded() {
    // 安装包下载完成,将自动安装,给出提示...
    // ...
  }
  // 开启自动安装 & 自动下载
  autoUpdater.autoInstallOnAppQuit = true;
  autoUpdater.autoDownload = true;

  if (process.platform === 'darwin') {
    // 苹果平台【interl/M系列区分】
    const ARM = process.arch == 'arm64';
    const feedUUL = ARM
      ? 'https://xxxx.com/installer/mac/arm'
      : 'https://xxxx.com/installer/mac/intel';
    autoUpdater.setFeedURL(feedUUL);
  } else {
    // windows平台
    autoUpdater.setFeedURL('https://xxxx.com/installer/win');
  }
  // 检测新版本
  autoUpdater.checkForUpdatesAndNotify().catch();
  // 发现可用的安装包
  autoUpdater.once('update-available', updateAvailable);
  // 安装包下载完成了
  autoUpdater.once('update-downloaded', updateDownloaded);
};

2)在 .electron-builder.config.js 中加入 publish 字段,用于检测当前地址中latest.yml文件记录的版本

在加入了publish字段后我们再打包会生成一个latest.yml文件(苹果是latest-mac.yml),该文件我们需要上传到自己的安装包所在的目录,检测新版本就是检测 latest.yml文件中记录的版本

latest.yml 文件格式

完结

好了,到这里整个流程就算是跑完了,大家有不懂的可以在评论区讨论,文章哪里有写的不好的欢迎大佬指点,我太想进步了!!🥹

相关推荐
WeiXiao_Hyy35 分钟前
成为 Top 1% 的工程师
java·开发语言·javascript·经验分享·后端
吃杠碰小鸡1 小时前
高中数学-数列-导数证明
前端·数学·算法
kingwebo'sZone1 小时前
C#使用Aspose.Words把 word转成图片
前端·c#·word
xjt_09011 小时前
基于 Vue 3 构建企业级 Web Components 组件库
前端·javascript·vue.js
我是伪码农1 小时前
Vue 2.3
前端·javascript·vue.js
夜郎king2 小时前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
辰风沐阳2 小时前
JavaScript 的宏任务和微任务
javascript
夏幻灵3 小时前
HTML5里最常用的十大标签
前端·html·html5
冰暮流星3 小时前
javascript之二重循环练习
开发语言·javascript·数据库
Mr Xu_3 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js