diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index 63afc2fd7..f210ccff0 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -38,7 +38,6 @@ module.exports = (grunt) -> buildDir = grunt.option('build-dir') buildDir ?= path.join(os.tmpdir(), 'atom-build') buildDir = path.resolve(buildDir) - disableAutoUpdate = grunt.option('no-auto-update') ? false channel = grunt.option('channel') releasableBranches = ['stable', 'beta'] @@ -181,7 +180,7 @@ module.exports = (grunt) -> pkg: grunt.file.readJSON('package.json') atom: { - appName, channel, metadata, disableAutoUpdate, + appName, channel, metadata, appFileName, apmFileName, appDir, buildDir, contentsDir, installDir, shellAppDir, symbolsDir, } diff --git a/build/tasks/build-task.coffee b/build/tasks/build-task.coffee index 213aa0da4..a86c7c1f4 100644 --- a/build/tasks/build-task.coffee +++ b/build/tasks/build-task.coffee @@ -186,5 +186,4 @@ module.exports = (grunt) -> dependencies = ['compile', 'generate-license:save', 'generate-module-cache', 'compile-packages-slug'] dependencies.push('copy-info-plist') if process.platform is 'darwin' dependencies.push('set-exe-icon') if process.platform is 'win32' - dependencies.push('disable-autoupdate') if grunt.config.get('atom.disableAutoUpdate') grunt.task.run(dependencies...) diff --git a/build/tasks/disable-autoupdate-task.coffee b/build/tasks/disable-autoupdate-task.coffee deleted file mode 100644 index 7517543da..000000000 --- a/build/tasks/disable-autoupdate-task.coffee +++ /dev/null @@ -1,12 +0,0 @@ -fs = require 'fs' -path = require 'path' - -module.exports = (grunt) -> - - grunt.registerTask 'disable-autoupdate', 'Set up disableAutoUpdate field in package.json file', -> - appDir = fs.realpathSync(grunt.config.get('atom.appDir')) - - metadata = grunt.file.readJSON(path.join(appDir, 'package.json')) - metadata._disableAutoUpdate = grunt.config.get('atom.disableAutoUpdate') - - grunt.file.write(path.join(appDir, 'package.json'), JSON.stringify(metadata)) diff --git a/spec/config-spec.coffee b/spec/config-spec.coffee index eab2f6f04..c431fea5f 100644 --- a/spec/config-spec.coffee +++ b/spec/config-spec.coffee @@ -872,6 +872,26 @@ describe "Config", -> atom.config.loadUserConfig() expect(atom.config.get("foo.bar")).toBe "baz" + describe "when the config file fails to load", -> + addErrorHandler = null + + beforeEach -> + atom.notifications.onDidAddNotification addErrorHandler = jasmine.createSpy() + spyOn(fs, "existsSync").andCallFake -> + error = new Error() + error.code = 'EPERM' + throw error + + it "creates a notification and does not try to save later changes to disk", -> + load = -> atom.config.loadUserConfig() + expect(load).not.toThrow() + expect(addErrorHandler.callCount).toBe 1 + + atom.config.set("foo.bar", "baz") + advanceClock(100) + expect(atom.config.save).not.toHaveBeenCalled() + expect(atom.config.get("foo.bar")).toBe "baz" + describe ".observeUserConfig()", -> updatedHandler = null diff --git a/src/browser/application-menu.coffee b/src/browser/application-menu.coffee index 27b9df8e1..74da80e43 100644 --- a/src/browser/application-menu.coffee +++ b/src/browser/application-menu.coffee @@ -103,8 +103,6 @@ class ApplicationMenu downloadingUpdateItem.visible = false installUpdateItem.visible = false - return if @autoUpdateManager.isDisabled() - switch state when 'idle', 'error', 'no-update-available' checkForUpdateItem.visible = true @@ -119,9 +117,10 @@ class ApplicationMenu # # Returns an Array of menu item Objects. getDefaultTemplate: -> - template = [ + [ label: "Atom" submenu: [ + {label: "Check for Update", metadata: {autoUpdate: true}} {label: 'Reload', accelerator: 'Command+R', click: => @focusedWindow()?.reload()} {label: 'Close Window', accelerator: 'Command+Shift+W', click: => @focusedWindow()?.close()} {label: 'Toggle Dev Tools', accelerator: 'Command+Alt+I', click: => @focusedWindow()?.toggleDevTools()} @@ -129,10 +128,6 @@ class ApplicationMenu ] ] - # Add `Check for Update` button if autoUpdateManager is enabled. - template[0].submenu.unshift({label: "Check for Update", metadata: {autoUpdate: true}}) unless @autoUpdateManager.isDisabled() - template - focusedWindow: -> _.find global.atomApplication.windows, (atomWindow) -> atomWindow.isFocused() diff --git a/src/browser/atom-application.coffee b/src/browser/atom-application.coffee index a5a535b93..3096fb7c0 100644 --- a/src/browser/atom-application.coffee +++ b/src/browser/atom-application.coffee @@ -74,8 +74,7 @@ class AtomApplication @pidsToOpenWindows = {} @windows = [] - disableAutoUpdate = require(path.join(@resourcePath, 'package.json'))._disableAutoUpdate ? false - @autoUpdateManager = new AutoUpdateManager(@version, options.test, disableAutoUpdate) + @autoUpdateManager = new AutoUpdateManager(@version, options.test, @resourcePath) @applicationMenu = new ApplicationMenu(@version, @autoUpdateManager) @atomProtocolHandler = new AtomProtocolHandler(@resourcePath, @safeMode) diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index 55ab2462b..6a008d44d 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -1,5 +1,6 @@ autoUpdater = null _ = require 'underscore-plus' +Config = require '../config' {EventEmitter} = require 'events' path = require 'path' @@ -15,10 +16,13 @@ module.exports = class AutoUpdateManager _.extend @prototype, EventEmitter.prototype - constructor: (@version, @testMode, @disabled) -> + constructor: (@version, @testMode, resourcePath) -> @state = IdleState @iconPath = path.resolve(__dirname, '..', '..', 'resources', 'atom.png') @feedUrl = "https://atom.io/api/updates?version=#{@version}" + @config = new Config({configDirPath: process.env.ATOM_HOME, resourcePath, enablePersistence: true}) + @config.setSchema null, {type: 'object', properties: _.clone(require('../config-schema'))} + @config.load() process.nextTick => @setupAutoUpdater() setupAutoUpdater: -> @@ -46,9 +50,13 @@ class AutoUpdateManager @setState(UpdateAvailableState) @emitUpdateAvailableEvent(@getWindows()...) - # Only check for updates periodically if enabled and running in release - # version. - @scheduleUpdateCheck() unless /\w{7}/.test(@version) or @disabled + @config.onDidChange 'core.automaticallyUpdate', ({newValue}) => + if newValue + @scheduleUpdateCheck() + else + @cancelScheduledUpdateCheck() + + @scheduleUpdateCheck() if @config.get 'core.automaticallyUpdate' switch process.platform when 'win32' @@ -56,9 +64,6 @@ class AutoUpdateManager when 'linux' @setState(UnsupportedState) - isDisabled: -> - @disabled - emitUpdateAvailableEvent: (windows...) -> return unless @releaseVersion? for atomWindow in windows @@ -74,10 +79,18 @@ class AutoUpdateManager @state scheduleUpdateCheck: -> - checkForUpdates = => @check(hidePopups: true) - fourHours = 1000 * 60 * 60 * 4 - setInterval(checkForUpdates, fourHours) - checkForUpdates() + # Only schedule update check periodically if running in release version and + # and there is no existing scheduled update check. + unless /\w{7}/.test(@version) or @checkForUpdatesIntervalID + checkForUpdates = => @check(hidePopups: true) + fourHours = 1000 * 60 * 60 * 4 + @checkForUpdatesIntervalID = setInterval(checkForUpdates, fourHours) + checkForUpdates() + + cancelScheduledUpdateCheck: -> + if @checkForUpdatesIntervalID + clearInterval(@checkForUpdatesIntervalID) + @checkForUpdatesIntervalID = null check: ({hidePopups}={}) -> unless hidePopups diff --git a/src/config-schema.coffee b/src/config-schema.coffee index d9c0c1d21..88e00c71d 100644 --- a/src/config-schema.coffee +++ b/src/config-schema.coffee @@ -104,6 +104,10 @@ module.exports = description: 'Automatically open an empty editor on startup.' type: 'boolean' default: true + automaticallyUpdate: + description: 'Automatically update Atom when a new release is available.' + type: 'boolean' + default: true editor: type: 'object' diff --git a/src/config.coffee b/src/config.coffee index 2e4387732..2a883a57d 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -779,9 +779,14 @@ class Config loadUserConfig: -> return if @shouldNotAccessFileSystem() - unless fs.existsSync(@configFilePath) - fs.makeTreeSync(path.dirname(@configFilePath)) - CSON.writeFileSync(@configFilePath, {}) + try + unless fs.existsSync(@configFilePath) + fs.makeTreeSync(path.dirname(@configFilePath)) + CSON.writeFileSync(@configFilePath, {}) + catch error + @configFileHasErrors = true + @notifyFailure("Failed to initialize `#{path.basename(@configFilePath)}`", error.stack) + return try unless @savePending @@ -820,7 +825,7 @@ class Config @watchSubscription = null notifyFailure: (errorMessage, detail) -> - @notificationManager.addError(errorMessage, {detail, dismissable: true}) + @notificationManager?.addError(errorMessage, {detail, dismissable: true}) save: -> return if @shouldNotAccessFileSystem()