23

I have my own server where I uploaded app installer via FTP. Its name is quickmargo Setup 1.0.0.exe and it's available at

https://quickmargo.pl/dist/download/quickmargo Setup 1.0.0.exe

Also via FTP I uploaded latest.yml to same directory and it is available at

https://quickmargo.pl/dist/download/latest.yml

In my project in index.js I have

import { autoUpdater } from 'electron-updater'

autoUpdater.setFeedURL('https://quickmargo.pl/dist/download');

autoUpdater.on('update-downloaded', () => {
    autoUpdater.quitAndInstall()
});

autoUpdater.on('update-available', (ev, info) => {
    alert('Update required!');
});

app.on('ready', async () => {
    if (process.env.NODE_ENV === 'production') {
        await autoUpdater.checkForUpdates()
    }
});

In package.json I have "version": "1.0.0", and inside build:{} I have:

"win": {
  "icon": "build/icons/icon.ico",
  "publish": [{
    "provider": "generic",
    "url": "https://quickmargo.pl/dist/download"
  }]
},

( I don't care about other platforms )

Now let's say I've made some changes in my app and I want to upload version 1.0.1 and I want my app to auto update if someone already downloaded installer and installed my app on his machine.

Tell me please if everything what I made so far is fine and what is next step. I consider following:

  • change version to 1.0.1 in package.json
  • run build command in terminal again
  • upload manually new installer to same place at my server

Edit

I did above three steps plus I also uploaded new latest.yml ( with version 1.0.1 ) and result is that when I now run previously installed (before uploading new version to server) version 1.0.0 on other PC then it doesn't detect that I added 1.0.1 to server and it doesn't update or show some popup or anything. What I'm doing wrong?

Edit 2

I'm trying to solve it on my own and now I uploaded 1.0.2 so now link to download app is:

https://quickmargo.pl/dist/download/quickmargo Setup 1.0.2.exe

Edit 3

I was trying to solve it on my own I edited code in index.js. I edited also above. alert('Update required!'); on update-available event never occure. It should show me error message window that alert is undefined. But apparently update-available event is never emitted.


Additional info:

  • My app was generated with vue-electron v1.0.6 boilerplate.
  • My electron-updater version is 4.1.2
  • npm run build actually invoke some code from boilerplate which is in .electron-vue/build.js you can see this file in above link (for example it set NODE_ENV to production. Script in package.json is: "build": "node .electron-vue/build.js && electron-builder",.
  • I don't want to host releases at github because my repository is private and I saw some information in electron.build docs that I shoudn't do that.
  • I also saw info in some issue that I could create new repo only for releases but I consider hosting everything at my own server as more clean approach.
Constantin Groß
  • 10,719
  • 4
  • 24
  • 50
BT101
  • 3,666
  • 10
  • 41
  • 90
  • There is an example here: https://github.com/iffy/electron-updater-example which covers "custom" updates – Lawrence Cherone Oct 27 '19 at 19:03
  • So what I do wrong? – BT101 Oct 27 '19 at 19:08
  • Run your packed app via the cmd to see the logs, having these in the question can be helpful – oktapodia Oct 28 '19 at 11:01
  • Do I need application to be signied with certificate in order to run auto-update correctly? – BT101 Oct 28 '19 at 17:16
  • @dopeCode no, autoupdate should work without code signature, if you don't see any error after starting your built exe in the terminal, use a logger like `electron-log` to give the autoUpdater a logger to use ( `const logger = require("electron-log"); autoUpdater.logger = logger; logger.transports.file.level = "debug";`) - this should print out information if your server was found and whether an update was found. If you get an error you can add it to your question. – Rhayene Oct 29 '19 at 17:44
  • also for faster test iterations, you can use `npx http-server ` on your dist directory – Rhayene Oct 29 '19 at 17:53

2 Answers2

22

I was able to set up an auto update configuration using a generic publish option following the docs, having never done it before. So it's definitively doable, and it does not require signing via a certificate, but I initially had issues because I had set publisherName in the build config, but no certificate. If the current version had a publisher or certificate specified, and the new one does not, it will also not be installed.

1. Enable logging

You can enable logging of the electron-updater package by also installing electron-log and then assigning the logger to the autoUpdater:

const log = require('electron-log');
autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = 'info';

The default output paths are:

  • Linux: ~/.config/<app name>/log.log
  • macOS: ~/Library/Logs/<app name>/log.log
  • Windows: %USERPROFILE%\AppData\Roaming\<app name>\log.log

If the following steps don't solve your issues, please post the log contents.

2. Don't call autoUpdater.setFeedURL()

The official docs state:

Do not call setFeedURL. electron-builder automatically creates app-update.yml file for you on build in the resources (this file is internal, you don’t need to be aware of it).

The URL is already defined in your publish provider object and that's enough for the updater to work with. Also, a URL string as argument of setFeedURL() is incorrect, it should be an options object. But again, specifying everything in your publish provider is sufficient.

3. Also upload the .blockmap files to your server

These should be created upon build in addition to your setup .exe files. Otherwise, you will see errors in your log that the files of the old and new version could not be found for comparison.

4. Add a trailing slash to your update server URL

Make sure that the url parameter of your provider object ends with a slash. While the yml file may still be found without it, there can be issues during the actual download otherwise.

5. Try the simpler approach using autoUpdater.checkForUpdatesAndNotify()

Instead of using the more flexible, but also more complicated way listening to the different update events and reacting to them within your app, try to get it to work with the following code first. Once that works, you can still go back to handling the different events for a better user experience.

app.on('ready', async () => {
  autoUpdater.checkForUpdatesAndNotify();
});

This will check for and download the update in the background and automatically install it as soon as you close your app. A default Windows notification will pop up to inform you about the available update and the procedure.

Constantin Groß
  • 10,719
  • 4
  • 24
  • 50
  • I added logger and now I see that it actually find new release, it even shows how many kb need to be downloaded: `Full: 151,317.35 KB, To download: 1,152.69 KB (1%)` but after that nothing happen.. App is not updating. Are you sure that `checkForUpdatesAndNotify` is enough to make app quit if it found new release then download new release and install it? – BT101 Nov 18 '19 at 19:48
  • Well I added this listeners back again and on `update-available` I log some text which I can see but `update-downloaded` is never emitted as I can not see log on this event. What might be the reason that it doesn't start/finish (tbh I don't even know which one) downloading? – BT101 Nov 18 '19 at 21:00
  • `checkForUpdatesAndNotify` should show a notification once the download was successful and then the app should update once you close it. But if the `update-downloaded` event never fires, you won't get that far. However, it's hard for me to say what's causing the issue. – Constantin Groß Nov 19 '19 at 06:43
  • Yes that's basically my behaviour `update-available` is emitted but `update-downloaded` is not. I don't know why yet. I'll try to reproduce it new, emty repository. – BT101 Nov 19 '19 at 20:17
  • Yhm now it started to work. Although I didn't change single line of code. I just waited like a day, during that time restarted PC also. It's working but it's downloading full app and it log error: `Cannot download differentially, fallback to full download: Error: Received data length 1527 is not equal to expected 634.` Also it try to update after app exit and ask for admin permission. And if I don't allow then I can run app again with previous version which is behaviour that I don't want. I want to force users to install update. I'm using autoUpdater.quitAndInstall() but it's not quiting. – BT101 Nov 20 '19 at 12:55
  • Then it sounds like the new version is requesting elevated execution. Did you upload the `.blockmap` files as well? It doesn't sound like it's missing, tough, rather like an issue with the request headers it's sending to the update server... That beats me, however. You could also try to set `differentialPackage: false` – Constantin Groß Nov 20 '19 at 13:40
  • Now it's not working again... I'm sick of it... Should it be appname.latest.yml or just latest.yml? Where should i add `differentialPackage: false`? Basically my log looks like this: https://gist.github.com/d0peCode/03177d8aefdc2c11148ec7160ba817a9 Also I added some logs after `checkForUpdatesAndNotify` and it seems that promise returned from this method is never resolved as I don't get log on `.then` therefore `update-downloaded` is not emitted. – BT101 Nov 21 '19 at 13:44
  • I have publish->provider = generic; should target be `nsis` or also `generic`? Currently it's `nsis`. – BT101 Nov 21 '19 at 14:22
  • Should I also upload `builder-effective-config.yaml` and `win-unpacked` directory to same place at server where `.exe` and `.exe.blockmap` is located? – BT101 Nov 21 '19 at 16:12
  • `publish:[ { ... } ]` should be within `win: {}` or within `build: {}`? Or it can be either in both places? Url shoulb be `"url": "https://quickmargo.pl/dist/download"` or `"url": "https://quickmargo.pl/dist/download/"` (with additional `/` at the end) ? – BT101 Nov 21 '19 at 16:19
  • According to the logs, the update is found and the blockmap file is read correctly. So your config seems to be correct, but just to make sur: `publish: { provider: ... }` is correct, `target` is not needed I believe. You only need to upload the exe, blockmap and latest.yml files. And `publish` should be in `win`. If the trailing slash should be handled automatically, but try adding it. If it still doesn't work, its something specific on your end (firewall, other parts of the app setup...) and impossible to debug from remote. – Constantin Groß Nov 21 '19 at 17:42
  • I'll try to do so then if it won't work I'll reproduce it with sample repository maybe it will help to solve the issue. – BT101 Nov 21 '19 at 17:53
  • Actually it seems working correctly after adding slash at the end of path in publish and that was my issue entire time. LOL – BT101 Nov 21 '19 at 23:32
  • Glad to hear you finally fixed it! :) I'll add that to the answer so people don't have to look through all the comments! – Constantin Groß Nov 22 '19 at 07:40
  • Maybe you know why everytime that I run build latest.yml have other sha512? Now I run into error which says basically `sha512 mismatch`. – BT101 Dec 01 '19 at 14:44
  • After a year I have much more exp with that and it would be also worth to notice to not replace effective-build.yaml on your server. Otherwise auto update can break. – BT101 Dec 30 '20 at 09:25
  • @BT101 I have the same problem as i tried this solution but still the same i am stuck can you provide an example of your main.js and package.json. – Kramti Apr 26 '21 at 14:24
  • What if we publish and fetch updates from different servers? How can we define one server in the "publish" key of the config, but manage to get an app-update.yml file with a different repo name for example? Why would it assume that the same server should act both as a release and an update one? – mliakos Oct 20 '21 at 19:36
1

I would like to introduce https://github.com/electron-delta/electron-delta-updater here.

It uses the binary diffing approach to generate very small delta files to update your app instead of the older blockmap approach.

enter image description here

npm install @electron-delta/updater
const DeltaUpdater = require("@electron-delta/updater");
const { app, BrowserWindow } = require("electron");

app.whenReady().then(async () => {

  const deltaUpdater = new DeltaUpdater({
    logger: require('electron-log'),
    // optionally set the autoUpdater from electron-updater
    autoUpdater: require("electron-updater").autoUpdater,
    // Where delta.json is hosted, for github provider it's not required to set the hostURL
    hostURL: "https://example.com/updates/windows/",
  });

  try {
    await deltaUpdater.boot();
  } catch (error) {
    logger.error(error);
  }
  // create main app window after the deltaUpdater.boot()
  createMainWindow();

});