电子软件的自动更新

11

我想要给Electron应用程序添加一个自动更新功能,但是在网络上找不到相关的资源。

之前我使用Adobe Air构建了一个自包含的应用程序,编写更新代码变得容易,可以有效地检查URL,并自动在Windows和MAC OSX之间下载和安装更新。

目前我正在使用electron-boilerplate来简化构建过程。

我有几个问题:

  • 如何调试自动更新功能?我需要设置本地连接并通过本地Node服务器测试吗?还是可以使用任何Web服务器?
  • 关于签署应用程序,我只想在MAC OSX和Windows上运行应用程序。我是否必须签署应用程序以实现自动更新?(我曾使用本地证书在Adobe Air中完成此操作。)
  • 有没有好的资源详细说明如何实现自动更新功能?因为我很难找到一些好的文档。
5个回答

4

我对Electron也是新手,但我认为从electron-boilerplate中没有简单的自动更新(我也在使用)。Electron的自动更新器使用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;
}

您需要在Electron的后台/主代码中添加一些针对Squirrel的代码。我使用了一个名为electron-squirrel-startup的模板。

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

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

非常感谢 Josef。我也在处理这个问题,如果我成功配置了,我会让你知道的... - Neil Young
所以我到了某个地方,并在我的答案中添加了一些信息。 - Josef Jura
1
不错,你看过这个吗:https://github.com/GitbookIO/nuts它可以帮助你设置自己的服务器,并在 Web 钩子上监听 Github 推送以进行自动更新。虽然我在我的应用程序中有大型视频,所以想避免尝试对资产进行源代码控制... - Neil Young
好的,我会看一下。谢谢。 - Josef Jura

0

问题1:

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

使用Postman测试Mac端点的示例

Request: 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:

是的,在Mac上使用自动更新功能,你的Electron应用程序必须进行代码签名。至于Windows,我不确定,因为我的Windows Electron应用程序已经进行了代码签名,并且我没有尝试过没有代码签名的情况。尽管如此,建议您即使自动更新可以在没有代码签名的情况下正常工作(不仅是出于安全原因,而主要是因为否则当用户第一次安装应用程序时,他们会从Windows收到可怕的危险警告,他们可能会立即将其删除),也应该签署应用程序。


问题3:

为了获得良好的文档,您应该从官方Electron自动更新程序文档开始,截至2021年7月7日,它非常好。

难点在于如何让Mac正常工作。对于Windows来说,只需要几分钟就可以完成。实际上...

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

针对 Mac 自动更新,您需要手动指定最新的 Electron App .zip 文件的绝对 URL 给 Electron autoUpdater。因此,为了使 Mac autoUpdater 正常工作,您需要找到一种方法来获取 一个特定格式的 JSON 响应。不幸的是,您不能只是将 Electron App 的文件放在服务器上,然后期望它能像其他平台一样与 Mac 兼容。相反,autoUpdater 需要一个 URL 来返回上述 JSON 响应。因此,您需要将 Electron 的 Auto Updater feedURL 传递给能够返回这种预期 JSON 响应的 URL。

您可以使用任何方式来实现这一点,但我使用 PHP,因为那是我已经支付的服务器。

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

这是我应用程序的 Electron 主进程的 main.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);
}


0

我按照这个tutorial操作指南进行了操作,并使它在我的电子应用程序中工作,尽管需要签名才能运行,因此您需要:

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

在任务配置中。

以及:

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

在 package.json 文件中


0

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接