yan-foto / neutron

Neutron: zero point of Electron apps
https://quaintous.com/neutron/
MIT License
59 stars 8 forks source link

Trouble with auto-update #14

Open adil-soubki opened 9 years ago

adil-soubki commented 9 years ago

I really like this set-up for development but I've found it very confusing trying to get an auto update system working with it. Do you have any plans or suggestions of how to integrate electrons auto updater (or https://github.com/EvolveLabs/electron-updater) with neutron?

yan-foto commented 9 years ago

electron-updater is pretty new to me. Could you please tell me what you exactly have done and where the complications arise?

adil-soubki commented 9 years ago

Thanks for the response!

Well I guess where the complications are arising is part of the question so I will try to explain what I have done very carefully.

I am trying to get a autoupdate system working with a neutron based project. I have tried two different methods of implementing this. The first was as specified through the electron api. The second was through a third party node module electron-updater.

I have neutron installed and working great with a directory structure that's set up like this:

neutron
|-- lib
|   |-- tasks
|   |   |-- etc...
|   |-- dep-utils.js
|   |-- installer.js
|-- node_modules
|   |-- .bin
|   |   |-- etc..
|   |-- etc...
|-- dist
|   |-- js
|   |   |-- util.js
|   |-- css
|   |   |-- main.css
|   |-- views
|   |   |-- index.html
|   |-- main.js
|   |-- package.json
|-- packages
|   |-- neutron-darwin-x64
|   |   |-- neutron.app
|   |   |-- etc...
|   |-- etc...
|-- src
|   |-- js
|   |   |-- util.js
|   |-- css
|   |   |-- main.scss
|   |-- views
|   |   |-- index.jade
|   |-- main.js
|-- package.json
|-- etc...

Attempt 1: Electron API

Following the instructions given in the docs, I set up my main.js like so:

var app = require('app');  // Module to control application life.
var os = require('os');

var autoUpdater = require('auto-updater');
autoUpdater.setFeedUrl('https://github.com/atom/electron/releases/tag/v0.31.2');

var BrowserWindow = require('browser-window');  // Module to create native browser window.
// This reloads all BrowserWindows if any of the files in __dirname is updated
//require('electron-reload')(__dirname);
// Report crashes to our server.
require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is GCed.
var mainWindow = null;

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform != 'darwin') {
    app.quit();
  }
});

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 800, height: 600});

  // and load the index.html of the app.
  mainWindow.loadUrl('file://' + __dirname + '/views/index.html');

  // Open the devtools.
  mainWindow.openDevTools();

  // Emitted when the window is closed.
  mainWindow.on('closed', function() {
    // Dereference the window object, usually you would store windows
    // in an array if your app supports multi windows, this is the time
    // when you should delete the corresponding element.
    mainWindow = null;
  });
});

However this results in a strange error (it is likely neutron has nothing to do with this)

Uncaught Exception:
Error: Uncaught, unspecified "error" event. ([object Object])
    at EventEmitter.emit (events.js:144:17)
   .
   .
   .

I tried to listen in on the error by adding:

autoUpdater.on("error", function(err) {
  console.log(err);
});

But Im not sure what to make of what is logged

[09:53:34] { preventDefault: [Function: preventDefault],
  sender: 
   EventEmitter {
     setFeedUrl: [Function: setFeedUrl],
     checkForUpdates: [Function: checkForUpdates],
     _quitAndInstall: [Function: _quitAndInstall],
     _events: { 'update-downloaded-raw': [Function], error: [Function] },
     _eventsCount: 2,
     quitAndInstall: [Function] } }

Attempt 2: Electron-Updater

Following the electron-updater readme I ran:

$ npm install electron-updater --save
$ npm install electron-plugins --save
$ npm install electron-updater-tools -g

My src/main.js was modified to look as follows:

var app = require('app');  // Module to control application life.
var os = require('os');
// Electron-updater
var updater = require('electron-updater');

var BrowserWindow = require('browser-window');  // Module to create native browser window.
// This reloads all BrowserWindows if any of the files in __dirname is updated
//require('electron-reload')(__dirname);
// Report crashes to our server.
require('crash-reporter').start();

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is GCed.
var mainWindow = null;

// Quit when all windows are closed.
app.on('window-all-closed', function() {
  // On OS X it is common for applications and their menu bar
  // to stay active until the user quits explicitly with Cmd + Q
  if (process.platform != 'darwin') {
    app.quit();
  }
});

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
  updater.on('ready', function () {
    // Create the browser window.
    mainWindow = new BrowserWindow({width: 800, height: 600});

    // and load the index.html of the app.
    mainWindow.loadUrl('file://' + __dirname + '/views/index.html');

    // Open the devtools.
    mainWindow.openDevTools();

    // Emitted when the window is closed.
    mainWindow.on('closed', function() {
      // Dereference the window object, usually you would store windows
      // in an array if your app supports multi windows, this is the time
      // when you should delete the corresponding element.
      mainWindow = null;
    });
  });
  updater.on('updateRequired', function () {
      app.quit();
  })
  updater.on('updateAvailable', function () {
      mainWindow.webContents.send('update-available');
  })
  updater.start()
});

My index.jade then became:

doctype html
html(lang='en')
  head
    title Hello World!
  body
    h1 Hello World!
    #content We are using io.js <script>document.write(process.version)</script> and Electron <script>document.write(process.versions['electron'])</script>.
script.
    var plugins = require('electron-plugins'),
        ipc = require('ipc');

    document.addEventListener('DOMContentLoaded', function () {
        var context = { document: document };
        plugins.load(context, function (err, loaded) {
            if(err) return console.error(err)
            console.log('Plugins loaded successfully.')
        });
    });

    ipc.on('update-available', function () {
        console.log('there is an update available for download')
    });

when I run npm start it looks like there's a bit of a routing problem as I get:

.
.
.
[10:06:40] Checking validity.
[10:06:40] [35910:0903/100640:INFO:renderer_main.cc(200)] Renderer process started
[10:06:40] Checking for updates.
[10:06:41] [35911:0903/100641:INFO:renderer_main.cc(200)] Renderer process started
[10:06:41] Updates are not available.
[10:06:41] [35906:0903/100641:INFO:CONSOLE(18)] "Error: ENOENT: no such file or directory, open '/Volumes/hrt/scratch/deploy-test/neutron/dist/views/package.json'", source: file:///Volumes/hrt/scratch/deploy-test/neutron/dist/views/index.html (18)

I tried commenting out the second block and leaving my script in src/index.jade as

var plugins = require('electron-plugins'),
    ipc = require('ipc');

ipc.on('update-available', function () {
    console.log('there is an update available for download')
});

This appears to fix things but I think that code might be important (not just related to plugins). This may be why I get poor results later.

At this point I need to set up a local instance of sinopia. I do this by running:

$ npm install sinopia -g
$ sinopia

In both neutron/src and neutron/dist I set the registry to the local sinopia instance:

.
.
.
   "publishConfig": {
       "registry": "http://localhost:4873/"
   }
}

Additionally I add a .npmrc with the content:

registry = "http://localhost:4873/"

My plan for testing was to run npm run packager then npm publish. I would then make a change in the development instance and see if it noticed in the electron log.

I published the package.json in neutron/src with the contents:

{
  "name": "neutron",
  "version": "0.2.2",
  "main": "main.js",
  "publishConfig": {
    "registry": "http://localhost:4873/"
  }
}

Unfortunately it didn't seem to notice if my currently running version was different from the published package. I'm not sure if I needed to instead publish a package with the packaged app (from neutron/packages) but when I tried that it also appeared to be unresponsive

Why is this on your repo?

I'm aware that this problem is not 100% relevant to your project. However, I think an auto updater is something most neutron projects will have to implement at some point. If I can figure this out I would like to add a section about how to do it in the docs. Additionally it might be possible to integrate it with neutron to streamline development even more.

yan-foto commented 9 years ago

I will look into this problem, but one question first: are you developing on mac? According to [electron autoUpdater doc]:

This module has only been implemented for OS X.

Since I have no computer running OS X and it is an OS specific solution, I will ignore this one and jump straight to the second solution.

adil-soubki commented 9 years ago

I am developing on a mac but a cross platform solution seems most appropriate considering the nature of electron. Thank you for helping me out!