我希望将自动更新功能部署到我拥有的Electron安装中,但是我发现很难在网络上找到任何资源。
我以前使用Adobe Air构建了一个自包含的应用程序,编写更新代码可以更轻松地编写有效检查URL并自动下载并在Windows和MAC OSX上安装更新。
我目前正在使用electron-boilerplate来简化构建。
我有几个问题:
答案 0 :(得分:4)
我也是Electron的新手,但我认为电子样板(我也使用)没有简单的自动更新。 Electron的自动更新程序使用Squirrel.Windows安装程序,您还需要将其实施到您的解决方案中才能使用它。
我目前正在尝试使用此功能:
可在此处找到更多信息:
在目前的配置中,我使用electronic-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脚本。
答案 1 :(得分:0)
你也可以在OS X上使用标准Electron的autoUpdater模块,在Windows上使用它的简单端口:https://www.npmjs.com/package/electron-windows-updater
答案 2 :(得分:0)
我遵循了这个tutorial并使用我的电子应用程序,虽然它需要签名才能工作,所以你需要:
certificateFile: './path/to/cert.pfx'
在任务配置中。
和
"build": {
"win": {
"certificateFile": "./path/to/cert.pfx",
"certificatePassword": "password"
}
},
在package.json
中答案 3 :(得分:0)
是否有任何好的资源详细说明如何实现自动更新功能?由于我很难找到一些有关如何执行此操作的文档。
您不必自己实施。您可以使用Electron提供的autoUpdater
,而只需设置feedUrl
。您需要一个提供符合Squirrel协议更新信息的服务器。
有几个自托管(https://electronjs.org/docs/tutorial/updates#deploying-an-update-server)或托管服务,例如https://www.update.rocks
答案 4 :(得分:0)
我使用 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"
}
是的,your Electron App must be code signed to use the auto-update feature on Mac。在 Windows 上我不确定,因为我的 Windows Electron 应用程序是代码签名的,没有它我没有尝试。尽管建议您签署您的应用程序,即使自动更新可以在没有它的情况下工作(不仅出于安全原因,而且主要是因为否则您的用户在第一次安装您的应用程序时会从 Windows 收到可怕的危险警告,他们可能直接删除即可)。
对于好的文档,您应该从 official Electron Auto Updater documentation 开始,从 2021-07-07 开始,它真的很好。
困难的部分是弄清楚如何让 Mac 工作。对于 Windows,只需几分钟即可完成。其实...
对于 Windows 自动更新,设置很容易 - 您只需将 RELEASES 和 nupkg 文件放在服务器上,然后使用该 URL 作为 FeedURL在您的电子应用程序的自动更新程序中。因此,如果您的应用程序的更新文件位于 https://my-server.com/updates/win32/x64/ - 您可以将 Electron Auto Updater 指向该 URL,就是这样。
对于 Mac 自动更新,您需要手动指定最新 Electron App .zip 文件的绝对 URL 到 Electron autoUpdater。因此,为了使 Mac autoUpdater 工作,您需要有一种方法来获取 a JSON response in a very specific format。遗憾的是,您不能将 Electron 应用程序的文件放在您的服务器上,并期望它像那样与 Mac 一起使用。相反,autoUpdater 需要一个将返回上述 JSON 响应的 URL。为此,您需要向 Electron 的自动更新程序 feedURL
传递能够返回这种预期类型的 JSON 响应的 URL。
您实现这一目标的方式可以是任何方式,但我使用 PHP 只是因为那是我已经付费购买的服务器。
总而言之,对于 Mac,即使您的文件位于 https://my-server.com/updates/darwin/x64/,您也不会将该 URL 提供给 Electron 的 Auto Updater FeedURL。相反,将提供另一个返回预期 JSON 响应的 URL。
以下是我的应用程序的 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 Auto Updater:
<?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);
}