Merge branch 'master' into dh-async-repo

This commit is contained in:
joshaber
2016-01-07 20:55:28 -05:00
12 changed files with 82 additions and 62 deletions

View File

@@ -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,
}
@@ -257,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:

View File

@@ -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...)

View File

@@ -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))

View File

@@ -66,9 +66,9 @@
"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-light-ui": "1.1.9",
"one-dark-syntax": "1.1.2",
"one-light-syntax": "1.1.2",
"solarized-dark-syntax": "0.39.0",
"solarized-light-syntax": "0.23.0",
"about": "1.1.0",

View File

@@ -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

View File

@@ -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()

View File

@@ -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)

View File

@@ -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

View File

@@ -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'

View File

@@ -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()

View File

@@ -721,30 +721,28 @@ 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'
when 'ETIMEDOUT' then 'Connection timed out'

View File

@@ -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