Chrome扩展程序之自动更新

最开始听到产品的Chrome插件需求时,由于用户较少,优先的关注点是功能的实现,并未过多考虑如何部署插件。每次更新都是提供一个压缩包,然后让用户使用开发者模式下加载本地压缩包的方式进行更新;后来用户逐渐增多,功能迭代也比较快,每次分发压缩包并重新安装,因此就有了插件自动更新的需求~

下面总结了两个插件实现的自动更新的一些整理和心得。

<!--more-->

1. 应用商店托管实现自动更新

通过应用商店托管扩展程序,有以下特性

  • 扩展程序安全性,这里的安全性指的是允许用户安装的策略,在发布应用时有公开、非公开、指定账户访问三种等级,其中
    • 公开是所有用户都可以通过应用商店搜索到该扩展程序
    • 非公开则需要知道扩展程序的下载链接,才能够进行安装,无法直接通过搜索应用商店直接安装
    • 指定账户访问则需要在开发者中心添加体验用户的chrome账号,才能够安装该插件,采用这种方式则需要收集使用者的chrome账户
  • 自动更新,后续只需要增加版本号并重新在开发者中心发布,chrome会自动更新相关插件,一般会在新版本发布后6个小时内进行更新
  • 强制更新,chrome自动更新有一定时间的延迟,如果在发布后想要立即体验更新版本,可以使用强制更新
    • 打开chrome://extensions/,选择开发者模式,然后点击更新按钮,强制更新全部扩展程序
    • chrome账户重新登录,在登录状态下安装的扩展程序,重新登录后chrome会进行数据同步,此时也会更新到最新的版本
    • 修改chrome扩展更新频率,可以通过在启动chrome时通过参数--extensions-update-frequency=45指定更新检测频率

采用非公开方式发布扩展程序,用户只需要第一次点击链接进行扩展程序的安装即可,后续更新都会由chrome自动更新完成,之前通过接口获取配置规则的逻辑也可由发布新版本的机制代替。

1.1. Chrome应用商店开发者中心相关操作流程

首先进入开发者中心https://chrome.google.com/webstore/developer/dashboard/g05227480483751387811?page=1&pli=1,应该需要输入当前Chome账号密码

在“您的列表”下面找到“添加新内容”,打开上传页面;也可以选在已发布的插件,点击右侧的修改操作,进入编辑页面,然后重新上传更新后的包

点击“选择文件”,选择npm run build打包(这里是本地的chrome插件打包脚本,在文末有相关代码)的压缩文件,然后点击上传,等到文件上传完毕,会自动跳转到编辑页面

在编辑页面需要填写扩展程序的基本信息,如介绍、图标、轮播图等,此处填写的内容会经过应用商店的审核,某些内容可能会导致审核不通过~

编辑完成后拉到页面最下面,点击发布按钮即可,此处的公开程度选项决定了是否在应用商店展示该扩展程序。

发布更改后等待一段时间(一般几分钟到几个小时不等),就可以在应用商店安装对应的扩展程序了。

之前经历过连续审核未通过的情形,如果审核未通过,会发送相关信息到该开发者的关联邮箱,需要根据被拒邮件列举的理由进行调整并重新发布更改,常见的理由有

  • 未上传图标、空白描述、无预览效果图等
  • 未描述产品隐私策略,需要附上隐私政策网址,在描述中详细介绍使用用户数据做什么事情

2. 开发者模式下安装包的自动更新

某些插件由于种种原因,并不方便托管在应用商店,如

  • 安全性、隐私性要求极高,即使是发布时选择“私密”公开程度也无法满足需求
  • 插件功能导致很难通过Chrome应用商店审核

恰好某个插件基于以上原因,不能托管在应用商店,此外,由于插件内置了某些特定账号,每次发布时需要根据特定账号发布不同的版本。

因此选择的部署策略是:插件部署在一台远程机器上,用户使用不同的远程桌面账号进行登录,每个用户的远程桌面Chrome都使用开发者模式安装对应版本的扩展程序。

如文章开头提到的问题:由于远程桌面及安装插件对于用户来说是透明的,需要开发者来实现自动更新策略,换言之,每次更新插件,都需要打包对应版本,然后登录所有的远程桌面账号,手动安装插件的开发者版本,比较繁琐。

基于上述情形,调研了相关的自动更新机制。

2.1. 方案一

将插件托管在应用商店,依赖应用商店的自动更新机制,实现插件后续的迭代,该方案存在下面几个问题

  • 第一次上架后,需要手动为现有的远程桌面安装对应的扩展程序,

    • 可以利用chrome登录账号的同步安装功能实现,需要准备多个个chrome账号,安装对应特定账号的chrome扩展程序,然后在对应的远程桌面上登录该chrome账号即可
  • 需要遵循应用商店审核机制,较为繁琐,扩展程序的保密等级可以设置为私有:只允许指定用户使用插件

  • 由于插件内置原始账号,需要打包不同的版本,相当于需要在应用商店上架多个个类似的扩展程序,

    • 审核风险较大
    • 每次发布更新都需要上传多个更新包
  • 如果采用chrome账号同步的扩展程序的方式,可以尝试根据将chrome账号和特定账号一一绑定,这样只需要发布一个版本,根据登录的chrome账号进行区分不同的特定账号即可,chrome.identity提供了oAuth的相关的接口

小结:使用chrome应用商店托管的方法 ,主要的问题是解决版本更新审核的问题,且每次版本更新都必须经历该审核

2.2. 方案二

根据window组策略安装定制化的chrome,通过组策略,每次更新时预装对应的扩展程序,并重新安装chrome浏览器

参考

这种方式绕开了应用商店的审核,但需要了解windows组策略的相关使用和批量安装软件机制。

此方案尚未经过完整demo测试,理论上是为chrome企业版安装内置插件的方案

2.3. 方案三

通过 **其他部署方式** 安装 扩展程序

chrome支持两种外部安装扩展程序的方法

  • 使用首选项 JSON 文件(仅用于 Mac OS X 和 Linux)
  • 使用 Windows 注册表(仅用于 Windows)

由于远程桌面均采用windows系统,因此可以通过windows注册表安装chrome扩展程序,经测试是可行的

  • windows搜索框输入 regedit 打开注册表管理
  • 64位系统 根据路径 HKEY_LOCAL_MACHINE\Software\Wow6432Node\Google\Chrome\Extensions 找到对应的配置目录
  • 获取插件的id,以该值新增项,然后再该项下新增键 update_url 和值 https://clients2.google.com/service/update2/crx
  • 然后重新打开chrome,就可以看见已安装对应的插件,默认插件非开启disable状态,会弹出是否打开插件的询问框

可以通过window脚本,自动修改注册表,然后安装插件。

处于安全考虑,windows通过注册表安装的扩展程序,无法指定本地安装目录,只能安装托管在应用商店的扩展程序,因此面临着与方案一相同的发版和审核问题。

在调研过程中发现了一款叫做 “ Chrome 插件伴侣 ” 的window工具,可以自动将本地crx文件解压,通过开发者模式安装本地的扩展程序。

2.4. 方案四

由于插件的特殊性,是部署在同一台远程机器上,通过不同的windows账号分发进行使用的,原始打包的四份插件位于系统的C盘目录下,所有账号均可访问该文件路径,之前的更新方式就是上传新版本插件解压包,然后选择加载对应的新版本插件目录,重新安装。

可以通过chrome.runtime.reload() 接口,重启插件,此时chrome会重新加载整个插件目录,如果在每次更新时覆盖上一次的插件目录,再调用一次reload,即可达到更新插件的目的,调用reload的时机有下面几种方式

  • 可在插件的popup.html弹出窗口上增加一个更新按钮,用户手动调用一次即可。
  • 实现一个插件版本检测的接口,定期检测到有新版本时,则后台调用reload,更新文件

这种方式修改成本较低,只需要更新时覆盖上一次的插件目录,然后调用一次reload即可。缺点在于新增的用户仍旧需要手动安装一次插件

下面新增版本号接口

    pluginVersion: function* (next) {
        let data = {
            version: 2.0,
            // 此字段用于强制更新
            // 为true则不会弹出confirm询问框,直接重新加载插件
            // 需要确保磁盘上的插件版本号与data.version一致,否则会反复尝试重新加载,触发chrome加载频繁的警告
            isForceUpdate: true,
        }
        this.body = {
            data: data,
            msg: 'success',
            code: 0
        };
    }

3. 其他

3.1. manifest中配置更新

在调研时还发现,可以在manifest.json中通过指定update_url的方式实现插件的自动更新,由于测试demo时发现貌似没有生效,因此摒弃了该方案,但是理论上该方式是“为了实现网上应用店以外的地方托管您的扩展程序”,具体未生效的原因还没有深究,这里暂且记录下来,以免遗忘。

具体步骤为

  • 在manifest.json中通过指定update_url,格式类似于

    "update_url": "http://host.com/updates.xml",
  • Chrome会定期定期该url,如果返回的xml文件内容指定的version版本号高于当前版本,则会向其中指定的codebase路径重新下载crx文件,更新对应插件

具体操作可以参考自动更新-官方文档

3.2. 本地打包脚本

由于开发预览需要及时更新的文件,因此采用gulp + watch的形式搭建开发环境,使用gulp-webpack进行打包,此外为了方便上传和分发插件压缩包,编写了一个本地打包的脚本

let shell = require('shelljs')
let name = process.argv.splice(2)[0];

function build(name) {
    // 整理插件需要的文件
    shell.exec('gulp js --env production')

    // todo 新增的html单独放在某个目录下面,如下面的variation.html
    let files = [
        'images/*', 'js/*', 'lib/*', 'background.html', 'manifest.json', 'options.html', 'popup.html',
    ].join(' ')

    let fileName = `${name}.zip`
    // 压缩
    shell.exec(`cd ./${name} && zip ${fileName} ${files} && mv ${fileName} ../dist`)
}

// todo 错误检测
if(name){
    build(name)
}else {
    console.log('没有指定打包插件的名字~')
}

4. 小结

这里整理了在项目中关于Chrome扩展程序更新的一些问题,

  • 对于常规的扩展程序,通过托管应用商店实现自动更新最为方便
  • 对于业务特定需求的扩展程序,需要根据具体常见进行分析,理论上通过“manifest中配置更新”应该可以实现,需要回头再研究一下