The previous article, Series 1, covered the principle in depth from the auto-update scenario, as well as the two packaging options.
If you are interested in a list of articles, you can read it here
Today, let’s take a look at some of the problems with automatic update development.
The project experienced in the article is adoptedelectron-vue
+ electron-builder
+ electron-release-server
Architecture.
Problems in development
This article was first published on the public account “Front-end Keep”. Welcome to follow it.
(一) Can not find Squirrel
1. The background
The update had the “Can not find Squirrel” problem. Why is this a problem? Let’s run it through Electron source code. Lot of Electron source address: https://GitHub.com/Electron/Electron.
2. The analysis
If you’re not familiar with Electron’s Autoupdate, you can read it here
The other modules in the Electron source are not analyzed too much in this article. The automatically updated modules are in the Electron lib API auto-updater folder.
Ts, auto-upater -native. Ts and squirrel-update-win.ts.
Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel Can not find Squirrel Squirrel
checkForUpdates () {
// This is the update server URL
const url = this.updateURL;
if(! url) {return this.emitError(new Error('Update URL is not set'));
}
// Whether updates are supported locally
if(! squirrelUpdate.supported()) {return this.emitError(new Error('Can not find Squirrel'));
}
// Check for updates
this.emit('checking-for-update');
squirrelUpdate.checkForUpdate(url, (error, update) = > {
// Update error throws an exception
if(error ! =null) {
return this.emitError(error);
}
// There is no update to remind the user
if (update == null) {
return this.emit('update-not-available');
}
this.updateAvailable = true;
// There are updates, automatically trigger download updates
this.emit('update-available');
squirrelUpdate.update(url, (error) = > {
if(error ! =null) {
return this.emitError(error);
}
const { releaseNotes, version } = update;
// Date is not available on Windows, so forge it.
const date = new Date(a);this.emit('update-downloaded', {}, releaseNotes, version, date, this.updateURL, () = > {
// Exit and install
this.quitAndInstall();
});
});
});
}
Copy the code
This method checks for newer versions and throws the error “Can not find Squirrel” if there is no supported method for Squirrel Update. Where does squirrelUpdate come from? Read on:
import * as squirrelUpdate from '@Electron/internal/browser/api/auto-updater/squirrel-update-win';
Copy the code
Squirrel -update-win.ts throws supported method: squirrel-update-win.ts
export function supported () {
try {
// Check whether there is a local update program
fs.accessSync(updateExe, fs.constants.R_OK);
return true;
} catch {
return false; }}Copy the code
This method checks whether the updateExe method is accessible. We go false, which means that the updateExe method is not accessible.
/ / i.e. for my - app/app - 0.1.13 /
const appFolder = path.dirname(process.ExecPath);
// i.e. my-app/Update.exe
const updateExe = path.resolve(appFolder, '.. '.'Update.exe);
Copy the code
After a reading, Electron goes to the installation directory to look for update.exe. If update. exe cannot be found, error “Can not find Squirrel” If found, the checkForUpdate method triggered by the timer can proceed smoothly, download the version specified by the URL, and write the new result to stdout. Update-not-available is triggered if there is no update. Finally, the update method is triggered to update the application to the latest remote version specified by the URL.
If your application is not installed, Calling Squirrel will not work. You need to install an application, and you can’t test automatic updates while debugging, which is a headache.
3. Solutions
The cause of the problem is known, it is not that difficult to solve it, as long as the installation is complete, provide update.exe. Squirrel – The XXX project for Windows provides update. exe, but Nsis doesn’t (it probably does, but I can’t find the configuration). This leads to question 2.
Tip: If you try to debug the application through Visual Studio, you will get an error saying “Update.exe not found, not a Squirrel-installed app”. You can solve this problem by placing a copy of update.exe in the bin directory.
2. In the installation directorypackages
The folder andUpdate.exe
Program not found
1. The background
If the electron Builder package is configured with Squirrel. Windows, it will automatically generate packages and update.exe in the installation directory. But NSIS will not.
Packages and update.exe will not be generated after the Electron autoupdate mechanism is configured with Nsis. Probably an Nsis problem, not integrated with updateManage. In any case, we will rewrite the Nsis installation to generate packages and update.exe in the installation directory.
2. Solutions
Minio is used as object storage in the project. You can choose Qiuniuyun, Ali Cloud and so on.
In the project, UploadAutoupDateDep.js adds the RELEASES file to Minio:
// Determine and delete the current version of the RELEASES file
minioClient.removeObject('XXX project', pkg.version, function (err) {
if (err) {
logger.error('cannot be deleted${pkg.version}Object `)
logger.error(err)
} else {
upload()
}
})
function upload () {
// Create the current RELEASES file
minioClient.fPutObject('XXX project'.`${pkg.version}/RELEASES`, releaseFile, metaData, function (err, etag) {
if (err) {
logger.error(err)
}
logger.info(`${pkg.version}Succeeded in uploading the /RELEASES file)})}Copy the code
The above code mainly deletes the existing RELEASE file in minio and uploads the current RELEASE file. The RELEASES file is a squirrel. Windows package that generates SHA1, the current NUPkg version, and the serial number.
Update.exe is never changed, so simply copy it to minio. In the project, getautoupDatedep.js supports downloading update dependencies:
// When the production environment starts the application, download 'update. exe' and the current version of 'RELEASES'
var BufferHelper = require('./bufferHelper')
module.exports = function () {
minioClient.getObject('XXX project'.`${pkg.version}/RELEASES`.function (err, dataStream) {
if (err) {
logger.error(err)
return console.log(err)
}
dataStream.on('data'.function (chunk) {
// Check whether folder Packages exists
if(! fs.existsSync('.. /packages')) {
fs.mkdirSync('.. /packages'.(err) = > {
if (err) throw err
logger.info('Packages directory created successfully')})}// Check whether the file RELEASES exist
// if (! fs.existsSync('.. /packages/RELEASES')) {
fs.writeFileSync('.. /packages/RELEASES', chunk, (err) = > {
if (err) {
logger.error(err)
}
logger.info('RELEASES the file successfully ')})// }
})
dataStream.on('end'.function () {
logger.info('End :RELEASES file successfully')
})
dataStream.on('error'.function (err) {
logger.error(err)
})
})
minioClient.getObject('XXX project'.'Update.exe'.function (err, dataStream) {
var bufferHelper = new BufferHelper()
if (err) {
logger.error(err)
return console.log(err)
}
dataStream.on('data'.function (chunk) {
bufferHelper.concat(chunk)
})
dataStream.on('end'.function () {
// Check whether the Update. Exe folder exists
if(! fs.existsSync('.. /Update.exe')) {
fs.writeFileSync('.. /Update.exe', bufferHelper.toBuffer(), (err) = > {
if (err) {
logger.error(err)
}
logger.info('Update.exe file downloaded successfully ')
})
}
logger.info('end: update. exe file downloaded successfully ')
})
dataStream.on('error'.function (err) {
logger.error(err)
})
})
}
Copy the code
This code implements two main functions:
- Check whether the installation directory exists
packages
The directory, if it exists, is deleted and rescheduledminio
Download; - Determine whether there is
Update.exe
If it doesn’t exist, download it.
Error: spawn UNKNOWN
1. The background
When updated, checkForUpdates detects that there is indeed a new version. Open the Packages folder and find that the nupkg file for the latest version of the application has been downloaded, but the update failed. The specific error is as follows:
[2020-10-09T10:29:28.047] [INFO] default-checkForupdates [2020-10-09T10:29:28.047] [ERROR] default-there was a Problem updating the application [2020-10-09T10:29:28.047] [ERROR] default-error: ERROR: Updating the application spawn UNKNOWN at AutoUpdater.emitError (electron/js2c/browser_init.js:17:1391) at electron/js2c/browser_init.js:17:968 at electron/js2c/browser_init.js:21:1005 at electron/js2c/browser_init.js:21:553 at processTicksAndRejections (internal/process/task_queues.js:79:11)Copy the code
2. Cause analysis
Once packed, update. exe was uploaded to minio, but there were problems downloading it.
The local update. Exe file is incomplete because writefilesync fails to write to the Exe file. Look at the arguments to fs.writefilesync (file, data[, options]) :
1. The file < string > | < Buffer > | < URL > | < integer > filename or file descriptors. 2. data <string> | <Buffer> | <TypedArray> | <DataView> 3. options <Object> | <string> 4. encoding <string> | <null> Default: 'utf8'. 5. Mode < INTEGER > Default: 0o666. 6. Flag <string> See support for file system flag. Default value: 'w'. 7. Return undefined.Copy the code
The problem is with buffer splicing. Before writing update.exe, I need to splice chunk together. Here is my original way of assembling them, because IN my mind I assembled them as strings:
var data = "";
res.on('data'.function (chunk) {
data += chunk;
})
.on("end".function () {
// Transcode data
});
fs.writeFileSync('.. /Update.exe'.new Buffer(), (err) = > {})
Copy the code
'new Buffer()' was deprecated since v6. Use 'Buffer.alloc()' or 'Buffer.from()' (use 'https://www.npmjs.com/package/safe-buffer' for '< 4.5.0' home)
The reason is that the two chunks (Buffer objects) are not properly spliced, which is equivalent to buffer.tostring () + buffer.tostring (). If the buffer is not complete, then the resulting string from toString is problematic (such as a Truncated Chinese character).
3. Solutions
Bufferhelper.js provides a way to concatenate buffers:
var BufferHelper = function () {
this.buffers = []
this.size = 0
this._status = 'changed'
}
BufferHelper.prototype.concat = function (buffer) {
for (var i = 0, l = arguments.length; i < l; i++) {
this._concat(arguments[i])
}
return this
}
BufferHelper.prototype._concat = function (buffer) {
this.buffers.push(buffer)
this.size = this.size + buffer.length
this._status = 'changed'
return this
}
BufferHelper.prototype._toBuffer = function () {
var data = null
var buffers = this.buffers
switch (buffers.length) {
case 0:
data = new Buffer(0)
break
case 1:
data = buffers[0]
break
default:
data = new Buffer(this.size)
for (var i = 0, pos = 0, l = buffers.length; i < l; i++) {
var buffer = buffers[i]
buffer.copy(data, pos)
pos += buffer.length
}
break
}
// Cache the calculated results
this._status = 'computed'
this.buffer = data
return data
}
BufferHelper.prototype.toBuffer = function () {
return this._status === 'computed' ? this.buffer : this._toBuffer()
}
BufferHelper.prototype.toString = function () {
return Buffer.prototype.toString.apply(this.toBuffer(), arguments)}module.exports = BufferHelper
Copy the code
There are two private methods, _concat and _toBuffer. _concat links buffer streams, and _toBuffer is converted to a buffer instance.
The goal is to ensure that each method has a single responsibility, and there are some state Settings in toBuffer that don’t waste CPU. Download the update. exe code in question 2.
conclusion
If series one is “what”, series two is “Why? How to do? “.
In this article, I listed three problems in the development, namely “Can not find Squirrel”, “Packages folder and update. exe program Can not be found in the installation directory” and “Error: spawn UNKNOWN”, analyzed and answered from different angles.
This article was first published on the public account “Front-end Keep”. Welcome to follow it.
Finally, I hope you must point to like three times.
You can read my other posts at the blog address