From 269bfaf4c43cdedb57752de4bd1a31bbe12ba91a Mon Sep 17 00:00:00 2001 From: Dan Cuellar Date: Sun, 1 Dec 2013 09:03:48 -0800 Subject: [PATCH 1/3] Adding doctor check for node binary path --- lib/doctor/ios.js | 155 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 2 deletions(-) diff --git a/lib/doctor/ios.js b/lib/doctor/ios.js index 2903765e3..51fd55590 100644 --- a/lib/doctor/ios.js +++ b/lib/doctor/ios.js @@ -18,7 +18,8 @@ IOSChecker.prototype.runAllChecks = function(cb) { this.checkForXcode.bind(this), this.checkForXcodeCommandLineTools.bind(this), this.checkDevToolsSecurity.bind(this), - this.checkAuthorizationDB.bind(this) + this.checkAuthorizationDB.bind(this), + this.checkForNodeBinary.bind(this) ], cb); }; @@ -144,6 +145,129 @@ IOSChecker.prototype.checkAuthorizationDB = function(cb) { }.bind(this)); }; +IOSChecker.prototype.checkForNodeBinary = function(cb) { + var msg; + + this.checkForNodeBinaryInCommonPlaces(function(err, msg) { + if (!err) { + cb(null, msg); + } else { + this.checkForNodeBinaryUsingWhichCommand(function(err, msg) { + if (!err) { + cb(null, msg); + } else { + this.checkForNodeBinaryUsingAppleScript(function(err, msg) { + if (!err) { + cb(null, msg); + } else { + this.checkForNodeBinaryUsingAppiumConfigFile(function(err, msg) { + if (!err) { + cb(null, msg); + } else { + msg = 'The node binary could not be found.'; + this.log.fail(msg); + this.log.promptToFix("The node binary could not be found.", function() { + this.setupNodeBinaryPath(cb); + }.bind(this), function() { + cb(msg, msg); + }); + } + }.bind(this)); + } + }.bind(this)); + } + }.bind(this)); + } + }.bind(this)); +}; + +IOSChecker.prototype.checkForNodeBinaryInCommonPlaces = function(cb) { + var msg; + if (env.NODE_BIN !== null && fs.existsSync(env.NODE_BIN)) { + msg = "Node binary found using NODE_BIN environment variable at " + env.NODE_BIN; + this.log.pass(msg); + cb(null, msg); + } else if (fs.existsSync('/usr/local/bin/node')) { + msg = "Node binary found at /usr/local/bin/node"; + this.log.pass(msg); + cb(null, msg); + } else if (fs.existsSync('/opt/local/bin/node')) { + msg = "Node binary found at /opt/local/bin/node"; + this.log.pass(msg); + cb(null, msg); + } else { + msg = 'Node binary could not be found in the usual places'; + cb(msg, msg); + } +}; + +IOSChecker.prototype.checkForNodeBinaryUsingWhichCommand = function(cb) { + var msg; + exec("which node", { maxBuffer: 524288}, function(err, stdout) { + if (err === null && fs.existsSync(stdout.replace("\n",""))) { + msg = "Node binary found using which command at " + stdout.replace("\n",""); + this.log.pass(msg); + cb(null, msg); + } else { + msg = 'Node binary not found using the which command.'; + cb(msg, msg); + } + }.bind(this)); +}; + +IOSChecker.prototype.checkForNodeBinaryUsingAppleScript = function(cb) { + var msg; + var appScript = [ + 'try' + , ' set appiumIsRunning to false' + , ' tell application "System Events"' + , ' set appiumIsRunning to name of every process contains "Appium"' + , ' end tell' + , ' if appiumIsRunning then' + , ' tell application "Appium" to return node path' + , ' end if' + , 'end try' + , 'return "NULL"' + ].join("\n"); + exec("osascript -e '" + appScript + "'", { maxBuffer: 524288}, function(err, stdout) { + if (err === null && fs.existsSync(stdout.replace("\n",""))) { + msg = "Node binary found using AppleScript at " + stdout.replace("\n",""); + this.log.pass(msg); + cb(null, msg); + } else { + msg = 'Node binary not found using AppleScript.'; + cb(msg, msg); + } + }.bind(this)); +}; + +IOSChecker.prototype.checkForNodeBinaryUsingAppiumConfigFile = function(cb) { + var msg = 'Node binary not found in the .appiumconfig file.'; + var appiumConfigPath = path.resolve(__dirname, "../..", ".appiumconfig"); + if (fs.existsSync(appiumConfigPath)) { + fs.readFile(appiumConfigPath, 'utf8', function(err, data) { + if (err === null) { + try { + var jsonobj = JSON.parse(data); + if (jsonobj.node_bin !== undefined && fs.existsSync(jsonobj.node_bin)) { + msg = "Node binary found using .appiumconfig at " + jsonobj.node_bin; + this.log.pass(msg); + cb(null, msg); + } else { + cb(msg, msg); + } + } catch (jsonErr) { + cb(msg, msg); + } + } else { + cb(msg, msg); + } + }.bind(this)); + } else { + cb(msg, msg); + } +}; + IOSChecker.prototype.installXcode = function(cb) { exec("xcode-select --install", { maxBuffer: 524288}, function() { this.log.promptToConfirmFix(function() { @@ -162,7 +286,7 @@ IOSChecker.prototype.installXcodeCommandLineTools = function(cb) { IOSChecker.prototype.authorizeIOS = function(outerCb, innerCb) { var authorizePath = path.resolve(__dirname, "../../bin", "authorize-ios.js"); - exec("'" + process.execPath + "' '" + authorizePath + "'" , { maxBuffer: 524288}, function(err,stdout) { + exec("'" + process.execPath + "' '" + authorizePath + "'" , { maxBuffer: 524288}, function(err) { if(err) { this.log.error('Could not authorize iOS: ' + err); } @@ -172,3 +296,30 @@ IOSChecker.prototype.authorizeIOS = function(outerCb, innerCb) { }.bind(this)); }.bind(this)); }; + +IOSChecker.prototype.setupNodeBinaryPath = function(cb) { + var msg; + var appiumConfigPath = path.resolve(__dirname, "../../", ".appiumconfig"); + if (fs.existsSync(appiumConfigPath)) { + fs.readFile(appiumConfigPath, 'utf8', function(err, data) { + if (!err) { + try { + var jsonobj = JSON.parse(data); + jsonobj.node_bin = process.execPath; + fs.writeFile(appiumConfigPath, JSON.stringify(jsonobj), function() { + this.checkForNodeBinary(cb); + }.bind(this)); + } catch (jsonErr) { + this.log.error("Could not setup node binary path in .appiumconfig. Error parsing JSON: " + jsonErr ); + this.checkForNodeBinary(cb); + } + } else { + this.log.error("Could not setup node binary path in .appiumconfig. Error reading config: " + err ); + this.checkForNodeBinary(cb); + } + }.bind(this)); + } else { + this.log.error('The .appiumconfig file was not found at ' + appiumConfigPath); + this.exitDoctor(); + } +}; From 4147158291692bc31c5f1f0be73f047f5092cff8 Mon Sep 17 00:00:00 2001 From: Dan Cuellar Date: Sun, 1 Dec 2013 12:29:23 -0800 Subject: [PATCH 2/3] Adding more android checks, adding optional cb to log.pass and log.fail --- lib/doctor/android.js | 63 ++++++++++++++++++++++++++----------------- lib/doctor/common.js | 10 +++++-- lib/doctor/ios.js | 55 +++++++++---------------------------- 3 files changed, 60 insertions(+), 68 deletions(-) diff --git a/lib/doctor/android.js b/lib/doctor/android.js index 8d6e6ed28..4a028be2d 100644 --- a/lib/doctor/android.js +++ b/lib/doctor/android.js @@ -4,6 +4,7 @@ var path = require('path') , env = process.env , exec = require('child_process').exec , common = require("./common.js") + , isWindows = require("../helpers.js").isWindows() , async = require('async'); function AndroidChecker(log) { @@ -13,41 +14,55 @@ exports.AndroidChecker = AndroidChecker; AndroidChecker.prototype.runAllChecks = function(cb) { async.series([ - this.IsAndroidHomeExported.bind(this), - this.IsJavaHomeExported.bind(this) + this.checkAndroidHomeExported.bind(this), + this.checkJavaHomeExported.bind(this), + this.checkADBExists.bind(this), + this.checkAndroidExists.bind(this), + this.checkEmulatorExists.bind(this) ], cb); }; -AndroidChecker.prototype.IsAndroidHomeExported = function(cb) { - var msg; +AndroidChecker.prototype.checkAndroidHomeExported = function(cb) { if (env.ANDROID_HOME === null) { - msg = 'ANDROID_HOME is not set'; - this.log.fail(msg); - cb(msg, msg); + this.log.fail('ANDROID_HOME is not set', cb); } else if (fs.existsSync(env.ANDROID_HOME)) { - msg = 'ANDROID_HOME is set to "' + env.ANDROID_HOME + '"'; - this.log.pass(msg); - cb(null, msg); + this.log.pass('ANDROID_HOME is set to "' + env.ANDROID_HOME + '"', cb); } else { - msg = 'ANDROID_HOME is set but does not exist on the file system at "' + env.ANDROID_HOME + '"'; - this.log.fail(msg); - cb(msg, msg); + this.log.fail('ANDROID_HOME is set but does not exist on the file system at "' + env.ANDROID_HOME + '"', cb); } }; -AndroidChecker.prototype.IsJavaHomeExported = function(cb) { - var msg; +AndroidChecker.prototype.checkJavaHomeExported = function(cb) { if (env.JAVA_HOME === null) { - this.log.fail(msg); - msg = 'JAVA_HOME is not set'; - cb(msg, msg); + this.log.fail('JAVA_HOME is not set', cb); } else if (fs.existsSync(env.JAVA_HOME)) { - msg = 'JAVA_HOME is set to "' + env.JAVA_HOME + '."'; - this.log.pass(msg); - cb(null, msg); + this.log.pass('JAVA_HOME is set to "' + env.JAVA_HOME + '."', cb); } else { - msg = 'JAVA_HOME is set but does not exist on the file system at "' + env.JAVA_HOME + '"'; - this.log.fail(msg); - cb(msg, msg); + this.log.fail('JAVA_HOME is set but does not exist on the file system at "' + env.JAVA_HOME + '"', cb); + } +}; + +AndroidChecker.prototype.checkADBExists = function(cb) { + this.checkAndroidSDKBinaryExists("ADB", path.join("platform-tools", (isWindows ? 'adb.exe' : 'adb')), cb); +}; + +AndroidChecker.prototype.checkAndroidExists = function(cb) { + this.checkAndroidSDKBinaryExists("Android", path.join("tools", (isWindows ? 'android.bat' : 'android')), cb); +}; + +AndroidChecker.prototype.checkEmulatorExists = function(cb) { + this.checkAndroidSDKBinaryExists("Emulator", path.join("tools", (isWindows ? 'emulator.exe' : 'emulator')), cb); +}; + +AndroidChecker.prototype.checkAndroidSDKBinaryExists = function(toolName, relativeToolPath, cb) { + if (env.ANDROID_HOME !== null) { + var adbPath = path.resolve(env.ANDROID_HOME, relativeToolPath); + if (fs.existsSync(adbPath)) { + this.log.pass(toolName + " exists at " + adbPath, cb); + } else { + this.log.fail(toolName + " could not be found at " + adbPath, cb); + } + } else { + this.log.fail(toolName + " could not be found because ANDROID_HOME is not set.", cb); } }; \ No newline at end of file diff --git a/lib/doctor/common.js b/lib/doctor/common.js index 19f68d910..a21a78629 100644 --- a/lib/doctor/common.js +++ b/lib/doctor/common.js @@ -15,12 +15,18 @@ function Log(port) { } exports.Log = Log; -Log.prototype.pass = function(msg) { +Log.prototype.pass = function(msg, cb) { this.logEntry('\u2714 '.green + msg.white); + if (cb) { + cb(null, msg); + } }; -Log.prototype.fail = function(msg) { +Log.prototype.fail = function(msg, cb) { this.logEntry('\u2716 '.red + msg.white); + if (cb) { + cb(msg, msg); + } }; Log.prototype.warning = function(msg) { diff --git a/lib/doctor/ios.js b/lib/doctor/ios.js index 51fd55590..bf5b819bb 100644 --- a/lib/doctor/ios.js +++ b/lib/doctor/ios.js @@ -24,7 +24,6 @@ IOSChecker.prototype.runAllChecks = function(cb) { }; IOSChecker.prototype.getMacOSXVersion = function(cb) { - var msg; exec("sw_vers -productVersion", function(err, stdout) { if (err === null) { if (stdout.match('10.8') !== null) { @@ -34,14 +33,10 @@ IOSChecker.prototype.getMacOSXVersion = function(cb) { this.osVersion = '10.9'; cb(null, "Mac OS X 10.9 is installed."); } else { - msg = "Could not detect Mac OS X Version"; - this.log.fail(msg); - cb(msg, msg); + this.log.fail("Could not detect Mac OS X Version", cb); } } else { - msg = "Unknown SW Version Command: " + err; - this.log.fail(msg); - cb(msg, msg); + this.log.fail("Unknown SW Version Command: " + err, cb); } }.bind(this)); }; @@ -52,9 +47,7 @@ IOSChecker.prototype.checkForXcode = function(cb) { if (err === null) { var xcodePath = stdout.replace("\n",""); if(fs.existsSync(xcodePath)) { - msg = "Xcode is installed at " + xcodePath; - this.log.pass(msg); - cb(null, msg); + this.log.pass("Xcode is installed at " + xcodePath, cb); } else { msg = "Xcode is not installed."; this.log.fail(msg); @@ -83,9 +76,7 @@ IOSChecker.prototype.checkForXcodeCommandLineTools = function(cb) { if (err === null) { var match = stdout.match(/install-time/); if (match !== null) { - msg = "Xcode Command Line Tools are installed."; - this.log.pass(msg); - cb(null, msg); + this.log.pass("Xcode Command Line Tools are installed.", cb); } else { msg = "Xcode Command Line Tools are NOT installed."; this.log.fail(msg); @@ -111,9 +102,7 @@ IOSChecker.prototype.checkDevToolsSecurity = function(cb) { var msg; exec("DevToolsSecurity", { maxBuffer: 524288}, function(err, stdout) { if (err === null && stdout.match(/enabled/) !== null) { - msg = "DevToolsSecurity is enabled."; - this.log.pass(msg); - cb(null, msg); + this.log.pass("DevToolsSecurity is enabled.", cb); } else { msg = 'DevToolsSecurity is not enabled.'; this.log.fail(msg); @@ -130,9 +119,7 @@ IOSChecker.prototype.checkAuthorizationDB = function(cb) { var msg; exec("security authorizationdb read system.privilege.taskport", { maxBuffer: 524288}, function(err, stdout) { if (err === null && stdout.match(/is-developer/) !== null) { - msg = "The Authorization DB is set up properly."; - this.log.pass(msg); - cb(null, msg); + this.log.pass("The Authorization DB is set up properly.", cb); } else { msg = 'The Authorization DB is NOT set up properly.'; this.log.fail(msg); @@ -146,8 +133,6 @@ IOSChecker.prototype.checkAuthorizationDB = function(cb) { }; IOSChecker.prototype.checkForNodeBinary = function(cb) { - var msg; - this.checkForNodeBinaryInCommonPlaces(function(err, msg) { if (!err) { cb(null, msg); @@ -182,21 +167,14 @@ IOSChecker.prototype.checkForNodeBinary = function(cb) { }; IOSChecker.prototype.checkForNodeBinaryInCommonPlaces = function(cb) { - var msg; if (env.NODE_BIN !== null && fs.existsSync(env.NODE_BIN)) { - msg = "Node binary found using NODE_BIN environment variable at " + env.NODE_BIN; - this.log.pass(msg); - cb(null, msg); + this.log.pass("Node binary found using NODE_BIN environment variable at " + env.NODE_BIN, cb); } else if (fs.existsSync('/usr/local/bin/node')) { - msg = "Node binary found at /usr/local/bin/node"; - this.log.pass(msg); - cb(null, msg); + this.log.pass("Node binary found at /usr/local/bin/node", cb); } else if (fs.existsSync('/opt/local/bin/node')) { - msg = "Node binary found at /opt/local/bin/node"; - this.log.pass(msg); - cb(null, msg); + this.log.pass("Node binary found at /opt/local/bin/node", cb); } else { - msg = 'Node binary could not be found in the usual places'; + var msg = 'Node binary could not be found in the usual places'; cb(msg, msg); } }; @@ -205,9 +183,7 @@ IOSChecker.prototype.checkForNodeBinaryUsingWhichCommand = function(cb) { var msg; exec("which node", { maxBuffer: 524288}, function(err, stdout) { if (err === null && fs.existsSync(stdout.replace("\n",""))) { - msg = "Node binary found using which command at " + stdout.replace("\n",""); - this.log.pass(msg); - cb(null, msg); + this.log.pass("Node binary found using which command at " + stdout.replace("\n",""), cb); } else { msg = 'Node binary not found using the which command.'; cb(msg, msg); @@ -231,9 +207,7 @@ IOSChecker.prototype.checkForNodeBinaryUsingAppleScript = function(cb) { ].join("\n"); exec("osascript -e '" + appScript + "'", { maxBuffer: 524288}, function(err, stdout) { if (err === null && fs.existsSync(stdout.replace("\n",""))) { - msg = "Node binary found using AppleScript at " + stdout.replace("\n",""); - this.log.pass(msg); - cb(null, msg); + this.log.pass("Node binary found using AppleScript at " + stdout.replace("\n",""), cb); } else { msg = 'Node binary not found using AppleScript.'; cb(msg, msg); @@ -250,9 +224,7 @@ IOSChecker.prototype.checkForNodeBinaryUsingAppiumConfigFile = function(cb) { try { var jsonobj = JSON.parse(data); if (jsonobj.node_bin !== undefined && fs.existsSync(jsonobj.node_bin)) { - msg = "Node binary found using .appiumconfig at " + jsonobj.node_bin; - this.log.pass(msg); - cb(null, msg); + this.log.pass("Node binary found using .appiumconfig at " + jsonobj.node_bin, cb); } else { cb(msg, msg); } @@ -298,7 +270,6 @@ IOSChecker.prototype.authorizeIOS = function(outerCb, innerCb) { }; IOSChecker.prototype.setupNodeBinaryPath = function(cb) { - var msg; var appiumConfigPath = path.resolve(__dirname, "../../", ".appiumconfig"); if (fs.existsSync(appiumConfigPath)) { fs.readFile(appiumConfigPath, 'utf8', function(err, data) { From 75d31929c6df04d481989f0ac4cb24ebc9985995 Mon Sep 17 00:00:00 2001 From: Dan Cuellar Date: Sun, 1 Dec 2013 12:48:13 -0800 Subject: [PATCH 3/3] Adding Dev Checks --- bin/appium-doctor.js | 28 +++++++++++++++++++++++----- lib/doctor/dev.js | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 5 deletions(-) create mode 100644 lib/doctor/dev.js diff --git a/bin/appium-doctor.js b/bin/appium-doctor.js index 1249c4ad1..f6f940464 100755 --- a/bin/appium-doctor.js +++ b/bin/appium-doctor.js @@ -1,7 +1,8 @@ #!/usr/bin/env node "use strict"; -var ios = require('../lib/doctor/ios.js') - , android = require('../lib/doctor/android.js') +var IOSChecker = require('../lib/doctor/ios.js').IOSChecker + , AndroidChecker = require('../lib/doctor/android.js').AndroidChecker + , DevChecker = require('../lib/doctor/dev.js').DevChecker , common = require("../lib/doctor/common.js") , eol = require('os').EOL , async = require('async'); @@ -9,6 +10,7 @@ var ios = require('../lib/doctor/ios.js') var argv = process.argv , doAndroid = argv.indexOf('--android') > -1 , doIOS = argv.indexOf('--ios') > -1 + , doDev = argv.indexOf('--dev') > -1 , verbose = argv.indexOf('--verbose') > -1 , broadcast = argv.indexOf('--port') > -1 , port = null; @@ -26,7 +28,7 @@ var log = new common.Log(port); var runiOSChecks = function(cb) { if (doIOS) { - var iosChecker = new ios.IOSChecker(log); + var iosChecker = new IOSChecker(log); log.comment("Running iOS Checks"); iosChecker.runAllChecks(function(err) { if (!err) { @@ -41,7 +43,7 @@ var runiOSChecks = function(cb) { var runAndroidChecks = function(cb) { if (doAndroid) { - var androidChecker = new android.AndroidChecker(log); + var androidChecker = new AndroidChecker(log); log.comment("Running Android Checks"); androidChecker.runAllChecks(function(err) { if (!err) { @@ -54,12 +56,28 @@ var runAndroidChecks = function(cb) { } }; +var runDevChecks = function(cb) { + if (doDev) { + var devChecker = new DevChecker(log); + log.comment("Running Dev Checks"); + devChecker.runAllChecks(function(err) { + if (!err) { + log.pass("Dev Checks were successful." + eol); + cb(); + } else { + log.exitDoctor(); + } + }); + } +}; + if (require.main === module) { var mainMethod = function() { async.series([ runiOSChecks, - runAndroidChecks + runAndroidChecks, + runDevChecks ], function(err) { if (!err) { log.pass("All Checks were successful"); diff --git a/lib/doctor/dev.js b/lib/doctor/dev.js new file mode 100644 index 000000000..56a778873 --- /dev/null +++ b/lib/doctor/dev.js @@ -0,0 +1,35 @@ +"use strict"; +var path = require('path') + , fs = require('fs') + , env = process.env + , exec = require('child_process').exec + , common = require("./common.js") + , isWindows = require("../helpers.js").isWindows() + , eol = require('os').EOL + , async = require('async'); + +function DevChecker(log) { + this.log = log; +} +exports.DevChecker = DevChecker; + +DevChecker.prototype.runAllChecks = function(cb) { + async.series([ + this.checkMavenExistsInPath.bind(this) + ], cb); +}; + +DevChecker.prototype.checkMavenExistsInPath = function(cb) { + exec(isWindows ? "where mvn.bat" : "which mvn", { maxBuffer: 524288 }, function(err, stdout) { + if (!err) { + var mvnPath = isWindows ? stdout.split(eol)[0] : stdout.replace(eol, ""); + if (fs.existsSync(mvnPath)) { + this.log.pass("Maven was found at " + mvnPath, cb); + } else { + this.log.fail("Maven does not exist at path " + mvnPath, cb); + } + } else { + this.log.fail("Could not find mvn in path.", cb); + } + }.bind(this)); +};