diff --git a/src/apps.js b/src/apps.js index f48341081..05e6dd983 100644 --- a/src/apps.js +++ b/src/apps.js @@ -34,6 +34,7 @@ exports = module.exports = { checkManifestConstraints: checkManifestConstraints, + canAutoupdateApp: canAutoupdateApp, autoupdateApps: autoupdateApps, restoreInstalledApps: restoreInstalledApps, @@ -1294,27 +1295,27 @@ function exec(appId, options, callback) { }); } +function canAutoupdateApp(app, newManifest) { + if (!app.enableAutomaticUpdate) return new Error('Automatic update disabled'); + if ((semver.major(app.manifest.version) !== 0) && (semver.major(app.manifest.version) !== semver.major(newManifest.version))) return new Error('Major version change'); // major changes are blocking + + const newTcpPorts = newManifest.tcpPorts || { }; + const newUdpPorts = newManifest.udpPorts || { }; + const portBindings = app.portBindings; // this is never null + + for (let portName in portBindings) { + if (!(portName in newTcpPorts) && !(portName in newUdpPorts)) return new Error(`${portName} was in use but new update removes it`); + } + + // it's fine if one or more (unused) keys got removed + return null; +} + function autoupdateApps(updateInfo, auditSource, callback) { // updateInfo is { appId -> { manifest } } assert.strictEqual(typeof updateInfo, 'object'); assert.strictEqual(typeof auditSource, 'object'); assert.strictEqual(typeof callback, 'function'); - function canAutoupdateApp(app, newManifest) { - if (!app.enableAutomaticUpdate) return new Error('Automatic update disabled'); - if ((semver.major(app.manifest.version) !== 0) && (semver.major(app.manifest.version) !== semver.major(newManifest.version))) return new Error('Major version change'); // major changes are blocking - - const newTcpPorts = newManifest.tcpPorts || { }; - const newUdpPorts = newManifest.udpPorts || { }; - const portBindings = app.portBindings; // this is never null - - for (let portName in portBindings) { - if (!(portName in newTcpPorts) && !(portName in newUdpPorts)) return new Error(`${portName} was in use but new update removes it`); - } - - // it's fine if one or more (unused) keys got removed - return null; - } - if (!updateInfo) return callback(null); async.eachSeries(Object.keys(updateInfo), function iterator(appId, iteratorDone) { diff --git a/src/updatechecker.js b/src/updatechecker.js index 2373fe471..9ae49a712 100644 --- a/src/updatechecker.js +++ b/src/updatechecker.js @@ -71,42 +71,44 @@ function checkAppUpdates(callback) { var oldState = loadState(); var newState = { }; // create new state so that old app ids are removed - apps.getAll(function (error, apps) { + settings.getAppAutoupdatePattern(function (error, result) { if (error) return callback(error); + const autoupdatesEnabled = (result !== constants.AUTOUPDATE_PATTERN_NEVER); - async.eachSeries(apps, function (app, iteratorDone) { - if (app.appStoreId === '') return iteratorDone(); // appStoreId can be '' for dev apps + apps.getAll(function (error, apps) { + if (error) return callback(error); - appstore.getAppUpdate(app, function (error, updateInfo) { - if (error) { - debug('Error getting app update info for %s', app.id, error); - return iteratorDone(); // continue to next - } + async.eachSeries(apps, function (app, iteratorDone) { + if (app.appStoreId === '') return iteratorDone(); // appStoreId can be '' for dev apps - // skip if no next version is found - if (!updateInfo) { - delete gAppUpdateInfo[app.id]; - return iteratorDone(); - } + appstore.getAppUpdate(app, function (error, updateInfo) { + if (error) { + debug('Error getting app update info for %s', app.id, error); + return iteratorDone(); // continue to next + } - gAppUpdateInfo[app.id] = updateInfo; + // skip if no next version is found + if (!updateInfo) { + delete gAppUpdateInfo[app.id]; + return iteratorDone(); + } - // decide whether to send email - newState[app.id] = updateInfo.manifest.version; + gAppUpdateInfo[app.id] = updateInfo; - if (oldState[app.id] === newState[app.id]) { - debug('Skipping notification of app update %s since user was already notified', app.id); - return iteratorDone(); - } + // decide whether to send email + newState[app.id] = updateInfo.manifest.version; - // only send notifications if update pattern is 'never' - settings.getAppAutoupdatePattern(function (error, result) { - if (error) return iteratorDone(error); - if (result !== constants.AUTOUPDATE_PATTERN_NEVER) return iteratorDone(); + if (oldState[app.id] === newState[app.id]) { + debug('Skipping notification of app update %s since user was already notified', app.id); + return iteratorDone(); + } - debug('Notifying user of app update for %s from %s to %s', app.id, app.manifest.version, updateInfo.manifest.version); + const updateIsBlocked = apps.canAutoupdateApp(app, updateInfo); + if (autoupdatesEnabled && !updateIsBlocked) return iteratorDone(); + + debug('Notifying of app update for %s from %s to %s', app.id, app.manifest.version, updateInfo.manifest.version); users.getAllAdmins(function (error, admins) { - if (error) return callback(error); + if (error) return iteratorDone(error); async.eachSeries(admins, (admin, done) => mailer.appUpdateAvailable(admin.email, app, true /* subscription */, updateInfo, done), iteratorDone); });