win: Make auto-updater really work

Apparently that PR was never tested.
This commit is contained in:
Cheng Zhao 2015-10-23 19:41:54 +08:00
parent aeafc46ded
commit fae2c7bc7a
2 changed files with 71 additions and 64 deletions

View file

@ -1,45 +1,42 @@
app = require 'app'
url = require 'url'
{EventEmitter} = require 'events' {EventEmitter} = require 'events'
SquirrelUpdate = require './auto-updater/squirrel-update-win'
app = require 'app' squirrelUpdate = require './squirrel-update-win'
url = require 'url'
class AutoUpdater extends EventEmitter class AutoUpdater extends EventEmitter
quitAndInstall: -> quitAndInstall: ->
SquirrelUpdate.processStart -> squirrelUpdate.processStart()
app.quit() app.quit()
setFeedUrl: (updateUrl) -> setFeedUrl: (updateUrl) ->
# set feed URL only when it hasn't been set before @updateUrl = updateUrl
unless @updateUrl
@updateUrl = updateUrl
checkForUpdates: -> 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' @emit 'checking-for-update'
unless SquirrelUpdate.supported() squirrelUpdate.download @updateUrl, (error, update) =>
@emit 'update-not-available' return @emitError error if error?
return return @emit 'update-not-available' unless update?
SquirrelUpdate.download (error, update) =>
if error?
@emit 'update-not-available'
return
unless update?
@emit 'update-not-available'
return
@emit 'update-available' @emit 'update-available'
SquirrelUpdate.update @updateUrl, (error) => squirrelUpdate.update @updateUrl, (error) =>
if error? return @emitError error if error?
@emit 'update-not-available'
return
# info about the newly installed version and a function any of the event listeners can call to restart the application {releaseNotes, version} = update
@emit 'update-downloaded', {}, update.releaseNotes, update.version, new Date(), @updateUrl, => @quitAndInstall() # 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

View file

@ -1,57 +1,67 @@
ChildProcess = require 'child_process' fs = require 'fs'
fs = require 'fs' path = require 'path'
path = require 'path' {spawn} = require 'child_process'
appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/ appFolder = path.dirname process.execPath # i.e. my-app/app-0.1.13/
rootApplicationFolder = path.resolve appFolder, '..' # i.e. my-app/ updateExe = path.resolve appFolder, '..', 'Update.exe' # i.e. my-app/Update.exe
updateDotExe = path.join rootApplicationFolder, 'Update.exe' exeName = path.basename process.execPath
exeName = path.basename process.execPath
# Spawn a command and invoke the callback when it completes with an error # Spawn a command and invoke the callback when it completes with an error
# and the output from standard out. # and the output from standard out.
spawnUpdate = (args, callback) -> spawnUpdate = (args, detached, callback) ->
stdout = ''
try try
spawnedProcess = ChildProcess.spawn(updateDotExe, args) spawnedProcess = spawn updateExe, args, {detached}
catch error catch error
# Spawn can throw an error # Shouldn't happen, but still guard it.
process.nextTick -> callback?(error, stdout) process.nextTick -> callback error
return return
stdout = ''
stderr = ''
spawnedProcess.stdout.on 'data', (data) -> stdout += data spawnedProcess.stdout.on 'data', (data) -> stdout += data
spawnedProcess.stderr.on 'data', (data) -> stderr += data
error = null errorEmitted = false
spawnedProcess.on 'error', (processError) -> error ?= processError spawnedProcess.on 'error', (error) ->
spawnedProcess.on 'close', (code, signal) -> errorEmitted = true
error ?= new Error("Command failed: #{signal ? code}") if code isnt 0 callback error
error?.code ?= code spawnedProcess.on 'exit', (code, signal) ->
error?.stdout ?= stdout # We may have already emitted an error.
callback?(error, stdout) return if errorEmitted
processStart = (callback) -> # Process terminated with error.
spawnUpdate(['--processStart', exeName], callback) if code isnt 0
return callback "Command failed: #{signal ? code}\n#{stderr}"
download = (callback) -> # Success.
spawnUpdate ['--download', @updateUrl], (error, stdout) -> 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? return callback(error) if error?
try try
# Last line of output is the JSON details about the releases # 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?() update = JSON.parse(json)?.releasesToApply?.pop?()
catch error catch
error.stdout = stdout return callback "Invalid result:\n#{stdout}"
return callback(error)
callback(null, update) callback null, update
update = (updateUrl, callback) -> # Update the application to the latest remote version specified by URL.
spawnUpdate ['--update', updateUrl], callback exports.update = (updateUrl, callback) ->
spawnUpdate ['--update', updateUrl], false, callback
# Is the Update.exe installed with the current application? # Is the Update.exe installed with the current application?
exports.supported = -> exports.supported = ->
fs.accessSync(updateDotExe, fs.R_OK) try
exports.processStart = processStart fs.accessSync updateExe, fs.R_OK
exports.download = download return true
exports.update = update catch
return false