electron-updater
Version:
Cross platform auto-updater for electron applications
132 lines (119 loc) • 4.39 kB
JavaScript
var commands = require('./lib/commands.js'),
util = require('util'),
fs = require('fs'),
spawn = require('child_process').spawn,
ipc = require('ipc'),
minimist = require('minimist'),
launch = require('./lib/launch.js'),
directory = require('./lib/directory.js'),
file = require('./lib/file.js'),
Logger = require('./lib/logger.js')
function _launch(args, logger) {
logger.log('launching ' + args.exe)
logger.log(' argv: ' + util.inspect(args.argv))
logger.log(' cwd: ' + args.cwd)
var child = spawn(args.exe, args.argv, {
detached: true,
cwd: args.cwd,
stdio: [ 'ignore', 'pipe', 'pipe'] // out, err]
});
child.unref();
}
var argv = minimist(process.argv.slice(2))
if (argv['electron-update']) {
var relaunch = typeof argv.relaunch === 'boolean' ? argv.relaunch : true
var encodedArgs = argv['electron-update']
var decodedArgs = new Buffer(encodedArgs, 'base64').toString('ascii')
var args = JSON.parse(decodedArgs)
// {
// name: appName,
// publisher: publisher,
// exe: process.execPath,
// cwd: process.cwd(),
// argv: process.argv,
// debug: true
// }
var appDir = directory.appDir(args.publisher, args.appName)
var pendingUpdatePath = path.join(appDir, '.update')
var logger = new Logger(appDir, Logger.appendToFile, args.debug)
logger.log('Starting Update:')
logger.log(' args: ' + util.inspect(args))
process.on('uncaughtException', function (err) {
logger.log('uncaught exception: ' + err)
})
// Flag an update as pending
file.touch(pendingUpdatePath, 'INPROGRESS', function (err) {
if(err) return console.log(err)
// Attempt to actually udpate now.
var app = require('app')
var BrowserWindow = require('browser-window')
var win = new BrowserWindow({
width: 400,
height: 100,
frame: false
})
win.loadUrl('file://' + __dirname + '/update.html')
ipc.on('initialize', function (event, arg) {
logger.log('Initialized.')
event.sender.send('initialize', args)
})
ipc.on('log', function (event, arg) {
logger.log(arg)
})
commands.update(process.cwd(), logger, function (err) {
if(err) {
// If the update fails for security reasons, then we have to attempt to relaunch this process
// with the right permissions.
if(err.code === 'EPERM') {
logger.log('No permission to update, elevating...')
var elevatedArgs = process.argv.slice(1)
// Tell the elevated process not to relaunch, we will relaunch from this process when its done.
elevatedArgs.push('--no-relaunch')
// relaunch self as an elevated process
launch.elevate(args.publisher, args.appName, process.execPath, elevatedArgs, process.cwd(), function (err) {
if(err) return logger.log(err)
// Watch for changes to the .update file, it will become empty when the update succeeds.
fs.watchFile(pendingUpdatePath, {persistent: true, interval:500}, function () {
fs.readFile(pendingUpdatePath, {encoding:'utf8'}, function (err, contents) {
if(err || contents === 'PENDING') {
if(err) console.log(err)
// Going back to a PENDING state means that the elevated process
// failed to update for an unexpected reason. In that case
// just shutdown and wait for the next attempt.
fs.unwatchFile(pendingUpdatePath)
app.quit()
} else if(contents === '') {
// When update is done the file will be changed to have empty content
fs.unwatchFile(pendingUpdatePath)
if (relaunch) {
logger.log('relaunching from unelevated process.')
_launch(args, logger)
}
app.quit()
}
})
})
})
} else {
logger.error('update failed for an unexected reason.')
logger.error(err)
file.touch(pendingUpdatePath, 'PENDING', function (err) {
if(err) logger.error(err)
app.quit()
})
}
} else {
// Update was successful!
logger.log('updated succeeded.')
file.touch(pendingUpdatePath, '', function (err) {
if(err) logger.log(err)
// If the app was already running as admin, this flag will be missing. Go ahead and re-launch the app.
if(relaunch) _launch(args, logger)
app.quit()
})
}
})
})
} else {
module.exports = commands
}