From fae2c7bc7a129e86b17e8222b61e3c6aca96afbd Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 23 Oct 2015 19:41:54 +0800 Subject: [PATCH] win: Make auto-updater really work Apparently that PR was never tested. --- .../lib/auto-updater/auto-updater-win.coffee | 55 ++++++------- .../auto-updater/squirrel-update-win.coffee | 80 +++++++++++-------- 2 files changed, 71 insertions(+), 64 deletions(-) diff --git a/atom/browser/api/lib/auto-updater/auto-updater-win.coffee b/atom/browser/api/lib/auto-updater/auto-updater-win.coffee index 764f76a2253f..a9a61d8efe3f 100644 --- a/atom/browser/api/lib/auto-updater/auto-updater-win.coffee +++ b/atom/browser/api/lib/auto-updater/auto-updater-win.coffee @@ -1,45 +1,42 @@ +app = require 'app' +url = require 'url' {EventEmitter} = require 'events' -SquirrelUpdate = require './auto-updater/squirrel-update-win' -app = require 'app' -url = require 'url' + +squirrelUpdate = require './squirrel-update-win' class AutoUpdater extends EventEmitter - quitAndInstall: -> - SquirrelUpdate.processStart -> - app.quit() + squirrelUpdate.processStart() + app.quit() setFeedUrl: (updateUrl) -> - # set feed URL only when it hasn't been set before - unless @updateUrl - @updateUrl = updateUrl + @updateUrl = updateUrl checkForUpdates: -> - throw new Error('Update URL is not set') unless @updateUrl + return @emitError 'Update URL is not set' unless @updateUrl + return @emitError 'Can not find Squirrel' unless squirrelUpdate.supported() @emit 'checking-for-update' - unless SquirrelUpdate.supported() - @emit 'update-not-available' - return - - SquirrelUpdate.download (error, update) => - if error? - @emit 'update-not-available' - return - - unless update? - @emit 'update-not-available' - return + squirrelUpdate.download @updateUrl, (error, update) => + return @emitError error if error? + return @emit 'update-not-available' unless update? @emit 'update-available' - SquirrelUpdate.update @updateUrl, (error) => - if error? - @emit 'update-not-available' - return + squirrelUpdate.update @updateUrl, (error) => + return @emitError error if error? - # info about the newly installed version and a function any of the event listeners can call to restart the application - @emit 'update-downloaded', {}, update.releaseNotes, update.version, new Date(), @updateUrl, => @quitAndInstall() + {releaseNotes, version} = update + # Following information is not available on Windows, so fake them. + date = new Date + url = @updateUrl -module.exports = new AutoUpdater() + @emit 'update-downloaded', {}, releaseNotes, version, date, url, => @quitAndInstall() + + # Private: Emit both error object and message, this is to keep compatibility + # with Old APIs. + emitError: (message) -> + @emit 'error', new Error(message), message + +module.exports = new AutoUpdater diff --git a/atom/browser/api/lib/auto-updater/squirrel-update-win.coffee b/atom/browser/api/lib/auto-updater/squirrel-update-win.coffee index 95d9e1465d8a..ed302124e52e 100644 --- a/atom/browser/api/lib/auto-updater/squirrel-update-win.coffee +++ b/atom/browser/api/lib/auto-updater/squirrel-update-win.coffee @@ -1,57 +1,67 @@ -ChildProcess = require 'child_process' -fs = require 'fs' -path = require 'path' +fs = require 'fs' +path = require 'path' +{spawn} = require 'child_process' -appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/ -rootApplicationFolder = path.resolve appFolder, '..' # i.e. my-app/ -updateDotExe = path.join rootApplicationFolder, 'Update.exe' -exeName = path.basename process.execPath +appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/ +updateExe = path.resolve appFolder, '..', 'Update.exe' # i.e. my-app/Update.exe +exeName = path.basename process.execPath # Spawn a command and invoke the callback when it completes with an error # and the output from standard out. -spawnUpdate = (args, callback) -> - stdout = '' - +spawnUpdate = (args, detached, callback) -> try - spawnedProcess = ChildProcess.spawn(updateDotExe, args) + spawnedProcess = spawn updateExe, args, {detached} catch error - # Spawn can throw an error - process.nextTick -> callback?(error, stdout) + # Shouldn't happen, but still guard it. + process.nextTick -> callback error return + stdout = '' + stderr = '' spawnedProcess.stdout.on 'data', (data) -> stdout += data + spawnedProcess.stderr.on 'data', (data) -> stderr += data - error = null - spawnedProcess.on 'error', (processError) -> error ?= processError - spawnedProcess.on 'close', (code, signal) -> - error ?= new Error("Command failed: #{signal ? code}") if code isnt 0 - error?.code ?= code - error?.stdout ?= stdout - callback?(error, stdout) + errorEmitted = false + spawnedProcess.on 'error', (error) -> + errorEmitted = true + callback error + spawnedProcess.on 'exit', (code, signal) -> + # We may have already emitted an error. + return if errorEmitted -processStart = (callback) -> - spawnUpdate(['--processStart', exeName], callback) + # Process terminated with error. + if code isnt 0 + return callback "Command failed: #{signal ? code}\n#{stderr}" -download = (callback) -> - spawnUpdate ['--download', @updateUrl], (error, stdout) -> + # Success. + callback null, stdout + +# Start an instance of the installed app. +exports.processStart = (callback) -> + spawnUpdate ['--processStart', exeName], true, -> + +# Download the releases specified by the URL and write new results to stdout. +exports.download = (updateUrl, callback) -> + spawnUpdate ['--download', updateUrl], false, (error, stdout) -> return callback(error) if error? try # Last line of output is the JSON details about the releases - json = stdout.trim().split('\n').pop() + json = stdout.trim().split('\n').pop() update = JSON.parse(json)?.releasesToApply?.pop?() - catch error - error.stdout = stdout - return callback(error) + catch + return callback "Invalid result:\n#{stdout}" - callback(null, update) + callback null, update -update = (updateUrl, callback) -> - spawnUpdate ['--update', updateUrl], callback +# Update the application to the latest remote version specified by URL. +exports.update = (updateUrl, callback) -> + spawnUpdate ['--update', updateUrl], false, callback # Is the Update.exe installed with the current application? exports.supported = -> - fs.accessSync(updateDotExe, fs.R_OK) -exports.processStart = processStart -exports.download = download -exports.update = update + try + fs.accessSync updateExe, fs.R_OK + return true + catch + return false