From d227d8b64611a2333468e9c861b6ae5fb66b2150 Mon Sep 17 00:00:00 2001 From: Chen Shen Date: Wed, 2 Dec 2015 16:57:23 -0800 Subject: [PATCH 01/10] Add config to disable autoupdate, revert previous build option --- build/Gruntfile.coffee | 3 +- build/tasks/build-task.coffee | 1 - build/tasks/disable-autoupdate-task.coffee | 12 -------- src/browser/application-menu.coffee | 9 ++---- src/browser/atom-application.coffee | 3 +- src/browser/auto-update-manager.coffee | 33 ++++++++++++++-------- src/config-schema.coffee | 4 +++ src/config.coffee | 2 +- 8 files changed, 31 insertions(+), 36 deletions(-) delete mode 100644 build/tasks/disable-autoupdate-task.coffee diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index 26d9c2f42..e4bb656c1 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -36,7 +36,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'] @@ -179,7 +178,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/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 e720597e3..d8b2257b7 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..00c00fedd 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.enableAutoupdate', ({newValue}) => + if newValue + @scheduleUpdateCheck() + else + @cancelScheduledUpdateCheck() + + @scheduleUpdateCheck() if @config.get 'core.enableAutoupdate' 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,16 @@ class AutoUpdateManager @state scheduleUpdateCheck: -> - checkForUpdates = => @check(hidePopups: true) - fourHours = 1000 * 60 * 60 * 4 - setInterval(checkForUpdates, fourHours) - checkForUpdates() + unless @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..f1e5e8bbd 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 + enableAutoupdate: + description: 'Automatically update Atom when new release is available.' + type: 'boolean' + default: true editor: type: 'object' diff --git a/src/config.coffee b/src/config.coffee index 2e4387732..c680cf415 100644 --- a/src/config.coffee +++ b/src/config.coffee @@ -820,7 +820,7 @@ class Config @watchSubscription = null notifyFailure: (errorMessage, detail) -> - @notificationManager.addError(errorMessage, {detail, dismissable: true}) + @notificationManager.addError(errorMessage, {detail, dismissable: true}) if @notificationManager save: -> return if @shouldNotAccessFileSystem() From ea3736aa703708e94a0b2e41e8e1ece3e991480d Mon Sep 17 00:00:00 2001 From: Chen Shen Date: Fri, 11 Dec 2015 10:36:01 -0800 Subject: [PATCH 02/10] address comments --- src/browser/auto-update-manager.coffee | 8 +++++--- src/config-schema.coffee | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index 00c00fedd..612702a67 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -50,13 +50,13 @@ class AutoUpdateManager @setState(UpdateAvailableState) @emitUpdateAvailableEvent(@getWindows()...) - @config.onDidChange 'core.enableAutoupdate', ({newValue}) => + @config.onDidChange 'core.enableAutoUpdate', ({newValue}) => if newValue @scheduleUpdateCheck() else @cancelScheduledUpdateCheck() - @scheduleUpdateCheck() if @config.get 'core.enableAutoupdate' + @scheduleUpdateCheck() if @config.get 'core.enableAutoUpdate' switch process.platform when 'win32' @@ -79,7 +79,9 @@ class AutoUpdateManager @state scheduleUpdateCheck: -> - unless @checkForUpdatesIntervalID + # 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) diff --git a/src/config-schema.coffee b/src/config-schema.coffee index f1e5e8bbd..31dd980bb 100644 --- a/src/config-schema.coffee +++ b/src/config-schema.coffee @@ -104,8 +104,8 @@ module.exports = description: 'Automatically open an empty editor on startup.' type: 'boolean' default: true - enableAutoupdate: - description: 'Automatically update Atom when new release is available.' + enableAutoUpdate: + description: 'Automatically update Atom when a new release is available.' type: 'boolean' default: true From 6c0643b4faa559e349d425eeb8527cf91cddb87a Mon Sep 17 00:00:00 2001 From: Chen Shen Date: Tue, 15 Dec 2015 14:04:09 -0800 Subject: [PATCH 03/10] address comments --- src/browser/auto-update-manager.coffee | 4 ++-- src/config-schema.coffee | 2 +- src/config.coffee | 13 +++++++++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/browser/auto-update-manager.coffee b/src/browser/auto-update-manager.coffee index 612702a67..6a008d44d 100644 --- a/src/browser/auto-update-manager.coffee +++ b/src/browser/auto-update-manager.coffee @@ -50,13 +50,13 @@ class AutoUpdateManager @setState(UpdateAvailableState) @emitUpdateAvailableEvent(@getWindows()...) - @config.onDidChange 'core.enableAutoUpdate', ({newValue}) => + @config.onDidChange 'core.automaticallyUpdate', ({newValue}) => if newValue @scheduleUpdateCheck() else @cancelScheduledUpdateCheck() - @scheduleUpdateCheck() if @config.get 'core.enableAutoUpdate' + @scheduleUpdateCheck() if @config.get 'core.automaticallyUpdate' switch process.platform when 'win32' diff --git a/src/config-schema.coffee b/src/config-schema.coffee index 31dd980bb..88e00c71d 100644 --- a/src/config-schema.coffee +++ b/src/config-schema.coffee @@ -104,7 +104,7 @@ module.exports = description: 'Automatically open an empty editor on startup.' type: 'boolean' default: true - enableAutoUpdate: + automaticallyUpdate: description: 'Automatically update Atom when a new release is available.' type: 'boolean' default: true diff --git a/src/config.coffee b/src/config.coffee index c680cf415..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}) if @notificationManager + @notificationManager?.addError(errorMessage, {detail, dismissable: true}) save: -> return if @shouldNotAccessFileSystem() From f774e1ce2171a40eac45b8b453ce156a68cb367c Mon Sep 17 00:00:00 2001 From: Chen Shen Date: Mon, 4 Jan 2016 16:55:44 -0800 Subject: [PATCH 04/10] add unittest --- spec/config-spec.coffee | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) 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 From 9efb3328bc0b252fc792f687377eb5d4b0f018a9 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 6 Jan 2016 18:20:46 -0800 Subject: [PATCH 05/10] Add helper to map error codes to friendly messages --- src/pane.coffee | 35 ++++++++++++++++------------------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/src/pane.coffee b/src/pane.coffee index 412fc5251..c494297cf 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -721,30 +721,27 @@ class Pane extends Model message = "#{message} '#{itemPath}'" if itemPath @notificationManager.addWarning(message, options) - if error.code is 'EISDIR' or error.message?.endsWith?('is a directory') + customMessage = @getMessageForErrorCode(error.code) + if customMessage? + addWarningWithPath("Unable to save file: #{customMessage}") + else if error.code is 'EISDIR' or error.message?.endsWith?('is a directory') @notificationManager.addWarning("Unable to save file: #{error.message}") - else if error.code is 'EACCES' - addWarningWithPath('Unable to save file: Permission denied') else if error.code in ['EPERM', 'EBUSY', 'UNKNOWN', 'EEXIST', 'ELOOP', 'EAGAIN'] addWarningWithPath('Unable to save file', detail: error.message) - else if error.code is 'EROFS' - addWarningWithPath('Unable to save file: Read-only file system') - else if error.code is 'ENOSPC' - addWarningWithPath('Unable to save file: No space left on device') - else if error.code is 'ENXIO' - addWarningWithPath('Unable to save file: No such device or address') - else if error.code is 'ENOTSUP' - addWarningWithPath('Unable to save file: Operation not supported on socket') - else if error.code is 'EIO' - addWarningWithPath('Unable to save file: I/O error writing file') - else if error.code is 'EINTR' - addWarningWithPath('Unable to save file: Interrupted system call') - else if error.code is 'ECONNRESET' - addWarningWithPath('Unable to save file: Connection reset') - else if error.code is 'ESPIPE' - addWarningWithPath('Unable to save file: Invalid seek') else if errorMatch = /ENOTDIR, not a directory '([^']+)'/.exec(error.message) fileName = errorMatch[1] @notificationManager.addWarning("Unable to save file: A directory in the path '#{fileName}' could not be written to") else throw error + + getMessageForErrorCode: (errorCode) -> + switch errorCode + when 'EACCES' then 'Permission denied' + when 'ECONNRESET' then 'Connection reset' + when 'EINTR' then 'Interrupted system call' + when 'EIO' then 'I/O error writing file' + when 'ENOSPC' then 'No space left on device' + when 'ENOTSUP' then 'Operation not supported on socket' + when 'ENXIO' then 'No such device or address' + when 'EROFS' then 'Read-only file system' + when 'ESPIPE' then 'Invalid seek' From 3bcb062ab0d8c6b9992dbcbee856e46608a0dbc1 Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 6 Jan 2016 18:23:25 -0800 Subject: [PATCH 06/10] Add friendly message for ETIMEDOUT --- src/pane.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pane.coffee b/src/pane.coffee index c494297cf..c006e29fe 100644 --- a/src/pane.coffee +++ b/src/pane.coffee @@ -745,3 +745,4 @@ class Pane extends Model when 'ENXIO' then 'No such device or address' when 'EROFS' then 'Read-only file system' when 'ESPIPE' then 'Invalid seek' + when 'ETIMEDOUT' then 'Connection timed out' From 41c8792a7d19b7cd4fbceb4cc7e4e1d99c51363a Mon Sep 17 00:00:00 2001 From: Katrina Uychaco Date: Wed, 6 Jan 2016 18:27:18 -0800 Subject: [PATCH 07/10] Handle EAGAIN errors on open --- src/workspace.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/workspace.coffee b/src/workspace.coffee index f64f58ee0..07e69cdcf 100644 --- a/src/workspace.coffee +++ b/src/workspace.coffee @@ -475,7 +475,7 @@ class Workspace extends Model when 'EACCES' @notificationManager.addWarning("Permission denied '#{error.path}'") return Promise.resolve() - when 'EPERM', 'EBUSY', 'ENXIO', 'EIO', 'ENOTCONN', 'UNKNOWN', 'ECONNRESET', 'EINVAL', 'EMFILE', 'ENOTDIR' + when 'EPERM', 'EBUSY', 'ENXIO', 'EIO', 'ENOTCONN', 'UNKNOWN', 'ECONNRESET', 'EINVAL', 'EMFILE', 'ENOTDIR', 'EAGAIN' @notificationManager.addWarning("Unable to open '#{error.path ? uri}'", detail: error.message) return Promise.resolve() else From ee564845d6ae36418a79b9c29354b5b61ae4f2e3 Mon Sep 17 00:00:00 2001 From: Lee DohM Date: Thu, 7 Jan 2016 14:19:27 -0800 Subject: [PATCH 08/10] Default to using standard GitHub API token during build --- build/Gruntfile.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/Gruntfile.coffee b/build/Gruntfile.coffee index f210ccff0..60d5916b8 100644 --- a/build/Gruntfile.coffee +++ b/build/Gruntfile.coffee @@ -256,7 +256,7 @@ module.exports = (grunt) -> outputDir: 'electron' downloadDir: electronDownloadDir rebuild: true # rebuild native modules after electron is updated - token: process.env.ATOM_ACCESS_TOKEN + token: process.env.ATOM_ACCESS_TOKEN ? 'da809a6077bb1b0aa7c5623f7b2d5f1fec2faae4' 'create-windows-installer': installer: From 26a2fe0f442c9f9d454b0dad8f1caa7126f483ba Mon Sep 17 00:00:00 2001 From: simurai Date: Fri, 8 Jan 2016 09:22:02 +0900 Subject: [PATCH 09/10] :arrow_up: one-dark/light-syntax@v1.1.2 --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index d6eefcc7c..bef892234 100644 --- a/package.json +++ b/package.json @@ -65,8 +65,8 @@ "base16-tomorrow-dark-theme": "1.1.0", "base16-tomorrow-light-theme": "1.1.1", "one-dark-ui": "1.1.9", - "one-dark-syntax": "1.1.1", - "one-light-syntax": "1.1.1", + "one-dark-syntax": "1.1.2", + "one-light-syntax": "1.1.2", "one-light-ui": "1.1.9", "solarized-dark-syntax": "0.39.0", "solarized-light-syntax": "0.23.0", From 9e8507f2b673070b377859bb43be910cc67f2a98 Mon Sep 17 00:00:00 2001 From: simurai Date: Fri, 8 Jan 2016 09:24:08 +0900 Subject: [PATCH 10/10] Reorder one-light-ui so it's after one-dark-ui. Handy since they get updated together. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bef892234..eaad0b69a 100644 --- a/package.json +++ b/package.json @@ -65,9 +65,9 @@ "base16-tomorrow-dark-theme": "1.1.0", "base16-tomorrow-light-theme": "1.1.1", "one-dark-ui": "1.1.9", + "one-light-ui": "1.1.9", "one-dark-syntax": "1.1.2", "one-light-syntax": "1.1.2", - "one-light-ui": "1.1.9", "solarized-dark-syntax": "0.39.0", "solarized-light-syntax": "0.23.0", "about": "1.1.0",