diff --git a/controllers/AutoUpdater.js b/controllers/AutoUpdater.js index d4a4390..c89cfa8 100644 --- a/controllers/AutoUpdater.js +++ b/controllers/AutoUpdater.js @@ -62,7 +62,7 @@ exports.init = function init(window) { } exports.manualCheckUpdate = function () { - app.helpers.CheckUpdate(FEED_URL, function (err, update) { + app.helpers.CheckAppUpdate(FEED_URL, function (err, update) { if (err) { console.error(TAG, err) app.controllers.MainWindow.notify('Update Error', 'Failed to check for updates'); diff --git a/controllers/ExtensionManager.js b/controllers/ExtensionManager.js index 47a11f0..1818a7b 100644 --- a/controllers/ExtensionManager.js +++ b/controllers/ExtensionManager.js @@ -1,5 +1,5 @@ 'use strict'; -var TAG = _TAG('TournamenterModules'); +var TAG = _TAG('ExtensionManager'); // // Manages (extra) extensions for Tournamenter // @@ -12,11 +12,18 @@ var TAG = _TAG('TournamenterModules'); // const fs = require('fs'); const path = require('path'); +const async = require('async'); const fork = require('child_process').fork; const readline = require('readline'); const emit = app.helpers.emit; +// +// Cache variables +// +exports._cachedUpdates = null +exports._cachedExtensions = null + // // Initialize module // @@ -54,6 +61,9 @@ exports.init = function () { ipc.on('ExtensionManager:executing', function (event, id) { event.returnValue = exports.isExecuting(); }) + + // Delay check for updates a bit + setTimeout(exports.checkUpdates, 4000); } @@ -89,7 +99,6 @@ exports.getExtensionsPaths = function (extensions) { // // List packages with it's `package.js` information // -exports._cachedExtensions = null exports.list = function () { const installPath = path.join(exports.getInstallPath(), 'node_modules'); @@ -142,7 +151,7 @@ exports.list = function () { if(!('_requiredBy' in extension)) return true; - // It's a root dependency (installed by `npm install `) + // It's a root dependency (installed by `npm install `) if(extension._requiredBy.indexOf('#USER') >= 0) return true; @@ -165,6 +174,12 @@ exports.list = function () { ]) }) + // Set updates into objects + extensions = extensions.map((extension) => { + extension.update = exports.getUpdate(extension.name) + return extension + }) + // Save cache exports._cachedExtensions = extensions; @@ -217,7 +232,8 @@ exports.install = function (extension, cb) { // Bind stdout and stderr read events and pipes to ipc app.helpers.bindProcessLogsToIPC(proc, 'ExtensionManager', { - error: /ERR!/g, + error: /ERR!/, + skip: /npm (verb|http|info)/ }); } @@ -239,10 +255,35 @@ exports.remove = function (extension, cb){ // Bind stdout and stderr read events and pipes to ipc app.helpers.bindProcessLogsToIPC(proc, 'ExtensionManager', { - error: /ERR!/g, + error: /ERR!/, + skip: /npm (verb|http|info)/ }); } +// +// Check for updates on all dependencies +// +exports.checkUpdates = function (next) { + let packages = exports.list() + async.mapLimit(packages, 2, app.helpers.CheckPackageUpdate, (err, updates) => { + // Join the 'names' with the versions, to create a map table from module name to version + let versions = _.zipObject(_.map(packages, 'name'), updates) + exports._cachedUpdates = versions + + // Clear Extensions cache + exports._cachedExtensions = null + + // Notify update + emit('ExtensionManager:update', true) + }) +} + +// +// Get an update for a given module name string. Returns null if up to date +// +exports.getUpdate = function (name) { + return exports._cachedUpdates && exports._cachedUpdates[name] || null +} // // Low level call for NPM @@ -295,6 +336,12 @@ exports.runNpm = function (params, cb){ console.log(TAG, chalk.green(`npm run ${params[0]} ${params[1]}... finish: ${code}`)); emit('ExtensionManager:log', 'server', `Install finished. Code ${code}`); + // Clear extension update cache + exports._cachedUpdates = null + + // Check for updates + exports.checkUpdates() + // Callback with error cb && cb(failed ? errors && errors.join('\r\n') : null); }) diff --git a/helpers/CheckUpdate.js b/helpers/CheckAppUpdate.js similarity index 92% rename from helpers/CheckUpdate.js rename to helpers/CheckAppUpdate.js index a73fad0..2238c9d 100644 --- a/helpers/CheckUpdate.js +++ b/helpers/CheckAppUpdate.js @@ -4,7 +4,7 @@ const request = require('request') * Checks for update given the url. * Returns the Update object with '' */ -module.exports = function CheckUpdate(url, next) { +module.exports = function CheckAppUpdate(url, next) { request({ url: url, json: true, diff --git a/helpers/CheckPackageUpdate.js b/helpers/CheckPackageUpdate.js new file mode 100644 index 0000000..2c90af1 --- /dev/null +++ b/helpers/CheckPackageUpdate.js @@ -0,0 +1,33 @@ +const semver = require('semver') +const request = require('request') + +const NPM_BASE = 'http://registry.npmjs.org' + +/* + * Checks for update for a specific package + * this will never send back errors. Only no-update available (null) + */ +module.exports = function CheckPackageUpdate(package, next) { + let {name, version} = package + let url = `${NPM_BASE}/${name}` + + request({ + url, + json: true, + }, function (error, response, body) { + if (error) { + return next && next(null, null) + } + + if (response.statusCode != 200) { + return next && next(null, null) + } + + // Check version + let newVersion = body['dist-tags'].latest + let hasUpdate = semver.gt(newVersion, version) + + // Send back data + return next && next(null, hasUpdate ? newVersion : null) + }) +} \ No newline at end of file diff --git a/helpers/bindProcessLogsToIPC.js b/helpers/bindProcessLogsToIPC.js index a810f51..14a6c70 100644 --- a/helpers/bindProcessLogsToIPC.js +++ b/helpers/bindProcessLogsToIPC.js @@ -17,9 +17,12 @@ module.exports = function bindProcessLogsToIPC(proc, namespace, regexs = {}){ if(!regexs.error || (regexs.error && regexs.error.test(line))) return emit(`${namespace}:log`, 'error', line); + // Skip lines + if(regexs.skip && regexs.skip.test(line)) + return; + // Emits a warning emit(`${namespace}:log`, 'warn', line); - }); } diff --git a/package.json b/package.json index af13510..9611cad 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "homepage": "https://github.com/ivanseidel/TournamenterApp", "repository": "https://github.com/ivanseidel/TournamenterApp", "license": "MIT", - "version": "1.6.2", + "version": "1.6.4", "README": "none", "engines": { "node": "6.9.1" @@ -72,7 +72,7 @@ } }, "dependencies": { - "async": "1.2.1", + "async": "^2.3.0", "chalk": "^1.1.3", "forever-monitor": "^1.7.0", "ip": "^1.1.5", @@ -80,7 +80,8 @@ "lodash": "^4.13.1", "npm": "^3.10.5", "request": "^2.81.0", - "tournamenter": "2.2.8" + "semver": "^5.3.0", + "tournamenter": "2.3.0" }, "devDependencies": { "bower": "latest", diff --git a/public/app/App.js b/public/app/App.js index 64ab53c..c2be276 100644 --- a/public/app/App.js +++ b/public/app/App.js @@ -151,7 +151,7 @@ angular.module('App', [ $scope._loaded = false; $scope.version = require('electron').remote.app.getVersion(); $scope.versionTournamenter = require('tournamenter/package.json').version; - $scope.newUpdate = require('electron').remote.require('./helpers/CheckUpdate.js').newUpdate; + $scope.newUpdate = require('electron').remote.require('./helpers/CheckAppUpdate.js').newUpdate; $scope.openExternal = function openExternal(link){ const {shell} = require('electron'); diff --git a/public/views/extensions.panel.html b/public/views/extensions.panel.html index 8cba87d..702beec 100644 --- a/public/views/extensions.panel.html +++ b/public/views/extensions.panel.html @@ -4,7 +4,7 @@ - +
Installed: {{ extensions.length }} @@ -25,12 +25,12 @@ ng-repeat="extension in extensions track by extension.name">
-
{{ extension.name }}
+
{{ extension.name }} {{ extension.update ? '(Update Available)' : ''}}
{{ extension.description }}
-
+
v{{ extension.version }}
diff --git a/public/views/extensions.window.html b/public/views/extensions.window.html index 5cab31e..12b0729 100644 --- a/public/views/extensions.window.html +++ b/public/views/extensions.window.html @@ -88,10 +88,12 @@

{{ extension.name }}

-
+
+ New Update: v{{ extension.version }} + -> v{{ extension.update }}