自动更新到 Electron

Posted

技术标签:

【中文标题】自动更新到 Electron【英文标题】:Auto-updates to Electron 【发布时间】:2016-03-11 21:20:33 【问题描述】:

我希望将自动更新功能部署到我拥有的 Electron 安装中,但是我发现很难在网络上找到任何资源。

我之前使用 Adob​​e Air 构建了一个自包含的应用程序,编写更新代码似乎要容易得多,它可以有效地检查一个 url 并在 Windows 和 MAC OSX 上自动下载和安装更新。

我目前使用electron-boilerplate 来简化构建。

我有几个问题:

如何调试自动更新功能?我是使用本地节点服务器设置本地连接并通过该连接进行测试,还是可以使用任何网络服务器? 在签署应用程序方面,我只希望在 MAC OSX 尤其是 Windows 上运行应用程序。我是否必须签署应用程序才能运行自动更新? (我设法使用本地证书通过 Adob​​e Air 做到了这一点。 有没有详细介绍如何实现自动更新功能的好资源?因为我很难找到有关如何执行此操作的一些好的文档。

【问题讨论】:

【参考方案1】:

问题一:

我使用Postman 来验证我的自动更新服务器 URL 是否返回了我期望的响应。当我知道这些 URL 提供了预期的结果时,我知道我可以在我的应用程序的 Electron's Auto Updater 中使用这些 URL。

使用 Postman 测试 Mac 端点的示例

请求: https://my-server.com/api/macupdates/checkforupdate.php?appversion=1.0.5&cpuarchitecture=x64

有可用更新时的 JSON 响应:


    "url": "https:/my-server.com/updates/darwin/x64/my-electron=app-x64-1.1.0.zip",
    "name": "1.1.0",
    "pub_date": "2021-07-03T15:17:12+00:00"


问题2:

是的,your Electron App must be code signed to use the auto-update feature on Mac。在 Windows 上我不确定,因为我的 Windows Electron 应用程序是代码签名的,没有它我没有尝试。虽然建议您签署您的应用程序,即使自动更新可以在没有它的情况下运行(不仅出于安全原因,而且主要是因为否则您的用户在第一次安装您的应用程序时会收到来自 Windows 的可怕危险警告,他们可能立即删除它)。


问题3:

要获得好的文档,您应该从 official Electron Auto Updater documentation 开始,截至 2021-07-07,它真的很好。

困难的部分是弄清楚如何让 Mac 工作。对于 Windows,只需几分钟即可完成。其实……

对于 Windows 自动更新,设置很容易 - 您只需将 RELEASES 和 nupkg 文件放在服务器上,然后将该 URL 用作 FeedURL在您的 Electron App 的 autoUpdater 中。因此,如果您的应用的更新文件位于 https://my-server.com/updates/win32/x64/ - 您可以将 Electron Auto Updater 指向该 URL,就是这样。

对于 Mac 自动更新,您需要手动将最新的 Electron App .zip 文件的绝对 URL 指定到 Electron autoUpdater。因此,为了使 Mac 自动更新程序正常工作,您需要有一种方法来获取 a JSON response in a very specific format。可悲的是,您不能只是将 Electron 应用程序的文件放在服务器上,并期望它可以像这样与 Mac 一起使用。相反,autoUpdater 需要一个返回上述 JSON 响应的 URL。为此,您需要将能够返回这种预期类型的​​ JSON 响应的 URL 传递给 Electron 的自动更新程序 feedURL。 实现这一点的方式可以是任何东西,但我使用 PHP 只是因为那是我已经支付的服务器。

因此,总而言之,对于 Mac,即使您的文件位于 https://my-server.com/updates/darwin/x64/ - 您也不会将该 URL 提供给 Electron 的 Auto Updater FeedURL。而是提供另一个返回预期 JSON 响应的 URL。

以下是我的应用的 Electron 主进程的 ma​​in.js 文件示例:

// main.js (Electron main process)
function registerAutoUpdater() 
    const appVersion = app.getVersion();
    const os = require('os');
    const cpuArchitecture = os.arch();

    const domain = 'https://my-server.com';

    const windowsURL = `$domain/updates/win32/x64`;
    const macURL = `$domain/api/macupdates/checkforupdate.php?appversion=$appVersion&cpuarchitecture=$cpuArchitecture`;

    //init the autoUpdater with proper update feed URL
    const autoUpdateURL = `$isMac ? macURL : windowsURL`;
    autoUpdater.setFeedURL(url: autoUpdateURL);
    log.info('Registered autoUpdateURL = ' + (isMac ? 'macURL' : 'windowsURL'));

    //initial checkForUpdates
    autoUpdater.checkForUpdates();

    //Automatic 2-hours interval loop checkForUpdates
    setInterval(() => 
        autoUpdater.checkForUpdates();
    , 7200000);

以下是 checkforupdate.php 文件 的示例,该文件将预期的 JSON 响应返回给 Electron 自动更新程序:

<?php
//FD Electron App Mac auto update API endpoint.
// The way Squirrel.Mac works is by checking a given API endpoint to see if there is a new version.
// If there is no new version, the endpoint should return HTTP 204. If there is a new version,
// however, it will expect a HTTP 200 JSON-formatted response, containing a url to a .zip file:
// https://github.com/Squirrel/Squirrel.Mac#server-support

$clientAppVersion = $_GET["appversion"] ?? null;
if (!isValidVersionString($clientAppVersion)) 
    http_response_code(204);
    exit();

$clientCpuArchitecture = $_GET["cpuarchitecture"] ?? null;

$latestVersionInfo = getLatestVersionInfo($clientAppVersion, $clientCpuArchitecture);
if (!isset($latestVersionInfo["versionNumber"])) 
    http_response_code(204);
    exit();


// Real logic starts here when basics did not fail
$isUpdateVailable = isUpdateAvailable($clientAppVersion, $latestVersionInfo["versionNumber"]);
if ($isUpdateVailable) 
    http_response_code(200);
    header('Content-Type: application/json;charset=utf-8');
    $jsonResponse = array(
        "url" => $latestVersionInfo["directZipFileURL"],
        "name" => $latestVersionInfo["versionNumber"],
        "pub_date" => date('c', $latestVersionInfo["createdAtUnixTimeStamp"]),
    );
    echo json_encode($jsonResponse);
 else 
    //no update: must respond with a status code of 204 No Content.
    http_response_code(204);

exit();
// End of execution.
// Everything bellow here are function declarations.


function getLatestVersionInfo($clientAppVersion, $clientCpuArchitecture): array 

    // override path if client requests an arm64 build
    if ($clientCpuArchitecture === 'arm64') 
        $directory = "../../updates/darwin/arm64/";
        $baseUrl = "https://my-server.com/updates/darwin/arm64/";
     else if (!$clientCpuArchitecture || $clientCpuArchitecture === 'x64') 
        $directory = "../../updates/darwin/";
        $baseUrl = "https://my-server.com/updates/darwin/";
    

    // default name with version 0.0.0 avoids failing
    $latestVersionFileName = "Finance D - Tenue de livres-darwin-x64-0.0.0.zip"; 

    $arrayOfFiles = scandir($directory);
    foreach ($arrayOfFiles as $file) 
        if (is_file($directory . $file)) 
            $serverFileVersion = getVersionNumberFromFileName($file);
            if (isVersionNumberGreater($serverFileVersion, $clientAppVersion)) 
                $latestVersionFileName = $file;
            
        
    

    return array(
        "versionNumber" => getVersionNumberFromFileName($latestVersionFileName),
        "directZipFileURL" => $baseUrl . rawurlencode($latestVersionFileName),
        "createdAtUnixTimeStamp" => filemtime(realpath($directory . $latestVersionFileName))
    );


function isUpdateAvailable($clientVersion, $serverVersion): bool 
    return
        isValidVersionString($clientVersion) &&
        isValidVersionString($serverVersion) &&
        isVersionNumberGreater($serverVersion, $clientVersion);


function getVersionNumberFromFileName($fileName) 
    // extract the version number with regEx replacement
    return preg_replace("/Finance D - Tenue de livres-darwin-(x64|arm64)-|\.zip/", "", $fileName);


function removeAllNonDigits($semanticVersionString) 
    // use regex replacement to keep only numeric values in the semantic version string
    return preg_replace("/\D+/", "", $semanticVersionString);


function isVersionNumberGreater($serverFileVersion, $clientFileVersion): bool 
    // receives two semantic versions (1.0.4) and compares their numeric value (104)
    // true when server version is greater than client version (105 > 104)
    return removeAllNonDigits($serverFileVersion) > removeAllNonDigits($clientFileVersion);


function isValidVersionString($versionString) 
    // true when matches semantic version numbering: 0.0.0
    return preg_match("/\d\.\d\.\d/", $versionString);


【讨论】:

【参考方案2】:

有没有详细说明如何实现自动更新功能的好资源?因为我很难找到一些关于如何做到这一点的好文档。

您不必自己实现它。您可以使用 Electron 提供的 autoUpdater 并设置一个 feedUrl。您需要一个提供符合 Squirrel 协议的更新信息的服务器。

有几个自托管的 (https://electronjs.org/docs/tutorial/updates#deploying-an-update-server) 或像 https://www.update.rocks 这样的托管服务

【讨论】:

【参考方案3】:

我关注了这个tutorial 并让它与我的电子应用程序一起工作,尽管它需要签名才能工作,所以你需要:

 certificateFile: './path/to/cert.pfx'

在任务配置中。

和:

"build": 
  "win": 
    "certificateFile": "./path/to/cert.pfx",
    "certificatePassword": "password"
  
,

在 package.json 中

【讨论】:

【参考方案4】:

您还可以在 OS X 上使用标准 Electron 的 autoUpdater 模块,在 Windows 上使用我的简单端口:https://www.npmjs.com/package/electron-windows-updater

【讨论】:

【参考方案5】:

我也是 Electron 的新手,但我认为电子样板(我也使用)没有简单的自动更新。 Electron 的自动更新程序使用 Squirrel.Windows 安装程序,您还需要在解决方案中实施它才能使用它。

我目前正在尝试使用这个:

https://www.npmjs.com/package/electron-installer-squirrel-windows

更多信息可以在这里找到:

https://github.com/atom/electron/blob/master/docs/api/auto-updater.md https://github.com/squirrel/squirrel.windows

编辑:我刚刚打开项目尝试了一段时间,它看起来很有效。它非常简单。这些是我的 gulpfile 中的片段。

在当前配置中,我使用 electron-packager 创建一个包。

var packager = require('electron-packager')
var createPackage = function () 
    var deferred = Q.defer();


    packager(
        //OPTIONS
    , function done(err, appPath) 
        if (err) 
            gulpUtil.log(err);
        

        deferred.resolve();
    );

    return deferred.promise;
;

然后我使用 electron-installer-squirrel-windows 创建一个安装程序。

var squirrelBuilder = require('electron-installer-squirrel-windows');
var createInstaller = function () 
    var deferred = Q.defer();

squirrelBuilder(
// OPTIONS
, function (err) 
        if (err)
            gulpUtil.log(err);

        deferred.resolve();
    );

    return deferred.promise;

您还需要将一些 Squirrel 代码添加到您的电子背景/主代码中。我使用了模板电子松鼠启动。

if(require('electron-squirrel-startup')) return;

上面提到的 electron-installer-squirrel-windows npm 文档中描述了整个事情。看起来一点文档就足以让它开始。 现在我正在通过 Squirrel 进行电子品牌推广,并为自动化创建适当的 gulp 脚本。

【讨论】:

非常感谢约瑟夫。我也在努力,所以如果我能成功配置它,我会告诉你... 所以我到了某个地方并在我的答案中添加了一些信息 很好,你有没有看到这个:github.com/GitbookIO/nuts 它可以帮助你设置你自己的服务器,并在 web hook 上监听推送到 github 以进行自动更新。尽管我的应用程序中有大型视频,但可能值得探索,因此希望避免尝试对资产进行源代码控制... 很好,我会看看。谢谢

以上是关于自动更新到 Electron的主要内容,如果未能解决你的问题,请参考以下文章

electron mac签名 - 针对electron-builder的自动更新

大话Electron应用自动更新

如何测试电子生成器自动更新流程?

如何使用电子更新器自动更新电子应用程序?

怎么给electron应用程序更换图标

Electron实战:创建ELectron开发的window应用安装包