Skip to content

Instantly share code, notes, and snippets.

@iffy
Last active August 15, 2024 09:27
Show Gist options
  • Save iffy/0ff845e8e3f59dbe7eaf2bf24443f104 to your computer and use it in GitHub Desktop.
Save iffy/0ff845e8e3f59dbe7eaf2bf24443f104 to your computer and use it in GitHub Desktop.
Example using electron-updater with `generic` provider.
node_modules
dist/
yarn.lock
wwwroot

This repo contains the bare minimum code to have an auto-updating Electron app using electron-updater with releases stored on a plain HTTP server.

This example uses localhost as the release server.

  1. For macOS, you will need a code-signing certificate.

    Install Xcode (from the App Store), then follow these instructions to make sure you have a "Mac Developer" certificate. If you'd like to export the certificate (for automated building, for instance) you can. You would then follow these instructions.

  2. Install necessary dependencies with:

     yarn
    

    or

     npm install
    
  3. Build your app with:

     node_modules/.bin/build --win --mac --x64 --ia32
    
  4. Copy the files in the dist/ directory to your webserver. Here's how to do it on a Linux system:

     mkdir -p wwwroot
     cp dist/*.json wwwroot/
     cp dist/*.yml wwwroot/
     cp dist/mac/*.zip wwwroot/
     cp dist/mac/*.dmg wwwroot/
     cp dist/*.exe wwwroot/
    
  5. Serve wwwroot over HTTP:

     node_modules/.bin/http-server wwwroot/ -p 8080
    
  6. Download and install the app from http://127.0.0.1:8080

  7. Update the version in package.json.

  8. Do steps 3 and 4 again.

  9. Open the installed version of the app and see that it updates itself.

const {app, BrowserWindow, Menu, protocol, ipcMain} = require('electron');
const log = require('electron-log');
const {autoUpdater} = require("electron-updater");
//-------------------------------------------------------------------
// Logging
//
// THIS SECTION IS NOT REQUIRED
//
// This logging setup is not required for auto-updates to work,
// but it sure makes debugging easier :)
//-------------------------------------------------------------------
autoUpdater.logger = log;
autoUpdater.logger.transports.file.level = 'info';
log.info('App starting...');
//-------------------------------------------------------------------
// Define the menu
//
// THIS SECTION IS NOT REQUIRED
//-------------------------------------------------------------------
let template = []
if (process.platform === 'darwin') {
// OS X
const name = app.getName();
template.unshift({
label: name,
submenu: [
{
label: 'About ' + name,
role: 'about'
},
{
label: 'Quit',
accelerator: 'Command+Q',
click() { app.quit(); }
},
]
})
}
//-------------------------------------------------------------------
// Open a window that displays the version
//
// THIS SECTION IS NOT REQUIRED
//
// This isn't required for auto-updates to work, but it's easier
// for the app to show a window than to have to click "About" to see
// that updates are working.
//-------------------------------------------------------------------
let win;
function sendStatusToWindow(text) {
log.info(text);
win.webContents.send('message', text);
}
function createDefaultWindow() {
win = new BrowserWindow();
win.webContents.openDevTools();
win.on('closed', () => {
win = null;
});
win.loadURL(`file://${__dirname}/version.html#v${app.getVersion()}`);
return win;
}
autoUpdater.on('checking-for-update', () => {
sendStatusToWindow('Checking for update...');
})
autoUpdater.on('update-available', (ev, info) => {
sendStatusToWindow('Update available.');
})
autoUpdater.on('update-not-available', (ev, info) => {
sendStatusToWindow('Update not available.');
})
autoUpdater.on('error', (ev, err) => {
sendStatusToWindow('Error in auto-updater.');
})
autoUpdater.on('download-progress', (ev, progressObj) => {
sendStatusToWindow('Download progress...');
})
autoUpdater.on('update-downloaded', (ev, info) => {
sendStatusToWindow('Update downloaded; will install in 5 seconds');
});
app.on('ready', function() {
// Create the Menu
const menu = Menu.buildFromTemplate(template);
Menu.setApplicationMenu(menu);
createDefaultWindow();
});
app.on('window-all-closed', () => {
app.quit();
});
//-------------------------------------------------------------------
// Auto updates
//
// For details about these events, see the Wiki:
// https://github.com/electron-userland/electron-builder/wiki/Auto-Update#events
//
// The app doesn't need to listen to any events except `update-downloaded`
//
// Uncomment any of the below events to listen for them. Also,
// look in the previous section to see them being used.
//-------------------------------------------------------------------
// autoUpdater.on('checking-for-update', () => {
// })
// autoUpdater.on('update-available', (ev, info) => {
// })
// autoUpdater.on('update-not-available', (ev, info) => {
// })
// autoUpdater.on('error', (ev, err) => {
// })
// autoUpdater.on('download-progress', (ev, progressObj) => {
// })
autoUpdater.on('update-downloaded', (ev, info) => {
// Wait 5 seconds, then quit and install
// In your application, you don't need to wait 5 seconds.
// You could call autoUpdater.quitAndInstall(); immediately
setTimeout(function() {
autoUpdater.quitAndInstall();
}, 5000)
})
app.on('ready', function() {
autoUpdater.checkForUpdates();
});
{
"name": "electron-updater-generic-example",
"version": "0.2.0",
"main": "main.js",
"description": "electron-updater generic example project",
"author": "Matt Haggard",
"devDependencies": {
"electron": "^1.4.15",
"electron-builder": "^12.3.1",
"http-server": "^0.9.0"
},
"dependencies": {
"electron-log": "^1.3.0",
"electron-updater": "^1.4.2"
},
"build": {
"publish": [
{
"provider": "generic",
"url": "http://127.0.0.1:8080/"
}
],
"appId": "com.github.iffy.electronupdatergenericexample",
"mac": {
"category": "your.app.category.type",
"target": [
"zip",
"dmg"
]
},
"nsis": {
"perMachine": true
}
}
}
<!DOCTYPE html>
<html>
<head>
<title>Electron Updater Example</title>
</head>
<body>
Current version: <span id="version">vX.Y.Z</span>
<div id="messages"></div>
<script>
// Display the current version
let version = window.location.hash.substring(1);
document.getElementById('version').innerText = version;
// Listen for messages
const {ipcRenderer} = require('electron');
ipcRenderer.on('message', function(event, text) {
var container = document.getElementById('messages');
var message = document.createElement('div');
message.innerHTML = text;
container.appendChild(message);
})
</script>
</body>
</html>
@JPorter44
Copy link

@gannons I should have clarified, that I tried it both ways. The nsis-web folder had the .7z, latest.yml, and the .exe inside it. I have also tried putting the contents of nsis-web directly on the server as well (see image).
image
When I upload the new files I replace all 4 files is that what I should be doing?

@gannons
Copy link

gannons commented Apr 2, 2020

As long as http://testserver/img/username/latest.yml can be downloaded you should, at least, be getting update events. They may be error events if the rest of the structure is incorrect.

You should be able to see these events on the console with

autoUpdater.logger = console;

@JPorter44
Copy link

@gannons How do I see these events with the console? I am packaging and deploying my app like it would be in production so I can't attach a debugger. I don't have a console window pop up when I run my application.

@JPorter44
Copy link

@gannons I got it working, I am not sure what the exact fix was, possibly a misconfiguration in my main.js (rookie mistake). Thanks for helping me out!

@yanlee26
Copy link

Skip app dependencies rebuild because platform is different Error: Cannot check wine version: Error: Exit code: ENOENT. spawn wine ENOENT at /Users/elliotyan/Documents/me/updater-test/node_modules/electron-builder/src/packager.ts:399:13 at Generator.throw (<anonymous>) From previous event: at checkWineVersion (/Users/elliotyan/Documents/me/updater-test/node_modules/electron-builder/out/packager.js:57:22) at /Users/elliotyan/Documents/me/updater-test/node_modules/electron-builder/src/packager.ts:207:17 From previous event: at Packager.doBuild (/Users/elliotyan/Documents/me/updater-test/node_modules/electron-builder/out/packager.js:335:11) at /Users/elliotyan/Documents/me/updater-test/node_modules/electron-builder/src/packager.ts:151:38 at Generator.next (<anonymous>) at processImmediate (internal/timers.js:456:21) From previous event: at Packager.build (/Users/elliotyan/Documents/me/updater-test/node_modules/electron-builder/out/packager.js:261:11) at /Users/elliotyan/Documents/me/updater-test/node_modules/electron-builder/src/builder.ts:214:40 at Generator.next (<anonymous>) From previous event: at build (/Users/elliotyan/Documents/me/updater-test/node_modules/electron-builder/out/builder.js:63:21) at Object.<anonymous> (/Users/elliotyan/Documents/me/updater-test/node_modules/electron-builder/out/cli/build-cli.js:68:41) at Module._compile (internal/modules/cjs/loader.js:1158:30) at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10) at Module.load (internal/modules/cjs/loader.js:1002:32) at Function.Module._load (internal/modules/cjs/loader.js:901:14) at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12) at internal/main/run_main_module.js:18:47

@brennomarques
Copy link

Hello, you can solve the problem, tell me how it went, I'm like the same problem.

[2020-06-01 10:32:16.327] [info] Checking for update
[2020-06-01 10:32:17.387] [info] Found version 2.0.0 (url: projeto-teste-Setup-2.0.0.exe)
[2020-06-01 10:32:17.400] [info] update-available
[2020-06-01 10:32:17.413] [info] Downloading update from projeto-teste-Setup-2.0.0.exe
[2020-06-01 10:32:17.435] [info] No cached update info available
[2020-06-01 10:32:17.485] [info] Download block maps (old: "http://localhost:8080/projeto-teste-Setup-1.0.0.exe.blockmap", new: http://localhost:8080/projeto-teste-Setup-2.0.0.exe.blockmap)
[2020-06-01 10:32:17.519] [error] Cannot download differentially, fallback to full download: Error: Cannot download "http://localhost/projeto-teste-Setup-1.0.0.exe.blockmap", status 404: Not Found
    at ClientRequest.<anonymous> (C:\Users\brenno\AppData\Local\Programs\projeto-teste\resources\app.asar\background.js:1:129099)
    at ClientRequest.emit (events.js:194:13)
    at URLRequest.<anonymous> (C:\Users\brenno\AppData\Local\Programs\projeto-teste\resources\electron.asar\browser\api\net.js:207:12)
    at URLRequest.emit (events.js:194:13)
[2020-06-01 10:32:17.552] [error] Error: Error: Cannot download "http://localhost/projeto-teste-Setup-2.0.0.exe", status 404: Not Found
    at ClientRequest.<anonymous> (C:\Users\brenno\AppData\Local\Programs\projeto-teste\resources\app.asar\background.js:1:129099)
    at ClientRequest.emit (events.js:194:13)
    at URLRequest.<anonymous> (C:\Users\brenno\AppData\Local\Programs\projeto-teste\resources\electron.asar\browser\api\net.js:207:12)
    at URLRequest.emit (events.js:194:13)
[2020-06-01 10:32:19.229] [info] Install on explicit quitAndInstall
[2020-06-01 10:32:19.253] [error] Error: Error: No valid update available, can't quit and install
    at m.install (C:\Users\brenno\AppData\Local\Programs\projeto-teste\resources\app.asar\background.js:1:146629)
    at m.quitAndInstall (C:\Users\brenno\AppData\Local\Programs\projeto-teste\resources\app.asar\background.js:1:146149)
    at Function.<anonymous> (C:\Users\brenno\AppData\Local\Programs\projeto-teste\resources\app.asar\background.js:2:256574)

Estou usando o autoUpdater.setFeedURL no main.js

autoUpdater.setFeedURL({
    provider: 'generic',
    url: 'http://localhost:8080/'
  });
autoUpdater.checkForUpdates();

@gannons
Copy link

gannons commented Jun 2, 2020

Cannot download differentially

I'm not familiar with that error but

fallback to full download: Error: Cannot download "http://localhost/projeto-teste-Setup-1.0.0.exe.blockmap", status 404: Not Found

Is due to the file not being present because it is searching for http://localhost rather than http://localhost:8080.

I'm not sure why this is the case. I suggest you check your dev-app-update.yml and package.json files and which url values are set in them.

@brennomarques
Copy link

I changed the logic, but another problem showing ...
I am configuring a local host according to .json below, remembering that I am using vue-cli-electron-builder.

{
  "name": "skeleton-vue-electron-update",
  "version": "2.0.0",
  "private": true,
  "scripts": {
    "serve": "vue-cli-service serve",
    "build": "vue-cli-service build",
    "lint": "vue-cli-service lint",
    "electron:build": "vue-cli-service electron:build -w --publish=never",
    "electron:serve": "vue-cli-service electron:serve",
    "postinstall": "electron-builder install-app-deps",
    "postuninstall": "electron-builder install-app-deps"
  },
  "main": "background.js",
  "electron:build": {
    "copyright": "© App livre",
    "productName": "Esqueleto",
    "icon": "build/icon.png",
    "win": {
      "target": [
        {
          "target": "nsis",
          "arch": [
            "x64",
            "ia32"
          ]
        }
      ]
    },
    "appId": "com.example.app",
    "publish": [
      {
        "provider": "generic",
        "url": "http://localhost:8080/"
      }
    ]
  },
  "dependencies": {
    "core-js": "^3.6.5",
    "electron-log": "^4.2.0",
    "electron-updater": "^4.3.1",
    "vue": "^2.6.11"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.4.0",
    "@vue/cli-plugin-eslint": "~4.4.0",
    "@vue/cli-service": "~4.4.0",
    "babel-eslint": "^10.1.0",
    "electron": "^5.0.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "vue-cli-plugin-electron-builder": "~1.4.6",
    "vue-template-compiler": "^2.6.11"
  },
  "eslintConfig": {
    "root": true,
    "env": {
      "node": true
    },
    "extends": [
      "plugin:vue/essential",
      "eslint:recommended"
    ],
    "parserOptions": {
      "parser": "babel-eslint"
    },
    "rules": {}
  },
  "browserslist": [
    "> 1%",
    "last 2 versions",
    "not dead"
  ]
}

This is generating the latest.yml, when I locate the artifacts on localhost that my local server, the logs that update informs like this .....

[2020-06-02 15:26:37.083] [info] Checking for update
[2020-06-02 15:26:38.569] [error] Error: Error: No published versions on GitHub
    at e.newError (C:\Users\brenno\AppData\Local\Programs\skeleton-vue-electron-update\resources\app.asar\background.js:1:5792)
    at s.element (C:\Users\brenno\AppData\Local\Programs\skeleton-vue-electron-update\resources\app.asar\background.js:1:269416)
    at e.GitHubProvider.getLatestVersion (C:\Users\brenno\AppData\Local\Programs\skeleton-vue-electron-update\resources\app.asar\background.js:1:142325)
    at processTicksAndRejections (internal/process/task_queues.js:86:5)
    at async m.getUpdateInfoAndProvider (C:\Users\brenno\AppData\Local\Programs\skeleton-vue-electron-update\resources\app.asar\background.js:1:68695)
    at async m.doCheckForUpdates (C:\Users\brenno\AppData\Local\Programs\skeleton-vue-electron-update\resources\app.asar\background.js:1:68981)

If anyone can try an additional force effort, follow or git to test,

https://github.com/brennomarques/skeleton-vue-electron-update

@gannons
Copy link

gannons commented Jun 3, 2020

I'm not familiar with the "electron:build" and use "build" myself. I don't if that matters as they could be aliases.

Regardless Error: No published versions on GitHub is thrown when the GitHub provider is selected. In your package you've set

"publish": [
  {
    "provider": "generic", <<--
    "url": "http://localhost:8080/"
  }
]

which implies this setting is not taking effect.

@brennomarques
Copy link

manage to resolve, but in another way. now it's working. a vue-config.js was created, in that file it was put all the configuration of the build. thank you very much for your attention.

I'll leave the link to the code, in case any future doubt.

https://github.com/brennomarques/skeleton-vue-electron-update

@muscaiu
Copy link

muscaiu commented Jun 11, 2020

With a newer electron and electron-updater versions, the http-server is not queried anymore.

Any idea how to make it work?

{
  "name": "electron-updater-generic-example",
  "version": "3.0.0",
  "main": "main.js",
  "description": "electron-updater generic example project",
  "author": "Matt Haggard",
  "license": "MIT",
  "scripts": {
    "start": "electron .",
    "test": "echo \"Error: no test specified\" && exit 1",
    "build": "electron-builder build --win --x64",
  },
  "devDependencies": {
    "electron": "^9.0.3",
    "electron-builder": "^22.7.0",
    "http-server": "^0.9.0"
  },
  "dependencies": {
    "electron-log": "^1.3.0",
    "electron-updater": "^4.3.1"
  },
  "build": {
    "publish": [
      {
        "provider": "generic",
        "url": "http://127.0.0.1:8080/"
      }
    ],
    "appId": "test",
    "nsis": {
      "perMachine": true
    }
  }
}

@brennomarques
Copy link

Hello, I have a skeleton that can help you, follow the link, it's running on Windows, Linux, Mac.

https://github.com/brennomarques/electron-update

@RealHinome
Copy link

Hi.

What would I need on my website to make it work?
I put the .exe for windows but I don't think that's it.

I understand it was with a .zip but what would be in the zip?

Thank you in advance!

@brennomarques
Copy link

Hello.

You can do this in a few ways ... I in particular use vue-cli-plugin-electron-builder, electron with vuejs, but you can use only electron first to understand how it works. I am leaving a link that I did this working well, however some details are missing, I am here to help you get noticed.
https://github.com/brennomarques/electron-update.

*https://github.com/nklayman/vue-cli-plugin-electron-builder
*https://github.com/brennomarques/electron-update
*https://www.electron.build/

@oliexe
Copy link

oliexe commented Jan 7, 2021

@brennomarques

Appreciate the sample app, everything works perfectly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment