From 77dcf37ee3587f18356943f46c28462ef9bd93a5 Mon Sep 17 00:00:00 2001 From: Giuseppe Piscopo Date: Thu, 21 Apr 2016 01:53:11 +0200 Subject: [PATCH 1/4] extract process spawning from squirrel-update --- spec/spawner-spec.coffee | 57 +++++++++++++++++++++++++ spec/squirrel-update-spec.coffee | 68 +++++++++++++----------------- src/browser/spawner.coffee | 36 ++++++++++++++++ src/browser/squirrel-update.coffee | 38 +++-------------- 4 files changed, 128 insertions(+), 71 deletions(-) create mode 100644 spec/spawner-spec.coffee create mode 100644 src/browser/spawner.coffee diff --git a/spec/spawner-spec.coffee b/spec/spawner-spec.coffee new file mode 100644 index 000000000..4ae2149e5 --- /dev/null +++ b/spec/spawner-spec.coffee @@ -0,0 +1,57 @@ +ChildProcess = require 'child_process' +Spawner = require '../src/browser/spawner' + +describe "Spawner", -> + beforeEach -> + # Prevent any commands from actually running and affecting the host + originalSpawn = ChildProcess.spawn + + harmlessSpawn = + # Just spawn something that won't actually modify the host + if process.platform is 'win32' + originalSpawn('dir') + else + originalSpawn('ls') + + spyOn(ChildProcess, 'spawn').andCallFake (command, args, callback) -> + harmlessSpawn + + it "invokes passed callback", -> + someCallback = jasmine.createSpy('someCallback') + + Spawner.spawn('some-command', 'some-args', someCallback) + + waitsFor -> + someCallback.callCount is 1 + + it "spawns passed command with arguments", -> + actualCommand = null + actualArgs = null + + # Redefine fake invocation, so to remember passed arguments + jasmine.unspy(ChildProcess, 'spawn') + spyOn(ChildProcess, 'spawn').andCallFake (command, args) -> + actualCommand = command + actualArgs = args + harmlessSpawn + + expectedCommand = 'some-command' + expectedArgs = 'some-args' + someCallback = jasmine.createSpy('someCallback') + + Spawner.spawn(expectedCommand, expectedArgs, someCallback) + + expect(actualCommand).toBe expectedCommand + expect(actualArgs).toBe expectedArgs + + it "ignores errors by spawned process", -> + # Redefine fake invocation, so to cause an error + jasmine.unspy(ChildProcess, 'spawn') + spyOn(ChildProcess, 'spawn').andCallFake -> throw new Error("EBUSY") + + someCallback = jasmine.createSpy('someCallback') + + expect(Spawner.spawn('some-command', 'some-args', someCallback)).toBe undefined + + waitsFor -> + someCallback.callCount is 1 diff --git a/spec/squirrel-update-spec.coffee b/spec/squirrel-update-spec.coffee index e3891ed1c..e11fb1f00 100644 --- a/spec/squirrel-update-spec.coffee +++ b/spec/squirrel-update-spec.coffee @@ -1,39 +1,22 @@ -ChildProcess = require 'child_process' {EventEmitter} = require 'events' fs = require 'fs-plus' path = require 'path' temp = require 'temp' SquirrelUpdate = require '../src/browser/squirrel-update' +Spawner = require '../src/browser/spawner' describe "Windows Squirrel Update", -> tempHomeDirectory = null - originalSpawn = ChildProcess.spawn - - harmlessSpawn = -> - # Just spawn something that won't actually modify the host - if process.platform is 'win32' - originalSpawn('dir') - else - originalSpawn('ls') beforeEach -> # Prevent the actual home directory from being manipulated tempHomeDirectory = temp.mkdirSync('atom-temp-home-') spyOn(fs, 'getHomeDirectory').andReturn(tempHomeDirectory) - # Prevent any commands from actually running and affecting the host - spyOn(ChildProcess, 'spawn').andCallFake (command, args) -> - harmlessSpawn() - - it "ignores errors spawning Squirrel", -> - jasmine.unspy(ChildProcess, 'spawn') - spyOn(ChildProcess, 'spawn').andCallFake -> throw new Error("EBUSY") - - app = quit: jasmine.createSpy('quit') - expect(SquirrelUpdate.handleStartupEvent(app, '--squirrel-install')).toBe true - - waitsFor -> - app.quit.callCount is 1 + # Prevent any spawned command from actually running and affecting the host + spyOn(Spawner, 'spawn').andCallFake (command, args, callback) -> + # do nothing on command, just run passed callback + invokeCallback callback it "quits the app on all squirrel events", -> app = quit: jasmine.createSpy('quit') @@ -69,51 +52,52 @@ describe "Windows Squirrel Update", -> describe "Desktop shortcut", -> desktopShortcutPath = '/non/existing/path' - + beforeEach -> desktopShortcutPath = path.join(tempHomeDirectory, 'Desktop', 'Atom.lnk') - jasmine.unspy(ChildProcess, 'spawn') - spyOn(ChildProcess, 'spawn').andCallFake (command, args) -> + jasmine.unspy(Spawner, 'spawn') + spyOn(Spawner, 'spawn').andCallFake (command, args, callback) -> if path.basename(command) is 'Update.exe' and args?[0] is '--createShortcut' - fs.writeFileSync(path.join(tempHomeDirectory, 'Desktop', 'Atom.lnk'), '') - harmlessSpawn() + fs.writeFileSync(desktopShortcutPath, '') else - throw new Error("API not mocked") - + # simply ignore other commands + + invokeCallback callback + it "does not exist before install", -> expect(fs.existsSync(desktopShortcutPath)).toBe false - + describe "on install", -> beforeEach -> app = quit: jasmine.createSpy('quit') SquirrelUpdate.handleStartupEvent(app, '--squirrel-install') waitsFor -> app.quit.callCount is 1 - + it "creates desktop shortcut", -> expect(fs.existsSync(desktopShortcutPath)).toBe true - + describe "when shortcut is deleted and then app is updated", -> beforeEach -> fs.removeSync(desktopShortcutPath) expect(fs.existsSync(desktopShortcutPath)).toBe false - + app = quit: jasmine.createSpy('quit') SquirrelUpdate.handleStartupEvent(app, '--squirrel-updated') waitsFor -> app.quit.callCount is 1 - + it "does not recreate shortcut", -> expect(fs.existsSync(desktopShortcutPath)).toBe false - + describe "when shortcut is kept and app is updated", -> beforeEach -> app = quit: jasmine.createSpy('quit') SquirrelUpdate.handleStartupEvent(app, '--squirrel-updated') waitsFor -> app.quit.callCount is 1 - + it "still has desktop shortcut", -> expect(fs.existsSync(desktopShortcutPath)).toBe true @@ -125,7 +109,13 @@ describe "Windows Squirrel Update", -> SquirrelUpdate.restartAtom(app) expect(app.quit.callCount).toBe 1 - expect(ChildProcess.spawn.callCount).toBe 0 + expect(Spawner.spawn.callCount).toBe 0 app.emit('will-quit') - expect(ChildProcess.spawn.callCount).toBe 1 - expect(path.basename(ChildProcess.spawn.argsForCall[0][0])).toBe 'atom.cmd' + expect(Spawner.spawn.callCount).toBe 1 + expect(path.basename(Spawner.spawn.argsForCall[0][0])).toBe 'atom.cmd' + +# Run passed callback as Spawner.spawn() would do +invokeCallback = (callback) -> + error = null + stdout = '' + callback?(error, stdout) diff --git a/src/browser/spawner.coffee b/src/browser/spawner.coffee new file mode 100644 index 000000000..edf93182e --- /dev/null +++ b/src/browser/spawner.coffee @@ -0,0 +1,36 @@ +ChildProcess = require 'child_process' + +# Spawn a command and invoke the callback when it completes with an error +# and the output from standard out. +# +# * `command` The underlying OS command {String} to execute. +# * `args` (optional) The {Array} with arguments to be passed to command. +# * `callback` (optional) The {Function} to call after the command has run. It will be invoked with arguments: +# * `error` (optional) An {Error} object returned by the command, `null` if no error was thrown. +# * `code` Error code returned by the command. +# * `stdout` The {String} output text generated by the command. +# * `stdout` The {String} output text generated by the command. +# +# Returns `undefined`. +exports.spawn = (command, args, callback) -> + stdout = '' + + try + spawnedProcess = ChildProcess.spawn(command, args) + catch error + # Spawn can throw an error + process.nextTick -> callback?(error, stdout) + return + + spawnedProcess.stdout.on 'data', (data) -> stdout += 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) + # This is necessary if using Powershell 2 on Windows 7 to get the events to raise + # http://stackoverflow.com/questions/9155289/calling-powershell-from-nodejs + spawnedProcess.stdin.end() diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index 9d6f772db..7afc7641f 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -1,6 +1,6 @@ -ChildProcess = require 'child_process' fs = require 'fs-plus' path = require 'path' +Spawner = require './spawner' appFolder = path.resolve(process.execPath, '..') rootAtomFolder = path.resolve(appFolder, '..') @@ -25,35 +25,9 @@ backgroundKeyPath = 'HKCU\\Software\\Classes\\directory\\background\\shell\\Atom applicationsKeyPath = 'HKCU\\Software\\Classes\\Applications\\atom.exe' environmentKeyPath = 'HKCU\\Environment' -# Spawn a command and invoke the callback when it completes with an error -# and the output from standard out. -spawn = (command, args, callback) -> - stdout = '' - - try - spawnedProcess = ChildProcess.spawn(command, args) - catch error - # Spawn can throw an error - process.nextTick -> callback?(error, stdout) - return - - spawnedProcess.stdout.on 'data', (data) -> stdout += 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) - # This is necessary if using Powershell 2 on Windows 7 to get the events to raise - # http://stackoverflow.com/questions/9155289/calling-powershell-from-nodejs - spawnedProcess.stdin.end() - - # Spawn reg.exe and callback when it completes spawnReg = (args, callback) -> - spawn(regPath, args, callback) + Spawner.spawn(regPath, args, callback) # Spawn powershell.exe and callback when it completes spawnPowershell = (args, callback) -> @@ -69,16 +43,16 @@ spawnPowershell = (args, callback) -> args.unshift('RemoteSigned') args.unshift('-ExecutionPolicy') args.unshift('-noprofile') - spawn(powershellPath, args, callback) + Spawner.spawn(powershellPath, args, callback) # Spawn setx.exe and callback when it completes spawnSetx = (args, callback) -> - spawn(setxPath, args, callback) + Spawner.spawn(setxPath, args, callback) # Spawn the Update.exe with the given arguments and invoke the callback when # the command completes. spawnUpdate = (args, callback) -> - spawn(updateDotExe, args, callback) + Spawner.spawn(updateDotExe, args, callback) # Install the Open with Atom explorer context menu items via the registry. installContextMenu = (callback) -> @@ -220,7 +194,7 @@ exports.existsSync = -> exports.restartAtom = (app) -> if projectPath = global.atomApplication?.lastFocusedWindow?.projectPath args = [projectPath] - app.once 'will-quit', -> spawn(path.join(binFolder, 'atom.cmd'), args) + app.once 'will-quit', -> Spawner.spawn(path.join(binFolder, 'atom.cmd'), args) app.quit() # Handle squirrel events denoted by --squirrel-* command line arguments. From 63c45cc8fd7d37903178c2e994b0e6e24a247046 Mon Sep 17 00:00:00 2001 From: Giuseppe Piscopo Date: Thu, 21 Apr 2016 03:25:09 +0200 Subject: [PATCH 2/4] extract Windows registry operations from squirrel-update --- spec/spawner-spec.coffee | 2 +- spec/squirrel-update-spec.coffee | 24 ++++++++---- src/browser/squirrel-update.coffee | 54 ++------------------------ src/browser/win-registry.coffee | 62 ++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 59 deletions(-) create mode 100644 src/browser/win-registry.coffee diff --git a/spec/spawner-spec.coffee b/spec/spawner-spec.coffee index 4ae2149e5..daa804bbb 100644 --- a/spec/spawner-spec.coffee +++ b/spec/spawner-spec.coffee @@ -13,7 +13,7 @@ describe "Spawner", -> else originalSpawn('ls') - spyOn(ChildProcess, 'spawn').andCallFake (command, args, callback) -> + spyOn(ChildProcess, 'spawn').andCallFake (command, args, callback) -> harmlessSpawn it "invokes passed callback", -> diff --git a/spec/squirrel-update-spec.coffee b/spec/squirrel-update-spec.coffee index e11fb1f00..ecfd780d6 100644 --- a/spec/squirrel-update-spec.coffee +++ b/spec/squirrel-update-spec.coffee @@ -4,8 +4,15 @@ path = require 'path' temp = require 'temp' SquirrelUpdate = require '../src/browser/squirrel-update' Spawner = require '../src/browser/spawner' +WinRegistry = require '../src/browser/win-registry' -describe "Windows Squirrel Update", -> +# Run passed callback as Spawner.spawn() would do +invokeCallback = (callback) -> + error = null + stdout = '' + callback?(error, stdout) + +fdescribe "Windows Squirrel Update", -> tempHomeDirectory = null beforeEach -> @@ -14,10 +21,17 @@ describe "Windows Squirrel Update", -> spyOn(fs, 'getHomeDirectory').andReturn(tempHomeDirectory) # Prevent any spawned command from actually running and affecting the host - spyOn(Spawner, 'spawn').andCallFake (command, args, callback) -> + spyOn(Spawner, 'spawn').andCallFake (command, args, callback) -> # do nothing on command, just run passed callback invokeCallback callback + # Prevent any actual change to Windows registry + for own method of WinRegistry + # all WinRegistry APIs share the same signature + spyOn(WinRegistry, method).andCallFake (callback) -> + # do nothing on registry, just run passed callback + invokeCallback callback + it "quits the app on all squirrel events", -> app = quit: jasmine.createSpy('quit') @@ -113,9 +127,3 @@ describe "Windows Squirrel Update", -> app.emit('will-quit') expect(Spawner.spawn.callCount).toBe 1 expect(path.basename(Spawner.spawn.argsForCall[0][0])).toBe 'atom.cmd' - -# Run passed callback as Spawner.spawn() would do -invokeCallback = (callback) -> - error = null - stdout = '' - callback?(error, stdout) diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index 7afc7641f..58bcf2db9 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -1,6 +1,7 @@ fs = require 'fs-plus' path = require 'path' Spawner = require './spawner' +WinRegistry = require './win-registry' appFolder = path.resolve(process.execPath, '..') rootAtomFolder = path.resolve(appFolder, '..') @@ -10,25 +11,12 @@ exeName = path.basename(process.execPath) if process.env.SystemRoot system32Path = path.join(process.env.SystemRoot, 'System32') - regPath = path.join(system32Path, 'reg.exe') powershellPath = path.join(system32Path, 'WindowsPowerShell', 'v1.0', 'powershell.exe') setxPath = path.join(system32Path, 'setx.exe') else - regPath = 'reg.exe' powershellPath = 'powershell.exe' setxPath = 'setx.exe' -# Registry keys used for context menu -fileKeyPath = 'HKCU\\Software\\Classes\\*\\shell\\Atom' -directoryKeyPath = 'HKCU\\Software\\Classes\\directory\\shell\\Atom' -backgroundKeyPath = 'HKCU\\Software\\Classes\\directory\\background\\shell\\Atom' -applicationsKeyPath = 'HKCU\\Software\\Classes\\Applications\\atom.exe' -environmentKeyPath = 'HKCU\\Environment' - -# Spawn reg.exe and callback when it completes -spawnReg = (args, callback) -> - Spawner.spawn(regPath, args, callback) - # Spawn powershell.exe and callback when it completes spawnPowershell = (args, callback) -> # set encoding and execute the command, capture the output, and return it via .NET's console in order to have consistent UTF-8 encoding @@ -54,30 +42,6 @@ spawnSetx = (args, callback) -> spawnUpdate = (args, callback) -> Spawner.spawn(updateDotExe, args, callback) -# Install the Open with Atom explorer context menu items via the registry. -installContextMenu = (callback) -> - addToRegistry = (args, callback) -> - args.unshift('add') - args.push('/f') - spawnReg(args, callback) - - installFileHandler = (callback) -> - args = ["#{applicationsKeyPath}\\shell\\open\\command", '/ve', '/d', "\"#{process.execPath}\" \"%1\""] - addToRegistry(args, callback) - - installMenu = (keyPath, arg, callback) -> - args = [keyPath, '/ve', '/d', 'Open with Atom'] - addToRegistry args, -> - args = [keyPath, '/v', 'Icon', '/d', "\"#{process.execPath}\""] - addToRegistry args, -> - args = ["#{keyPath}\\command", '/ve', '/d', "\"#{process.execPath}\" \"#{arg}\""] - addToRegistry(args, callback) - - installMenu fileKeyPath, '%1', -> - installMenu directoryKeyPath, '%1', -> - installMenu backgroundKeyPath, '%V', -> - installFileHandler(callback) - # Get the user's PATH environment variable registry value. getPath = (callback) -> spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) -> @@ -87,16 +51,6 @@ getPath = (callback) -> pathOutput = stdout.replace(/^\s+|\s+$/g, '') callback(null, pathOutput) -# Uninstall the Open with Atom explorer context menu items via the registry. -uninstallContextMenu = (callback) -> - deleteFromRegistry = (keyPath, callback) -> - spawnReg(['delete', keyPath, '/f'], callback) - - deleteFromRegistry fileKeyPath, -> - deleteFromRegistry directoryKeyPath, -> - deleteFromRegistry backgroundKeyPath, -> - deleteFromRegistry(applicationsKeyPath, callback) - # Add atom and apm to the PATH # # This is done by adding .cmd shims to the root bin folder in the Atom @@ -202,19 +156,19 @@ exports.handleStartupEvent = (app, squirrelCommand) -> switch squirrelCommand when '--squirrel-install' createShortcuts -> - installContextMenu -> + WinRegistry.installContextMenu -> addCommandsToPath -> app.quit() true when '--squirrel-updated' updateShortcuts -> - installContextMenu -> + WinRegistry.installContextMenu -> addCommandsToPath -> app.quit() true when '--squirrel-uninstall' removeShortcuts -> - uninstallContextMenu -> + WinRegistry.uninstallContextMenu -> removeCommandsFromPath -> app.quit() true diff --git a/src/browser/win-registry.coffee b/src/browser/win-registry.coffee new file mode 100644 index 000000000..f4b81b377 --- /dev/null +++ b/src/browser/win-registry.coffee @@ -0,0 +1,62 @@ +path = require 'path' +Spawner = require './spawner' + +if process.env.SystemRoot + system32Path = path.join(process.env.SystemRoot, 'System32') + regPath = path.join(system32Path, 'reg.exe') +else + regPath = 'reg.exe' + +# Registry keys used for context menu +fileKeyPath = 'HKCU\\Software\\Classes\\*\\shell\\Atom' +directoryKeyPath = 'HKCU\\Software\\Classes\\directory\\shell\\Atom' +backgroundKeyPath = 'HKCU\\Software\\Classes\\directory\\background\\shell\\Atom' +applicationsKeyPath = 'HKCU\\Software\\Classes\\Applications\\atom.exe' + +# Spawn reg.exe and callback when it completes +spawnReg = (args, callback) -> + Spawner.spawn(regPath, args, callback) + +# Install the Open with Atom explorer context menu items via the registry. +# +# * `callback` The {Function} to call after registry operation is done. +# It will be invoked with the same arguments provided by {Spawner.spawn}. +# +# Returns `undefined`. +exports.installContextMenu = (callback) -> + addToRegistry = (args, callback) -> + args.unshift('add') + args.push('/f') + spawnReg(args, callback) + + installFileHandler = (callback) -> + args = ["#{applicationsKeyPath}\\shell\\open\\command", '/ve', '/d', "\"#{process.execPath}\" \"%1\""] + addToRegistry(args, callback) + + installMenu = (keyPath, arg, callback) -> + args = [keyPath, '/ve', '/d', 'Open with Atom'] + addToRegistry args, -> + args = [keyPath, '/v', 'Icon', '/d', "\"#{process.execPath}\""] + addToRegistry args, -> + args = ["#{keyPath}\\command", '/ve', '/d', "\"#{process.execPath}\" \"#{arg}\""] + addToRegistry(args, callback) + + installMenu fileKeyPath, '%1', -> + installMenu directoryKeyPath, '%1', -> + installMenu backgroundKeyPath, '%V', -> + installFileHandler(callback) + +# Uninstall the Open with Atom explorer context menu items via the registry. +# +# * `callback` The {Function} to call after registry operation is done. +# It will be invoked with the same arguments provided by {Spawner.spawn}. +# +# Returns `undefined`. +exports.uninstallContextMenu = (callback) -> + deleteFromRegistry = (keyPath, callback) -> + spawnReg(['delete', keyPath, '/f'], callback) + + deleteFromRegistry fileKeyPath, -> + deleteFromRegistry directoryKeyPath, -> + deleteFromRegistry backgroundKeyPath, -> + deleteFromRegistry(applicationsKeyPath, callback) From bebaf1bdb08db56a527c625d6c537e356a33cf16 Mon Sep 17 00:00:00 2001 From: Giuseppe Piscopo Date: Thu, 28 Apr 2016 09:24:37 +0200 Subject: [PATCH 3/4] extract Windows PowerShell operations from squirrel-update --- spec/squirrel-update-spec.coffee | 3 ++- src/browser/squirrel-update.coffee | 32 +++--------------------- src/browser/win-powershell.coffee | 39 ++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 30 deletions(-) create mode 100644 src/browser/win-powershell.coffee diff --git a/spec/squirrel-update-spec.coffee b/spec/squirrel-update-spec.coffee index ecfd780d6..4a6936a50 100644 --- a/spec/squirrel-update-spec.coffee +++ b/spec/squirrel-update-spec.coffee @@ -4,6 +4,7 @@ path = require 'path' temp = require 'temp' SquirrelUpdate = require '../src/browser/squirrel-update' Spawner = require '../src/browser/spawner' +WinPowerShell = require '../src/browser/win-powershell' WinRegistry = require '../src/browser/win-registry' # Run passed callback as Spawner.spawn() would do @@ -12,7 +13,7 @@ invokeCallback = (callback) -> stdout = '' callback?(error, stdout) -fdescribe "Windows Squirrel Update", -> +describe "Windows Squirrel Update", -> tempHomeDirectory = null beforeEach -> diff --git a/src/browser/squirrel-update.coffee b/src/browser/squirrel-update.coffee index 58bcf2db9..a1bfc5359 100644 --- a/src/browser/squirrel-update.coffee +++ b/src/browser/squirrel-update.coffee @@ -2,6 +2,7 @@ fs = require 'fs-plus' path = require 'path' Spawner = require './spawner' WinRegistry = require './win-registry' +WinPowerShell = require './win-powershell' appFolder = path.resolve(process.execPath, '..') rootAtomFolder = path.resolve(appFolder, '..') @@ -11,28 +12,10 @@ exeName = path.basename(process.execPath) if process.env.SystemRoot system32Path = path.join(process.env.SystemRoot, 'System32') - powershellPath = path.join(system32Path, 'WindowsPowerShell', 'v1.0', 'powershell.exe') setxPath = path.join(system32Path, 'setx.exe') else - powershellPath = 'powershell.exe' setxPath = 'setx.exe' -# Spawn powershell.exe and callback when it completes -spawnPowershell = (args, callback) -> - # set encoding and execute the command, capture the output, and return it via .NET's console in order to have consistent UTF-8 encoding - # http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell - # to address https://github.com/atom/atom/issues/5063 - args[0] = """ - [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 - $output=#{args[0]} - [Console]::WriteLine($output) - """ - args.unshift('-command') - args.unshift('RemoteSigned') - args.unshift('-ExecutionPolicy') - args.unshift('-noprofile') - Spawner.spawn(powershellPath, args, callback) - # Spawn setx.exe and callback when it completes spawnSetx = (args, callback) -> Spawner.spawn(setxPath, args, callback) @@ -42,15 +25,6 @@ spawnSetx = (args, callback) -> spawnUpdate = (args, callback) -> Spawner.spawn(updateDotExe, args, callback) -# Get the user's PATH environment variable registry value. -getPath = (callback) -> - spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) -> - if error? - return callback(error) - - pathOutput = stdout.replace(/^\s+|\s+$/g, '') - callback(null, pathOutput) - # Add atom and apm to the PATH # # This is done by adding .cmd shims to the root bin folder in the Atom @@ -88,7 +62,7 @@ addCommandsToPath = (callback) -> installCommands (error) -> return callback(error) if error? - getPath (error, pathEnv) -> + WinPowerShell.getPath (error, pathEnv) -> return callback(error) if error? pathSegments = pathEnv.split(/;+/).filter (pathSegment) -> pathSegment @@ -99,7 +73,7 @@ addCommandsToPath = (callback) -> # Remove atom and apm from the PATH removeCommandsFromPath = (callback) -> - getPath (error, pathEnv) -> + WinPowerShell.getPath (error, pathEnv) -> return callback(error) if error? pathSegments = pathEnv.split(/;+/).filter (pathSegment) -> diff --git a/src/browser/win-powershell.coffee b/src/browser/win-powershell.coffee new file mode 100644 index 000000000..f3e73bc8a --- /dev/null +++ b/src/browser/win-powershell.coffee @@ -0,0 +1,39 @@ +path = require 'path' +Spawner = require './spawner' + +if process.env.SystemRoot + system32Path = path.join(process.env.SystemRoot, 'System32') + powershellPath = path.join(system32Path, 'WindowsPowerShell', 'v1.0', 'powershell.exe') +else + powershellPath = 'powershell.exe' + +# Spawn powershell.exe and callback when it completes +spawnPowershell = (args, callback) -> + # Set encoding and execute the command, capture the output, and return it + # via .NET's console in order to have consistent UTF-8 encoding. + # See http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell + # to address https://github.com/atom/atom/issues/5063 + args[0] = """ + [Console]::OutputEncoding=[System.Text.Encoding]::UTF8 + $output=#{args[0]} + [Console]::WriteLine($output) + """ + args.unshift('-command') + args.unshift('RemoteSigned') + args.unshift('-ExecutionPolicy') + args.unshift('-noprofile') + Spawner.spawn(powershellPath, args, callback) + +# Get the user's PATH environment variable registry value. +# +# * `callback` The {Function} to call after registry operation is done. +# It will be invoked with the same arguments provided by {Spawner.spawn}. +# +# Returns the user's path {String}. +exports.getPath = (callback) -> + spawnPowershell ['[environment]::GetEnvironmentVariable(\'Path\',\'User\')'], (error, stdout) -> + if error? + return callback(error) + + pathOutput = stdout.replace(/^\s+|\s+$/g, '') + callback(null, pathOutput) From 07bf40879d0526f4255726e5c63d3720dcb75037 Mon Sep 17 00:00:00 2001 From: Giuseppe Piscopo Date: Thu, 28 Apr 2016 19:25:54 +0200 Subject: [PATCH 4/4] Fix coffeelint error on trailing whitespace --- src/browser/win-powershell.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/win-powershell.coffee b/src/browser/win-powershell.coffee index f3e73bc8a..29925a7c1 100644 --- a/src/browser/win-powershell.coffee +++ b/src/browser/win-powershell.coffee @@ -9,7 +9,7 @@ else # Spawn powershell.exe and callback when it completes spawnPowershell = (args, callback) -> - # Set encoding and execute the command, capture the output, and return it + # Set encoding and execute the command, capture the output, and return it # via .NET's console in order to have consistent UTF-8 encoding. # See http://stackoverflow.com/questions/22349139/utf-8-output-from-powershell # to address https://github.com/atom/atom/issues/5063