Created
February 1, 2015 08:06
-
-
Save cappert/fac1dba362d6a93a90f4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Atom context menu pollution | |
https://github.com/atom/atom/blob/master/src/browser/squirrel-update.coffee |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
ChildProcess = require 'child_process' | |
fs = require 'fs-plus' | |
path = require 'path' | |
appFolder = path.resolve(process.execPath, '..') | |
rootAtomFolder = path.resolve(appFolder, '..') | |
binFolder = path.join(rootAtomFolder, 'bin') | |
updateDotExe = path.join(rootAtomFolder, 'Update.exe') | |
exeName = path.basename(process.execPath) | |
if process.env.SystemRoot | |
system32Path = path.join(process.env.SystemRoot, 'System32') | |
regPath = path.join(system32Path, 'reg.exe') | |
setxPath = path.join(system32Path, 'setx.exe') | |
else | |
regPath = 'reg.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' | |
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) | |
# Spawn reg.exe and callback when it completes | |
spawnReg = (args, callback) -> | |
spawn(regPath, args, callback) | |
# Spawn setx.exe and callback when it completes | |
spawnSetx = (args, callback) -> | |
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) | |
# 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) | |
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', callback) | |
# Get the user's PATH environment variable registry value. | |
getPath = (callback) -> | |
spawnReg ['query', environmentKeyPath, '/v', 'Path'], (error, stdout) -> | |
if error? | |
if error.code is 1 | |
# The query failed so the Path does not exist yet in the registry | |
return callback(null, '') | |
else | |
return callback(error) | |
# Registry query output is in the form: | |
# | |
# HKEY_CURRENT_USER\Environment | |
# Path REG_SZ C:\a\folder\on\the\path;C\another\folder | |
# | |
lines = stdout.split(/[\r\n]+/).filter (line) -> line | |
segments = lines[lines.length - 1]?.split(' ') | |
if segments[1] is 'Path' and segments.length >= 3 | |
pathEnv = segments?[3..].join(' ') | |
callback(null, pathEnv) | |
else | |
callback(new Error('Registry query for PATH failed')) | |
# 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, callback) | |
# Add atom and apm to the PATH | |
# | |
# This is done by adding .cmd shims to the root bin folder in the Atom | |
# install directory that point to the newly installed versions inside | |
# the versioned app directories. | |
addCommandsToPath = (callback) -> | |
installCommands = (callback) -> | |
atomCommandPath = path.join(binFolder, 'atom.cmd') | |
relativeAtomPath = path.relative(binFolder, path.join(appFolder, 'resources', 'cli', 'atom.cmd')) | |
atomCommand = "@echo off\r\n\"%~dp0\\#{relativeAtomPath}\" %*" | |
atomShCommandPath = path.join(binFolder, 'atom') | |
relativeAtomShPath = path.relative(binFolder, path.join(appFolder, 'resources', 'cli', 'atom.sh')) | |
atomShCommand = "#!/bin/sh\r\n\"$0/../#{relativeAtomShPath.replace(/\\/g, '/')}\" \"$@\"" | |
apmCommandPath = path.join(binFolder, 'apm.cmd') | |
relativeApmPath = path.relative(binFolder, path.join(process.resourcesPath, 'app', 'apm', 'bin', 'apm.cmd')) | |
apmCommand = "@echo off\r\n\"%~dp0\\#{relativeApmPath}\" %*" | |
apmShCommandPath = path.join(binFolder, 'apm') | |
relativeApmShPath = path.relative(binFolder, path.join(appFolder, 'resources', 'cli', 'apm.sh')) | |
apmShCommand = "#!/bin/sh\r\n\"$0/../#{relativeApmShPath.replace(/\\/g, '/')}\" \"$@\"" | |
fs.writeFile atomCommandPath, atomCommand, -> | |
fs.writeFile atomShCommandPath, atomShCommand, -> | |
fs.writeFile apmCommandPath, apmCommand, -> | |
fs.writeFile apmShCommandPath, apmShCommand, -> | |
callback() | |
addBinToPath = (pathSegments, callback) -> | |
pathSegments.push(binFolder) | |
newPathEnv = pathSegments.join(';') | |
spawnSetx(['Path', newPathEnv], callback) | |
installCommands (error) -> | |
return callback(error) if error? | |
getPath (error, pathEnv) -> | |
return callback(error) if error? | |
pathSegments = pathEnv.split(/;+/).filter (pathSegment) -> pathSegment | |
if pathSegments.indexOf(binFolder) is -1 | |
addBinToPath(pathSegments, callback) | |
else | |
callback() | |
# Remove atom and apm from the PATH | |
removeCommandsFromPath = (callback) -> | |
getPath (error, pathEnv) -> | |
return callback(error) if error? | |
pathSegments = pathEnv.split(/;+/).filter (pathSegment) -> | |
pathSegment and pathSegment isnt binFolder | |
newPathEnv = pathSegments.join(';') | |
if pathEnv isnt newPathEnv | |
spawnSetx(['Path', newPathEnv], callback) | |
else | |
callback() | |
# Create a desktop and start menu shortcut by using the command line API | |
# provided by Squirrel's Update.exe | |
createShortcuts = (callback) -> | |
spawnUpdate(['--createShortcut', exeName], callback) | |
# Update the desktop and start menu shortcuts by using the command line API | |
# provided by Squirrel's Update.exe | |
updateShortcuts = (callback) -> | |
if homeDirectory = fs.getHomeDirectory() | |
desktopShortcutPath = path.join(homeDirectory, 'Desktop', 'Atom.lnk') | |
# Check if the desktop shortcut has been previously deleted and | |
# and keep it deleted if it was | |
fs.exists desktopShortcutPath, (desktopShortcutExists) -> | |
createShortcuts -> | |
if desktopShortcutExists | |
callback() | |
else | |
# Remove the unwanted desktop shortcut that was recreated | |
fs.unlink(desktopShortcutPath, callback) | |
else | |
createShortcuts(callback) | |
# Remove the desktop and start menu shortcuts by using the command line API | |
# provided by Squirrel's Update.exe | |
removeShortcuts = (callback) -> | |
spawnUpdate(['--removeShortcut', exeName], callback) | |
exports.spawn = spawnUpdate | |
# Is the Update.exe installed with Atom? | |
exports.existsSync = -> | |
fs.existsSync(updateDotExe) | |
# Restart Atom using the version pointed to by the atom.cmd shim | |
exports.restartAtom = (app) -> | |
if projectPath = global.atomApplication?.lastFocusedWindow?.projectPath | |
args = [projectPath] | |
app.once 'will-quit', -> spawn(path.join(binFolder, 'atom.cmd'), args) | |
app.quit() | |
# Handle squirrel events denoted by --squirrel-* command line arguments. | |
exports.handleStartupEvent = (app, squirrelCommand) -> | |
switch squirrelCommand | |
when '--squirrel-install' | |
createShortcuts -> | |
installContextMenu -> | |
addCommandsToPath -> | |
app.quit() | |
true | |
when '--squirrel-updated' | |
updateShortcuts -> | |
installContextMenu -> | |
addCommandsToPath -> | |
app.quit() | |
true | |
when '--squirrel-uninstall' | |
removeShortcuts -> | |
uninstallContextMenu -> | |
removeCommandsFromPath -> | |
app.quit() | |
true | |
when '--squirrel-obsolete' | |
app.quit() | |
true | |
else | |
false |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment