diff --git a/README.md b/README.md index 10904e15c..321d31f46 100644 --- a/README.md +++ b/README.md @@ -38,6 +38,11 @@ want to run tests on. See below for particular platform requirements. If you want to run Appium via an `npm install`, hack with or contribute to Appium, you will need [node.js and npm](http://nodejs.org) 0.8 or greater (`brew install node`). +To verify that all of Appium's dependencies are met you can use `appium-doctor`. +Run `appium-doctor` and supply the `--ios` or `--android` flags to verify that all +of the dependencies are set up correctly. If running from source, you mayve have to use +`bin/appium-doctor.js` or `node bin/appium-doctor.js`. + ### iOS Requirements * Mac OS X 10.7 or higher, 10.8.4 recommended diff --git a/bin/appium-doctor.js b/bin/appium-doctor.js new file mode 100755 index 000000000..169e24a45 --- /dev/null +++ b/bin/appium-doctor.js @@ -0,0 +1,59 @@ +#!/usr/bin/env node +"use strict"; +var ios = require('../lib/doctor/ios.js') + , android = require('../lib/doctor/android.js') + , common = require("../lib/doctor/common.js") + , log = require('../lib/doctor/common.js').log + , eol = require('os').EOL + , async = require('async'); + +var argv = process.argv + , doAndroid = argv.indexOf('--android') > -1 + , doIOS = argv.indexOf('--ios') > -1 + , verbose = argv.indexOf('--verbose') > -1; + +if (!doIOS && !doAndroid) { + doIOS = true; + doAndroid = true; +} + +var runiOSChecks = function(cb) { + if (doIOS) { + log.comment("Running iOS Checks"); + ios.runAllChecks(function(err) { + if (!err) { + log.pass("iOS Checks were successful." + eol); + cb(); + } else { + common.exitDoctor(); + } + }); + } +}; + +var runAndroidChecks = function(cb) { + if (doAndroid) { + log.comment("Running Android Checks"); + android.runAllChecks(function(err) { + if (!err) { + log.pass("Android Checks were successful." + eol); + cb(); + } else { + common.exitDoctor(); + } + }); + } +}; + +if (require.main === module) { + async.series([ + runiOSChecks, + runAndroidChecks + ], function(err) { + if (!err) { + log.pass("All Checks were successful"); + } else { + common.exitDoctor(); + } + }); +} diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index 6e3e897d5..dcb4496ba 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -24,6 +24,8 @@ to github or write to the appium-discuss mailing list. ./reset.sh --android # android-only ./reset.sh --selendroid # selendroid-only * You might also want to run `reset.sh` with the `--dev` flag if you want the test apps downloaded and built as well. +* You can also use `appium-doctor` to automatically verify that all dependencies are met. If running from source, you +may have to use `bin/appium-doctor.js` or `node bin/appium-doctor.js`. * If you get this error after upgrading to Android SDK 22: `{ANDROID_HOME}/tools/ant/uibuild.xml:155: SDK does not have any Build Tools installed.` In the Android SDK 22, the platform and build tools are split up into their own items in the SDK manager. Make sure you install the build-tools and platform-tools. diff --git a/lib/doctor/android.js b/lib/doctor/android.js new file mode 100644 index 000000000..44cdb00c8 --- /dev/null +++ b/lib/doctor/android.js @@ -0,0 +1,49 @@ +"use strict"; +var path = require('path') + , fs = require('fs') + , env = process.env + , exec = require('child_process').exec + , common = require("./common.js") + , log = require('./common.js').log + , async = require('async'); + +exports.runAllChecks = function(cb) { + async.series([ + exports.IsAndroidHomeExported, + exports.IsJavaHomeExported + ], cb); +}; + +exports.IsAndroidHomeExported = function(cb) { + var msg; + if (env.ANDROID_HOME === null) { + msg = 'ANDROID_HOME is not set'; + log.fail(msg); + cb(msg, msg); + } else if (fs.existsSync(env.ANDROID_HOME)) { + msg = 'ANDROID_HOME is set to "' + env.ANDROID_HOME + '."'; + log.pass(msg); + cb(null, msg); + } else { + msg = 'ANDROID_HOME is set but does not exist on the file system at "' + env.ANDROID_HOME + '"'; + log.fail(msg); + cb(msg, msg); + } +}; + +exports.IsJavaHomeExported = function(cb) { + var msg; + if (env.JAVA_HOME === null) { + log.fail(msg); + msg = 'JAVA_HOME is not set'; + cb(msg, msg); + } else if (fs.existsSync(env.JAVA_HOME)) { + msg = 'JAVA_HOME is set to "' + env.JAVA_HOME + '."'; + log.pass(msg); + cb(null, msg); + } else { + msg = 'JAVA_HOME is set but does not exist on the file system at "' + env.JAVA_HOME + '"'; + log.fail(msg); + cb(msg, msg); + } +}; \ No newline at end of file diff --git a/lib/doctor/common.js b/lib/doctor/common.js new file mode 100644 index 000000000..6372bffc1 --- /dev/null +++ b/lib/doctor/common.js @@ -0,0 +1,84 @@ +"use strict"; +var prompt = require("prompt") + , colors = require("colors"); + +prompt.message = ''; +prompt.delimiter = ''; + +var log = { + pass : function(msg) { + console.log('\u2714 '.green + msg.white); + }, + + fail : function(msg) { + console.log('\u2716 '.red + msg.white); + }, + + warning : function(msg) { + console.log(msg.yellow); + }, + + error : function(msg) { + console.log(msg.red); + }, + + comment : function(msg) { + console.log(msg.cyan); + }, + + info : function(msg) { + console.log(msg.white); + }, + + verbose : function(msg) { + console.log(msg.grey); + }, + + debug : function(msg) { + console.log(msg.darkgrey); + } +}; +exports.log = Object.create(log); + +exports.exitDoctor = function() { + log.error("Appium-Doctor detected problems. Please fix and rerun Appium-Doctor."); + process.exit(-1); +}; + +exports.promptYesOrNo = function(message, yesCb, noCb) { + prompt.start(); + var promptSchema = { + properties: { + continue: { + description: (message + ' (y/n) ').white, + delimiter: '', + type: 'string', + pattern: /^(y|n)/, + message: 'Please enter y or n!', + required: true + } + } + }; + prompt.get(promptSchema, function(err, result) { + if (result.continue == 'y') { + yesCb(); + } else { + noCb(); + } + }); +}; + +exports.promptForAnyKey = function(cb) { + prompt.start(); + var promptSchema = { + properties: { + continue: { + description: 'Press any key to continue:'.white, + type: 'string' + } + } + }; + prompt.get(promptSchema, function() { + cb(); + }); +}; \ No newline at end of file diff --git a/lib/doctor/ios.js b/lib/doctor/ios.js new file mode 100644 index 000000000..e5b2f2e70 --- /dev/null +++ b/lib/doctor/ios.js @@ -0,0 +1,90 @@ +"use strict"; +var path = require('path') + , fs = require('fs') + , env = process.env + , exec = require('child_process').exec + , common = require("./common.js") + , log = require('./common.js').log + , async = require('async'); + +exports.runAllChecks = function(cb) { + async.series([ + exports.checkForXcode, + exports.checkForXcodeCommandLineTools + ], cb); +}; + +exports.checkForXcode = function(cb) { + var msg; + exec("xcode-select -p", { maxBuffer: 524288}, function(err, stdout) { + if (err === null) { + var xcodePath = stdout.replace("\n",""); + if(fs.existsSync(xcodePath)) { + msg = "Xcode is installed at " + xcodePath; + log.pass(msg); + cb(null, msg); + } else { + msg = "Xcode is not installed."; + log.fail(msg); + common.promptYesOrNo("Fix it?", function() { + exports.installXcode(cb); + }, function() { + cb(msg, msg); + }); + } + } else { + msg = "Xcode is not installed: " + err; + log.fail(msg); + common.promptYesOrNo("Fix it?", function() { + exports.installXcode(cb); + }, function() { + cb(msg, msg); + }); + } + }); +}; + +exports.checkForXcodeCommandLineTools = function(cb) { + var msg; + exec("pkgutil --pkg-info=com.apple.pkg.CLTools_Executables", { maxBuffer: 524288}, function(err, stdout) { + if (err === null) { + var match = stdout.match(/install-time/); + if (match !== null) { + msg = "Xcode Command Line Tools are installed."; + cb(null, msg); + } else { + msg = "Xcode Command Line Tools are NOT installed."; + log.fail(msg); + common.promptYesOrNo("Fix it?", function() { + exports.installXcodeCommandLineTools(cb); + }, function() { + cb(msg, msg); + }); + } + } else { + msg = "Xcode Command Line Tools are NOT installed: " + err; + log.fail(msg); + common.promptYesOrNo("Fix it?", function() { + exports.installXcodeCommandLineTools(cb); + }, function() { + cb(msg, msg); + }); + } + }); +}; + +exports.installXcode = function(cb) { + exec("xcode-select --install", { maxBuffer: 524288}, function() { + common.promptForAnyKey(function() { + exports.checkForXcode(cb); + }); + }); +}; + +exports.installXcodeCommandLineTools = function(cb) { + exec("xcode-select --install", { maxBuffer: 524288}, function() { + common.promptForAnyKey(function() { + exports.checkForXcodeCommandLineTools(cb); + }); + }); +}; diff --git a/package.json b/package.json index 0257a8362..aa67ea2f7 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "main": "./lib/server/main.js", "bin": { "appium": "./bin/appium.js", + "appium-doctor": "./bin/appium-doctor.js", "instruments_client": "./bin/instruments-client.js", "authorize_ios": "./bin/authorize-ios.js" },