From d620da33f2dd57ef574c6c76535fa45f46569b23 Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Wed, 19 Aug 2015 07:50:11 -0700 Subject: [PATCH 01/22] Enable Portable Mode This PR sets the default ATOM_HOME to be relative to atom.exe, if the following cases are true: 1. We're not in DevMode 1. We're on Windows 1. The EXE path is not in the normal installed location This allows users to take the entire Atom folder and use it as a "Portable" application (i.e. portableapps.com) --- static/index.js | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/static/index.js b/static/index.js index 914290321..a128036c8 100644 --- a/static/index.js +++ b/static/index.js @@ -40,6 +40,21 @@ } } + function isPortableMode() { + // No portable mode on non-Windows + if (process.platform !== 'win32') return false + + // DevMode? Nope + var devMode = loadSettings && + (loadSettings.devMode || !loadSettings.resourcePath.startsWith(process.resourcesPath + path.sep)) + + if (devMode) return false + + // Compare our EXE's path to where it would normally be in an installed app + var ourPath = process.execPath.toLowerCase() + return (ourPath.indexOf(process.env.LOCALAPPDATA.toLowerCase()) === 0) + } + function setLoadTime (loadTime) { if (global.atom) { global.atom.loadTime = loadTime @@ -95,7 +110,11 @@ try { atomHome = fs.realpathSync(atomHome) } catch (error) { - // Ignore since the path might just not exist yet. + // If we're in portable mode *and* the user doesn't already have a .atom + // folder in the normal place, we'll use the portable folder instead + if (isPortableMode()) { + atomHome = path.join(path.dirname(process.execPath), '.atom') + } } process.env.ATOM_HOME = atomHome } From 4a536197800b7d08cf63d5b58047589db800c740 Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Mon, 7 Sep 2015 11:42:18 -0700 Subject: [PATCH 02/22] Patch the browser process too --- src/browser/main.coffee | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 33a58289c..573e42f27 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -56,11 +56,22 @@ handleStartupEventWithSquirrel = -> setupCrashReporter = -> crashReporter.start(productName: 'Atom', companyName: 'GitHub') +isPortableInstall = -> + return false unless process.platform is 'win32' + return false unless (process and process.type) + + ourPath = process.execPath.toLowerCase() + return (ourPath.indexOf(process.env.LOCALAPPDATA.toLowerCase()) is 0) + setupAtomHome = -> return if process.env.ATOM_HOME atomHome = path.join(app.getHomeDir(), '.atom') try atomHome = fs.realpathSync(atomHome) + fs.statSync(atomHome) + catch + atomHome = path.join(path.dirname(process.execPath), '.atom') if isPortableInstall() + process.env.ATOM_HOME = atomHome setupCompileCache = -> From 477559581e74e0a7c74511e1981f12dd130b97bf Mon Sep 17 00:00:00 2001 From: Paul Betts Date: Mon, 7 Sep 2015 12:08:32 -0700 Subject: [PATCH 03/22] Correctly handle devMode --- src/browser/main.coffee | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 573e42f27..7bd7f9fce 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -8,13 +8,13 @@ yargs = require 'yargs' console.log = require 'nslog' start = -> + args = parseCommandLine() + setupUncaughtExceptionHandler() - setupAtomHome() + setupAtomHome(args) setupCompileCache() return if handleStartupEventWithSquirrel() - args = parseCommandLine() - addPathToOpen = (event, pathToOpen) -> event.preventDefault() args.pathsToOpen.push(pathToOpen) @@ -56,21 +56,23 @@ handleStartupEventWithSquirrel = -> setupCrashReporter = -> crashReporter.start(productName: 'Atom', companyName: 'GitHub') -isPortableInstall = -> +isPortableInstall = (args) -> return false unless process.platform is 'win32' return false unless (process and process.type) + return false if args.devMode or args.testMode ourPath = process.execPath.toLowerCase() return (ourPath.indexOf(process.env.LOCALAPPDATA.toLowerCase()) is 0) -setupAtomHome = -> +setupAtomHome = (args) -> return if process.env.ATOM_HOME + atomHome = path.join(app.getHomeDir(), '.atom') try atomHome = fs.realpathSync(atomHome) fs.statSync(atomHome) catch - atomHome = path.join(path.dirname(process.execPath), '.atom') if isPortableInstall() + atomHome = path.join(path.dirname(process.execPath), '.atom') if isPortableInstall(args) process.env.ATOM_HOME = atomHome From b8a153781e89ef1812fac7fe6db05569ac0bc404 Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Fri, 9 Oct 2015 10:54:30 -0600 Subject: [PATCH 04/22] Implement Portable Mode According to these specifications: Portable mode only applies to Windows (for now) Portable mode only applies if ATOM_HOME is not set If there is a .atom directory as sibling to directory with running process use that as ATOM_HOME --- spec/atom-portable-spec.coffee | 52 ++++++++++++++++++++++++++++++++ src/browser/atom-portable.coffee | 15 +++++++++ src/browser/main.coffee | 18 +++-------- 3 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 spec/atom-portable-spec.coffee create mode 100644 src/browser/atom-portable.coffee diff --git a/spec/atom-portable-spec.coffee b/spec/atom-portable-spec.coffee new file mode 100644 index 000000000..f5b24a4a1 --- /dev/null +++ b/spec/atom-portable-spec.coffee @@ -0,0 +1,52 @@ +path = require "path" +fs = require 'fs-plus' +temp = require "temp" +rimraf = require "rimraf" +AtomPortable = require "../src/browser/atom-portable" + +fdescribe "Portable Mode", -> + describe "Windows", -> + platform = "win32" + + describe "with ATOM_HOME environment variable", -> + environmentAtomHome = "C:\\some\\path" + it "returns false", -> + expect(AtomPortable.isPortableInstall(platform, environmentAtomHome)).toBe false + + describe "without ATOM_HOME environment variable", -> + environmentAtomHome = undefined + portableAtomHomePath = path.join(path.dirname(process.execPath), "../.atom").toString() + portableAtomHomeNaturallyExists = fs.existsSync(portableAtomHomePath) + portableAtomHomeBackupPath = portableAtomHomePath + ".temp" + + beforeEach -> + fs.renameSync(portableAtomHomePath, portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomePath) + + afterEach -> + if portableAtomHomeNaturallyExists + fs.renameSync(portableAtomHomeBackupPath, portableAtomHomePath) if not fs.existsSync(portableAtomHomePath) + else + rimraf.sync(portableAtomHomePath) if fs.existsSync(portableAtomHomePath) + rimraf.sync(portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomeBackupPath) + + describe "with .atom directory sibling to exec", -> + beforeEach -> + fs.mkdirSync(portableAtomHomePath) if not fs.existsSync(portableAtomHomePath) + it "returns true", -> + expect(AtomPortable.isPortableInstall(platform, environmentAtomHome)).toBe true + + describe "without .atom directory sibling to exec", -> + beforeEach -> + rimraf.sync(portableAtomHomePath) if fs.existsSync(portableAtomHomePath) + it "returns false", -> + expect(AtomPortable.isPortableInstall(platform, environmentAtomHome)).toBe false + + describe "Mac", -> + platform = "darwin" + it "returns false", -> + expect(AtomPortable.isPortableInstall(platform, platform)).toBe false + + describe "Linux", -> + platform = "linux" + it "returns false", -> + expect(AtomPortable.isPortableInstall(platform, platform)).toBe false diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee new file mode 100644 index 000000000..2a8e4d7a0 --- /dev/null +++ b/src/browser/atom-portable.coffee @@ -0,0 +1,15 @@ +fs = require 'fs-plus' +path = require 'path' + +module.exports = +class AtomPortable + @portableAtomHomePath: -> + execDirectoryPath = path.dirname(process.execPath) + return path.join(execDirectoryPath, "../.atom") + @isPortableInstall: (platform, environmentAtomHome) -> + return false unless platform is 'win32' + return false if environmentAtomHome + + # currently checking only that the directory exists, probably want to do + # some integrity checks on contents and make sure it's writable in future + return fs.existsSync(@portableAtomHomePath()) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index cfac6f34c..1db0b87e7 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -57,23 +57,13 @@ handleStartupEventWithSquirrel = -> setupCrashReporter = -> crashReporter.start(productName: 'Atom', companyName: 'GitHub') -isPortableInstall = (args) -> - return false unless process.platform is 'win32' - return false unless (process and process.type) - return false if args.devMode or args.testMode - - ourPath = process.execPath.toLowerCase() - return (ourPath.indexOf(process.env.LOCALAPPDATA.toLowerCase()) is 0) - setupAtomHome = (args) -> return if process.env.ATOM_HOME - atomHome = path.join(app.getHomeDir(), '.atom') - try - atomHome = fs.realpathSync(atomHome) - fs.statSync(atomHome) - catch - atomHome = path.join(path.dirname(process.execPath), '.atom') if isPortableInstall(args) + AtomPortable = require './atom-portable' + atomHome = AtomPortable.portableAtomHomePath() if AtomPortable.isPortableInstall process.platform, process.env.ATOM_HOME + atomHome = fs.realpathSync(atomHome) + fs.statSync(atomHome) process.env.ATOM_HOME = atomHome From db57479bf92474bf9bfb832c70a2c5c947238e8d Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Fri, 9 Oct 2015 11:21:41 -0600 Subject: [PATCH 05/22] Remove superfluous check on ATOM_HOME always set at the process level, so always set here --- static/index.js | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/static/index.js b/static/index.js index 11527e963..9d09bf060 100644 --- a/static/index.js +++ b/static/index.js @@ -13,9 +13,6 @@ console.error('Unhandled promise rejection %o with error: %o', promise, error) }) - // Ensure ATOM_HOME is always set before anything else is required - setupAtomHome() - // Normalize to make sure drive letter case is consistent on Windows process.resourcesPath = path.normalize(process.resourcesPath) @@ -80,24 +77,6 @@ require('ipc').sendChannel('window-command', 'window:loaded') } - function setupAtomHome () { - if (!process.env.ATOM_HOME) { - var home - if (process.platform === 'win32') { - home = process.env.USERPROFILE - } else { - home = process.env.HOME - } - var atomHome = path.join(home, '.atom') - try { - atomHome = fs.realpathSync(atomHome) - } catch (error) { - // Ignore since the path might just not exist yet. - } - process.env.ATOM_HOME = atomHome - } - } - function setupCsonCache (cacheDir) { require('season').setCacheDir(path.join(cacheDir, 'cson')) } From 28c322a4cfb97bf6601167a98fcd3a1c4491c512 Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Fri, 9 Oct 2015 11:32:56 -0600 Subject: [PATCH 06/22] Fix broken focused suite in spec --- spec/atom-portable-spec.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/atom-portable-spec.coffee b/spec/atom-portable-spec.coffee index f5b24a4a1..9b1413eaa 100644 --- a/spec/atom-portable-spec.coffee +++ b/spec/atom-portable-spec.coffee @@ -4,7 +4,7 @@ temp = require "temp" rimraf = require "rimraf" AtomPortable = require "../src/browser/atom-portable" -fdescribe "Portable Mode", -> +describe "Portable Mode", -> describe "Windows", -> platform = "win32" From 4312f76ed7162b85cc10a6caa044c8881045a666 Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Tue, 13 Oct 2015 05:21:54 -0600 Subject: [PATCH 07/22] Added command line parameter to set portable If parameter included, home directory will be copied to the portable location to make this a portable install --- spec/atom-portable-spec.coffee | 21 ++++++++++++++++++++- src/browser/atom-portable.coffee | 6 +++++- src/browser/main.coffee | 5 ++++- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/spec/atom-portable-spec.coffee b/spec/atom-portable-spec.coffee index 9b1413eaa..2e4feae88 100644 --- a/spec/atom-portable-spec.coffee +++ b/spec/atom-portable-spec.coffee @@ -4,7 +4,26 @@ temp = require "temp" rimraf = require "rimraf" AtomPortable = require "../src/browser/atom-portable" -describe "Portable Mode", -> +describe "Set Portable Mode", -> + portableAtomHomePath = path.join(path.dirname(process.execPath), "../.atom").toString() + portableAtomHomeNaturallyExists = fs.existsSync(portableAtomHomePath) + portableAtomHomeBackupPath = portableAtomHomePath + ".temp" + + beforeEach -> + fs.renameSync(portableAtomHomePath, portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomePath) + + afterEach -> + if portableAtomHomeNaturallyExists + fs.renameSync(portableAtomHomeBackupPath, portableAtomHomePath) if not fs.existsSync(portableAtomHomePath) + else + rimraf.sync(portableAtomHomePath) if fs.existsSync(portableAtomHomePath) + rimraf.sync(portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomeBackupPath) + + it "creates portable home directory", -> + AtomPortable.setPortable(process.env.ATOM_HOME) + expect(fs.existsSync(portableAtomHomePath)).toBe true + +describe "Check for Portable Mode", -> describe "Windows", -> platform = "win32" diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index 2a8e4d7a0..b8c4281d5 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -5,7 +5,11 @@ module.exports = class AtomPortable @portableAtomHomePath: -> execDirectoryPath = path.dirname(process.execPath) - return path.join(execDirectoryPath, "../.atom") + return path.join(execDirectoryPath, "../.atom/") + + @setPortable: (existingAtomHome) -> + fs.copySync(existingAtomHome, @portableAtomHomePath()) + @isPortableInstall: (platform, environmentAtomHome) -> return false unless platform is 'win32' return false if environmentAtomHome diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 1db0b87e7..59875351f 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -61,6 +61,7 @@ setupAtomHome = (args) -> return if process.env.ATOM_HOME atomHome = path.join(app.getHomeDir(), '.atom') AtomPortable = require './atom-portable' + AtomPortable.setPortable(atomHome) if not AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME) and args.setPortable atomHome = AtomPortable.portableAtomHomePath() if AtomPortable.isPortableInstall process.platform, process.env.ATOM_HOME atomHome = fs.realpathSync(atomHome) fs.statSync(atomHome) @@ -107,6 +108,7 @@ parseCommandLine = -> options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.') options.alias('v', 'version').boolean('v').describe('v', 'Print the version.') options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.') + options.alias('p', 'set-portable').boolean('p').describe('p', 'Set portable mode.') options.string('socket-path') args = options.argv @@ -132,6 +134,7 @@ parseCommandLine = -> profileStartup = args['profile-startup'] urlsToOpen = [] devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH ? path.join(app.getHomeDir(), 'github', 'atom') + setPortable = args['set-portable'] if args['resource-path'] devMode = true @@ -161,6 +164,6 @@ parseCommandLine = -> {resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, specDirectory, - logFile, socketPath, profileStartup} + logFile, socketPath, profileStartup, setPortable} start() From 16c2391b1447c66b07e9fa5a2a603bc9f6d30c7f Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Wed, 14 Oct 2015 13:49:04 -0600 Subject: [PATCH 08/22] Add notification if Portable Home not writable --- src/atom.coffee | 9 +++++++++ src/browser/atom-portable.coffee | 25 ++++++++++++++++++++++--- 2 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/atom.coffee b/src/atom.coffee index 59d7765ac..3a3c5c6aa 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -243,6 +243,15 @@ class Atom extends Model @windowEventHandler = new WindowEventHandler + checkPortableHomeWritable = -> + responseChannel = "check-portable-home-writable-response" + ipc.on responseChannel, (response) -> + ipc.removeAllListeners(responseChannel) + atom.notifications.addWarning(response.message) if not response.writable + ipc.send('check-portable-home-writable', responseChannel) + + checkPortableHomeWritable() + # Register the core views as early as possible in case they are needed for # package deserialization. registerViewProviders: -> diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index b8c4281d5..f845f05d0 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -1,5 +1,6 @@ fs = require 'fs-plus' path = require 'path' +ipc = require 'ipc' module.exports = class AtomPortable @@ -14,6 +15,24 @@ class AtomPortable return false unless platform is 'win32' return false if environmentAtomHome - # currently checking only that the directory exists, probably want to do - # some integrity checks on contents and make sure it's writable in future - return fs.existsSync(@portableAtomHomePath()) + return false if not fs.existsSync(@portableAtomHomePath()) + + # currently checking only that the directory exists and is writable, + # probably want to do some integrity checks on contents in future + return @portableAtomHomePathWritable() + + @portableAtomHomePathWritable: -> + writable = false + message = "" + try + writePermissionTestFile = path.join(@portableAtomHomePath(), "write.test") + fs.writeFileSync(writePermissionTestFile, "test") if not fs.existsSync(writePermissionTestFile) + fs.removeSync(writePermissionTestFile) + writable = true + catch error + message = "Failed to use portable Atom home directory. Using the default instead." + message = "Portable Atom home directory (#{@portableAtomHomePath()}) is not writable. Using the default instead." if error.code == "EPERM" + + ipc.on 'check-portable-home-writable', (event, arg) -> + event.sender.send 'check-portable-home-writable-response', {writable, message} + return writable From 265ee01c1dc5d68e67101fa4584e8e63f8b9a29f Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Thu, 15 Oct 2015 14:32:33 -0600 Subject: [PATCH 09/22] Clean convention inconsistencies for portable mode --- spec/atom-portable-spec.coffee | 5 +++-- src/browser/atom-portable.coffee | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/spec/atom-portable-spec.coffee b/spec/atom-portable-spec.coffee index 2e4feae88..50e7425c0 100644 --- a/spec/atom-portable-spec.coffee +++ b/spec/atom-portable-spec.coffee @@ -5,9 +5,9 @@ rimraf = require "rimraf" AtomPortable = require "../src/browser/atom-portable" describe "Set Portable Mode", -> - portableAtomHomePath = path.join(path.dirname(process.execPath), "../.atom").toString() + portableAtomHomePath = path.join(path.dirname(process.execPath), '..', '.atom') portableAtomHomeNaturallyExists = fs.existsSync(portableAtomHomePath) - portableAtomHomeBackupPath = portableAtomHomePath + ".temp" + portableAtomHomeBackupPath = "#{portableAtomHomePath}.temp" beforeEach -> fs.renameSync(portableAtomHomePath, portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomePath) @@ -19,6 +19,7 @@ describe "Set Portable Mode", -> rimraf.sync(portableAtomHomePath) if fs.existsSync(portableAtomHomePath) rimraf.sync(portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomeBackupPath) + it "creates portable home directory", -> AtomPortable.setPortable(process.env.ATOM_HOME) expect(fs.existsSync(portableAtomHomePath)).toBe true diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index f845f05d0..8e044ff24 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -6,7 +6,7 @@ module.exports = class AtomPortable @portableAtomHomePath: -> execDirectoryPath = path.dirname(process.execPath) - return path.join(execDirectoryPath, "../.atom/") + return path.join(execDirectoryPath, '..', '.atom') @setPortable: (existingAtomHome) -> fs.copySync(existingAtomHome, @portableAtomHomePath()) From 28a1bbf9a2a214183bd744d5e097d7fd1641cc9b Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Thu, 15 Oct 2015 14:35:04 -0600 Subject: [PATCH 10/22] Fix function names - consistent with convention --- src/browser/atom-portable.coffee | 12 +++++------- src/browser/main.coffee | 6 +++--- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index 8e044ff24..2a6136a98 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -4,19 +4,17 @@ ipc = require 'ipc' module.exports = class AtomPortable - @portableAtomHomePath: -> + @getPortableAtomHomePath: -> execDirectoryPath = path.dirname(process.execPath) return path.join(execDirectoryPath, '..', '.atom') @setPortable: (existingAtomHome) -> - fs.copySync(existingAtomHome, @portableAtomHomePath()) + fs.copySync(existingAtomHome, @getPortableAtomHomePath()) @isPortableInstall: (platform, environmentAtomHome) -> return false unless platform is 'win32' return false if environmentAtomHome - - return false if not fs.existsSync(@portableAtomHomePath()) - + return false if not fs.existsSync(@getPortableAtomHomePath()) # currently checking only that the directory exists and is writable, # probably want to do some integrity checks on contents in future return @portableAtomHomePathWritable() @@ -25,13 +23,13 @@ class AtomPortable writable = false message = "" try - writePermissionTestFile = path.join(@portableAtomHomePath(), "write.test") + writePermissionTestFile = path.join(@getPortableAtomHomePath(), "write.test") fs.writeFileSync(writePermissionTestFile, "test") if not fs.existsSync(writePermissionTestFile) fs.removeSync(writePermissionTestFile) writable = true catch error message = "Failed to use portable Atom home directory. Using the default instead." - message = "Portable Atom home directory (#{@portableAtomHomePath()}) is not writable. Using the default instead." if error.code == "EPERM" + message = "Portable Atom home directory (#{@getPortableAtomHomePath()}) is not writable. Using the default instead." if error.code == "EPERM" ipc.on 'check-portable-home-writable', (event, arg) -> event.sender.send 'check-portable-home-writable-response', {writable, message} diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 59875351f..60bf87f29 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -62,9 +62,9 @@ setupAtomHome = (args) -> atomHome = path.join(app.getHomeDir(), '.atom') AtomPortable = require './atom-portable' AtomPortable.setPortable(atomHome) if not AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME) and args.setPortable - atomHome = AtomPortable.portableAtomHomePath() if AtomPortable.isPortableInstall process.platform, process.env.ATOM_HOME - atomHome = fs.realpathSync(atomHome) - fs.statSync(atomHome) + atomHome = AtomPortable.getPortableAtomHomePath() if AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME) + try + atomHome = fs.realpathSync(atomHome) process.env.ATOM_HOME = atomHome From ef71211b8255c78ae60aa7821795ac26c3f24fbe Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Thu, 15 Oct 2015 16:35:35 -0600 Subject: [PATCH 11/22] Always include portable home path in warning when warning that portable home path not writable --- src/browser/atom-portable.coffee | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index 2a6136a98..e0630c6d3 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -28,8 +28,7 @@ class AtomPortable fs.removeSync(writePermissionTestFile) writable = true catch error - message = "Failed to use portable Atom home directory. Using the default instead." - message = "Portable Atom home directory (#{@getPortableAtomHomePath()}) is not writable. Using the default instead." if error.code == "EPERM" + message = "Failed to use portable Atom home directory (#{@getPortableAtomHomePath()}). Using the default instead. #{error.message}" ipc.on 'check-portable-home-writable', (event, arg) -> event.sender.send 'check-portable-home-writable-response', {writable, message} From bcfb6ab4079a10b5bb2a5ecd3c927fee37ea2ead Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Thu, 15 Oct 2015 16:57:12 -0600 Subject: [PATCH 12/22] Remove explicit returns --- src/browser/atom-portable.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index e0630c6d3..2a381efe9 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -6,7 +6,7 @@ module.exports = class AtomPortable @getPortableAtomHomePath: -> execDirectoryPath = path.dirname(process.execPath) - return path.join(execDirectoryPath, '..', '.atom') + path.join(execDirectoryPath, '..', '.atom') @setPortable: (existingAtomHome) -> fs.copySync(existingAtomHome, @getPortableAtomHomePath()) @@ -17,7 +17,7 @@ class AtomPortable return false if not fs.existsSync(@getPortableAtomHomePath()) # currently checking only that the directory exists and is writable, # probably want to do some integrity checks on contents in future - return @portableAtomHomePathWritable() + @portableAtomHomePathWritable() @portableAtomHomePathWritable: -> writable = false @@ -32,4 +32,4 @@ class AtomPortable ipc.on 'check-portable-home-writable', (event, arg) -> event.sender.send 'check-portable-home-writable-response', {writable, message} - return writable + writable From 82f5e81cec2727013d3291907ddd04cc7ffd7a37 Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Fri, 16 Oct 2015 04:24:57 -0600 Subject: [PATCH 13/22] Restore check on ATOM_HOME (undo db57479bf92474bf9bfb832c70a2c5c947238e8d) Check on ATOM_HOME is not superfluous - needed on Linux because of difference in inheriting environment variables between browser and render processes --- static/index.js | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/static/index.js b/static/index.js index 9d09bf060..167a87d8e 100644 --- a/static/index.js +++ b/static/index.js @@ -13,6 +13,11 @@ console.error('Unhandled promise rejection %o with error: %o', promise, error) }) + // Ensure ATOM_HOME is always set before anything else is required + // This is because of a difference in Linux not inherited between browser and render processes + // issue #5142 + setupAtomHome() + // Normalize to make sure drive letter case is consistent on Windows process.resourcesPath = path.normalize(process.resourcesPath) @@ -77,6 +82,24 @@ require('ipc').sendChannel('window-command', 'window:loaded') } + function setupAtomHome () { + if (!process.env.ATOM_HOME) { + var home + if (process.platform === 'win32') { + home = process.env.USERPROFILE + } else { + home = process.env.HOME + } + var atomHome = path.join(home, '.atom') + try { + atomHome = fs.realpathSync(atomHome) + } catch (error) { + // Ignore since the path might just not exist yet. + } + process.env.ATOM_HOME = atomHome + } + } + function setupCsonCache (cacheDir) { require('season').setCacheDir(path.join(cacheDir, 'cson')) } From 03faddd7ae4c2796b803bb44865338ff88ca0ed2 Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Fri, 16 Oct 2015 07:02:53 -0600 Subject: [PATCH 14/22] Undo add command line parameter to set portable undo 4312f76ed7162b85cc10a6caa044c8881045a666 to include this in a pull request of its own --- spec/atom-portable-spec.coffee | 20 -------------------- src/browser/atom-portable.coffee | 3 --- src/browser/main.coffee | 5 +---- 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/spec/atom-portable-spec.coffee b/spec/atom-portable-spec.coffee index 50e7425c0..91399fdb4 100644 --- a/spec/atom-portable-spec.coffee +++ b/spec/atom-portable-spec.coffee @@ -4,26 +4,6 @@ temp = require "temp" rimraf = require "rimraf" AtomPortable = require "../src/browser/atom-portable" -describe "Set Portable Mode", -> - portableAtomHomePath = path.join(path.dirname(process.execPath), '..', '.atom') - portableAtomHomeNaturallyExists = fs.existsSync(portableAtomHomePath) - portableAtomHomeBackupPath = "#{portableAtomHomePath}.temp" - - beforeEach -> - fs.renameSync(portableAtomHomePath, portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomePath) - - afterEach -> - if portableAtomHomeNaturallyExists - fs.renameSync(portableAtomHomeBackupPath, portableAtomHomePath) if not fs.existsSync(portableAtomHomePath) - else - rimraf.sync(portableAtomHomePath) if fs.existsSync(portableAtomHomePath) - rimraf.sync(portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomeBackupPath) - - - it "creates portable home directory", -> - AtomPortable.setPortable(process.env.ATOM_HOME) - expect(fs.existsSync(portableAtomHomePath)).toBe true - describe "Check for Portable Mode", -> describe "Windows", -> platform = "win32" diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index 2a381efe9..76e30082c 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -8,9 +8,6 @@ class AtomPortable execDirectoryPath = path.dirname(process.execPath) path.join(execDirectoryPath, '..', '.atom') - @setPortable: (existingAtomHome) -> - fs.copySync(existingAtomHome, @getPortableAtomHomePath()) - @isPortableInstall: (platform, environmentAtomHome) -> return false unless platform is 'win32' return false if environmentAtomHome diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 60bf87f29..2e5a65aeb 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -61,7 +61,6 @@ setupAtomHome = (args) -> return if process.env.ATOM_HOME atomHome = path.join(app.getHomeDir(), '.atom') AtomPortable = require './atom-portable' - AtomPortable.setPortable(atomHome) if not AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME) and args.setPortable atomHome = AtomPortable.getPortableAtomHomePath() if AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME) try atomHome = fs.realpathSync(atomHome) @@ -108,7 +107,6 @@ parseCommandLine = -> options.alias('t', 'test').boolean('t').describe('t', 'Run the specified specs and exit with error code on failures.') options.alias('v', 'version').boolean('v').describe('v', 'Print the version.') options.alias('w', 'wait').boolean('w').describe('w', 'Wait for window to be closed before returning.') - options.alias('p', 'set-portable').boolean('p').describe('p', 'Set portable mode.') options.string('socket-path') args = options.argv @@ -134,7 +132,6 @@ parseCommandLine = -> profileStartup = args['profile-startup'] urlsToOpen = [] devResourcePath = process.env.ATOM_DEV_RESOURCE_PATH ? path.join(app.getHomeDir(), 'github', 'atom') - setPortable = args['set-portable'] if args['resource-path'] devMode = true @@ -164,6 +161,6 @@ parseCommandLine = -> {resourcePath, devResourcePath, pathsToOpen, urlsToOpen, executedFrom, test, version, pidToKillWhenClosed, devMode, safeMode, newWindow, specDirectory, - logFile, socketPath, profileStartup, setPortable} + logFile, socketPath, profileStartup} start() From 6d4105ac97f2e6dcb0ce34d1281a1f23050d2f2e Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Tue, 20 Oct 2015 07:09:58 -0600 Subject: [PATCH 15/22] Escape characters is portable path not writable warning to resolve missing backslash and other potential problems resulting from the markdown-parsed nature of the display of notifications. --- src/atom.coffee | 4 +++- src/browser/atom-portable.coffee | 8 ++++---- src/browser/main.coffee | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/atom.coffee b/src/atom.coffee index 3a3c5c6aa..e7feaca6c 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -247,7 +247,9 @@ class Atom extends Model responseChannel = "check-portable-home-writable-response" ipc.on responseChannel, (response) -> ipc.removeAllListeners(responseChannel) - atom.notifications.addWarning(response.message) if not response.writable + escapeMarkdown = (inputMessage) -> + inputMessage.split('\\').join('\\\\').split('.').join('\\.').split('-').join('\\-').split('+').join('\\+').split('_').join('\\_').split('#').join('\\#').split('!').join('\\!') + atom.notifications.addWarning("#{escapeMarkdown response.message}") if not response.writable ipc.send('check-portable-home-writable', responseChannel) checkPortableHomeWritable() diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index 76e30082c..d121d8c13 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -8,15 +8,15 @@ class AtomPortable execDirectoryPath = path.dirname(process.execPath) path.join(execDirectoryPath, '..', '.atom') - @isPortableInstall: (platform, environmentAtomHome) -> + @isPortableInstall: (platform, environmentAtomHome, defaultHome) -> return false unless platform is 'win32' return false if environmentAtomHome return false if not fs.existsSync(@getPortableAtomHomePath()) # currently checking only that the directory exists and is writable, # probably want to do some integrity checks on contents in future - @portableAtomHomePathWritable() + @portableAtomHomePathWritable(defaultHome) - @portableAtomHomePathWritable: -> + @portableAtomHomePathWritable: (defaultHome) -> writable = false message = "" try @@ -25,7 +25,7 @@ class AtomPortable fs.removeSync(writePermissionTestFile) writable = true catch error - message = "Failed to use portable Atom home directory (#{@getPortableAtomHomePath()}). Using the default instead. #{error.message}" + message = "Failed to use portable Atom home directory (#{@getPortableAtomHomePath()}). Using the default instead (#{defaultHome}). #{error.message}" ipc.on 'check-portable-home-writable', (event, arg) -> event.sender.send 'check-portable-home-writable-response', {writable, message} diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 2e5a65aeb..8951ccb4d 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -61,7 +61,7 @@ setupAtomHome = (args) -> return if process.env.ATOM_HOME atomHome = path.join(app.getHomeDir(), '.atom') AtomPortable = require './atom-portable' - atomHome = AtomPortable.getPortableAtomHomePath() if AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME) + atomHome = AtomPortable.getPortableAtomHomePath() if AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME, atomHome) try atomHome = fs.realpathSync(atomHome) From 86528909588a925d8deb3cb6da7a872504a5664b Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Tue, 20 Oct 2015 09:45:01 -0600 Subject: [PATCH 16/22] Simplify markdown escaping for warning by using a regular expression instead of multiple replaces --- src/atom.coffee | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/atom.coffee b/src/atom.coffee index e7feaca6c..17d1e21f3 100644 --- a/src/atom.coffee +++ b/src/atom.coffee @@ -247,9 +247,7 @@ class Atom extends Model responseChannel = "check-portable-home-writable-response" ipc.on responseChannel, (response) -> ipc.removeAllListeners(responseChannel) - escapeMarkdown = (inputMessage) -> - inputMessage.split('\\').join('\\\\').split('.').join('\\.').split('-').join('\\-').split('+').join('\\+').split('_').join('\\_').split('#').join('\\#').split('!').join('\\!') - atom.notifications.addWarning("#{escapeMarkdown response.message}") if not response.writable + atom.notifications.addWarning("#{response.message.replace(/([\\\.+\\-_#!])/g, '\\$1')}") if not response.writable ipc.send('check-portable-home-writable', responseChannel) checkPortableHomeWritable() From e93b013059821712f89d18da2a668228dfbf53ff Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Wed, 21 Oct 2015 04:40:05 -0600 Subject: [PATCH 17/22] Remove unused function parameter for setupAtomHome function no longer needing args --- src/browser/main.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index cecf737ad..40ec9a930 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -14,7 +14,7 @@ console.log = require 'nslog' start = -> args = parseCommandLine() - setupAtomHome(args) + setupAtomHome setupCompileCache() return if handleStartupEventWithSquirrel() @@ -57,7 +57,7 @@ handleStartupEventWithSquirrel = -> setupCrashReporter = -> crashReporter.start(productName: 'Atom', companyName: 'GitHub') -setupAtomHome = (args) -> +setupAtomHome = -> return if process.env.ATOM_HOME atomHome = path.join(app.getHomeDir(), '.atom') AtomPortable = require './atom-portable' From 810f0e45836b9b0de5dcb5e2a380a862a6ed4b3d Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Wed, 21 Oct 2015 09:47:33 -0600 Subject: [PATCH 18/22] Fix merge problem and missing function call Merge resulted in a missing require and change to remove unused function parameters resulted in not calling function --- src/atom-environment.coffee | 1 + src/browser/main.coffee | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/atom-environment.coffee b/src/atom-environment.coffee index 8c418fbd1..964e9b4a6 100644 --- a/src/atom-environment.coffee +++ b/src/atom-environment.coffee @@ -1,5 +1,6 @@ crypto = require 'crypto' path = require 'path' +ipc = require 'ipc' _ = require 'underscore-plus' {deprecate} = require 'grim' diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 40ec9a930..4a3eec52d 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -14,7 +14,7 @@ console.log = require 'nslog' start = -> args = parseCommandLine() - setupAtomHome + setupAtomHome() setupCompileCache() return if handleStartupEventWithSquirrel() From 35345893fcad7a628bf5d17d68109ef25aaa0f8a Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Thu, 22 Oct 2015 08:49:08 -0600 Subject: [PATCH 19/22] Fix function name to be consistent with convention --- src/browser/atom-portable.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index d121d8c13..4037c95bb 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -14,9 +14,9 @@ class AtomPortable return false if not fs.existsSync(@getPortableAtomHomePath()) # currently checking only that the directory exists and is writable, # probably want to do some integrity checks on contents in future - @portableAtomHomePathWritable(defaultHome) + @isPortableAtomHomePathWritable(defaultHome) - @portableAtomHomePathWritable: (defaultHome) -> + @isPortableAtomHomePathWritable: (defaultHome) -> writable = false message = "" try From 8acb2af055d618d80b15facecc4dcfe5a3639a21 Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Thu, 22 Oct 2015 08:58:39 -0600 Subject: [PATCH 20/22] Remove unused parameter to keep the code clean and readable --- src/browser/atom-portable.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/browser/atom-portable.coffee b/src/browser/atom-portable.coffee index 4037c95bb..a8cbd2d41 100644 --- a/src/browser/atom-portable.coffee +++ b/src/browser/atom-portable.coffee @@ -27,6 +27,6 @@ class AtomPortable catch error message = "Failed to use portable Atom home directory (#{@getPortableAtomHomePath()}). Using the default instead (#{defaultHome}). #{error.message}" - ipc.on 'check-portable-home-writable', (event, arg) -> + ipc.on 'check-portable-home-writable', (event) -> event.sender.send 'check-portable-home-writable-response', {writable, message} writable From b1d10ef4dcc953df6afa0dc1895d594d8f46f313 Mon Sep 17 00:00:00 2001 From: Dave Rael Date: Thu, 22 Oct 2015 09:01:56 -0600 Subject: [PATCH 21/22] Revert moving parse of command line to the first line of start function because the move is not necessary without having the code for using a command line parameter to set Atom to portable mode. This belongs on that branch and in that pull request. --- src/browser/main.coffee | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 4a3eec52d..3f768d39b 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -12,14 +12,14 @@ yargs = require 'yargs' console.log = require 'nslog' start = -> - args = parseCommandLine() - setupAtomHome() setupCompileCache() return if handleStartupEventWithSquirrel() # NB: This prevents Win10 from showing dupe items in the taskbar app.setAppUserModelId('com.squirrel.atom.atom') + + args = parseCommandLine() addPathToOpen = (event, pathToOpen) -> event.preventDefault() From 02fe2cf61850f10729c49cf59c6a3dfaebb59a77 Mon Sep 17 00:00:00 2001 From: Kevin Sawicki Date: Tue, 27 Oct 2015 11:44:02 -0600 Subject: [PATCH 22/22] :art: --- spec/atom-portable-spec.coffee | 21 +++++++++------------ src/browser/main.coffee | 3 +-- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/spec/atom-portable-spec.coffee b/spec/atom-portable-spec.coffee index 91399fdb4..4665621ff 100644 --- a/spec/atom-portable-spec.coffee +++ b/spec/atom-portable-spec.coffee @@ -6,18 +6,15 @@ AtomPortable = require "../src/browser/atom-portable" describe "Check for Portable Mode", -> describe "Windows", -> - platform = "win32" - describe "with ATOM_HOME environment variable", -> - environmentAtomHome = "C:\\some\\path" it "returns false", -> - expect(AtomPortable.isPortableInstall(platform, environmentAtomHome)).toBe false + expect(AtomPortable.isPortableInstall("win32", "C:\\some\\path")).toBe false describe "without ATOM_HOME environment variable", -> environmentAtomHome = undefined - portableAtomHomePath = path.join(path.dirname(process.execPath), "../.atom").toString() + portableAtomHomePath = path.join(path.dirname(process.execPath), "..", ".atom") portableAtomHomeNaturallyExists = fs.existsSync(portableAtomHomePath) - portableAtomHomeBackupPath = portableAtomHomePath + ".temp" + portableAtomHomeBackupPath = "#{portableAtomHomePath}.temp" beforeEach -> fs.renameSync(portableAtomHomePath, portableAtomHomeBackupPath) if fs.existsSync(portableAtomHomePath) @@ -32,21 +29,21 @@ describe "Check for Portable Mode", -> describe "with .atom directory sibling to exec", -> beforeEach -> fs.mkdirSync(portableAtomHomePath) if not fs.existsSync(portableAtomHomePath) + it "returns true", -> - expect(AtomPortable.isPortableInstall(platform, environmentAtomHome)).toBe true + expect(AtomPortable.isPortableInstall("win32", environmentAtomHome)).toBe true describe "without .atom directory sibling to exec", -> beforeEach -> rimraf.sync(portableAtomHomePath) if fs.existsSync(portableAtomHomePath) + it "returns false", -> - expect(AtomPortable.isPortableInstall(platform, environmentAtomHome)).toBe false + expect(AtomPortable.isPortableInstall("win32", environmentAtomHome)).toBe false describe "Mac", -> - platform = "darwin" it "returns false", -> - expect(AtomPortable.isPortableInstall(platform, platform)).toBe false + expect(AtomPortable.isPortableInstall("darwin", "darwin")).toBe false describe "Linux", -> - platform = "linux" it "returns false", -> - expect(AtomPortable.isPortableInstall(platform, platform)).toBe false + expect(AtomPortable.isPortableInstall("linux", "linux")).toBe false diff --git a/src/browser/main.coffee b/src/browser/main.coffee index 3f768d39b..48485486b 100644 --- a/src/browser/main.coffee +++ b/src/browser/main.coffee @@ -18,7 +18,7 @@ start = -> # NB: This prevents Win10 from showing dupe items in the taskbar app.setAppUserModelId('com.squirrel.atom.atom') - + args = parseCommandLine() addPathToOpen = (event, pathToOpen) -> @@ -64,7 +64,6 @@ setupAtomHome = -> atomHome = AtomPortable.getPortableAtomHomePath() if AtomPortable.isPortableInstall(process.platform, process.env.ATOM_HOME, atomHome) try atomHome = fs.realpathSync(atomHome) - process.env.ATOM_HOME = atomHome setupCompileCache = ->