Files
appium/lib/doctor/ios.js
T

311 lines
11 KiB
JavaScript

"use strict";
var path = require('path')
, fs = require('fs')
, env = process.env
, exec = require('child_process').exec
, async = require('async');
require("./common.js");
function IOSChecker(log) {
this.log = log;
this.osVersion = null;
}
exports.IOSChecker = IOSChecker;
IOSChecker.prototype.runAllChecks = function (cb) {
async.series([
this.getMacOSXVersion.bind(this),
this.checkForXcode.bind(this),
this.checkForXcodeCommandLineTools.bind(this),
this.checkDevToolsSecurity.bind(this),
this.checkAuthorizationDB.bind(this),
this.checkForNodeBinary.bind(this)
], cb);
};
IOSChecker.prototype.getMacOSXVersion = function (cb) {
exec("sw_vers -productVersion", function (err, stdout) {
if (err === null) {
if (stdout.match('10.8') !== null) {
this.osVersion = '10.8';
cb(null, "Mac OS X 10.8 is installed.");
} else if (stdout.match('10.9') !== null) {
this.osVersion = '10.9';
cb(null, "Mac OS X 10.9 is installed.");
} else {
this.log.fail("Could not detect Mac OS X Version", cb);
}
} else {
this.log.fail("Unknown SW Version Command: " + err, cb);
}
}.bind(this));
};
IOSChecker.prototype.checkForXcode = function (cb) {
var msg;
exec("xcode-select --print-path", { maxBuffer: 524288}, function (err, stdout) {
if (err === null) {
var xcodePath = stdout.replace("\n", "");
if (fs.existsSync(xcodePath)) {
this.log.pass("Xcode is installed at " + xcodePath, cb);
} else {
msg = "Xcode is not installed.";
this.log.fail(msg);
this.log.promptToFix("Xcode is not installed.", function () {
this.installXcode(cb);
}.bind(this), function () {
cb(msg, msg);
});
}
} else {
msg = "Xcode is not installed: " + err;
this.log.fail(msg);
this.log.promptToFix("Xcode is not installed.", function () {
this.installXcode(cb);
}.bind(this), function () {
cb(msg, msg);
});
}
}.bind(this));
};
IOSChecker.prototype.checkForXcodeCommandLineTools = function (cb) {
var msg;
var pkgName = this.osVersion === '10.8' ? "com.apple.pkg.DeveloperToolsCLI" : "com.apple.pkg.CLTools_Executables";
exec("pkgutil --pkg-info=" + pkgName, { maxBuffer: 524288}, function (err, stdout) {
if (err === null) {
var match = stdout.match(/install-time/);
if (match !== null) {
this.log.pass("Xcode Command Line Tools are installed.", cb);
} else {
msg = "Xcode Command Line Tools are NOT installed.";
this.log.fail(msg);
this.log.promptToFix("Xcode's command line tools are NOT installed.", function () {
this.installXcodeCommandLineTools(cb);
}.bind(this), function () {
cb(msg, msg);
});
}
} else {
msg = "Xcode Command Line Tools are NOT installed: " + err;
this.log.fail(msg);
this.log.promptToFix("Xcode's command line tools are NOT installed.", function () {
this.installXcodeCommandLineTools(cb);
}.bind(this), function () {
cb(msg, msg);
});
}
}.bind(this));
};
IOSChecker.prototype.checkDevToolsSecurity = function (cb) {
var msg;
exec("DevToolsSecurity", { maxBuffer: 524288}, function (err, stdout) {
if (err === null && stdout.match(/enabled/) !== null) {
this.log.pass("DevToolsSecurity is enabled.", cb);
} else {
msg = 'DevToolsSecurity is not enabled.';
this.log.fail(msg);
this.log.promptToFix(msg, function () {
this.authorizeIOS(this.checkDevToolsSecurity.bind(this), cb);
}.bind(this), function () {
cb(msg, msg);
});
}
}.bind(this));
};
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 || stdout.match(/allow/) !== null)) {
this.log.pass("The Authorization DB is set up properly.", cb);
} else if (this.osVersion === '10.8') {
fs.readFile('/etc/authorization', 'utf8', function (err, data) {
if (err) {
this.log.fail('The Authorization DB is NOT set up properly.', cb);
} else {
var match = data.match(/<key>system.privilege.taskport<\/key>\s*\n\s*<dict>\n\s*<key>allow-root<\/key>\n\s*(<true\/>)/);
if (match !== null) {
this.log.pass("The Authorization DB is set up properly.", cb);
} else {
this.log.fail('The Authorization DB is NOT set up properly.', cb);
}
}
}.bind(this));
} else {
msg = 'The Authorization DB is NOT set up properly.';
this.log.fail(msg);
this.log.promptToFix(msg, function () {
this.authorizeIOS(this.checkAuthorizationDB.bind(this), cb);
}.bind(this), function () {
cb(msg, msg);
});
}
}.bind(this));
};
IOSChecker.prototype.checkForNodeBinary = function (cb) {
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) {
if (typeof env.NODE_BIN !== "undefined" && fs.existsSync(env.NODE_BIN)) {
this.log.pass("Node binary found using NODE_BIN environment variable at " + env.NODE_BIN, cb);
} else if (fs.existsSync('/usr/local/bin/node')) {
this.log.pass("Node binary found at /usr/local/bin/node", cb);
} else if (fs.existsSync('/opt/local/bin/node')) {
this.log.pass("Node binary found at /opt/local/bin/node", cb);
} else {
var 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", ""))) {
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);
}
}.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", ""))) {
this.log.pass("Node binary found using AppleScript at " + stdout.replace("\n", ""), cb);
} 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.json file.';
var appiumConfigPath = path.resolve(__dirname, "../..", ".appiumconfig.json");
if (fs.existsSync(appiumConfigPath)) {
fs.readFile(appiumConfigPath, 'utf8', function (err, data) {
if (err === null) {
try {
var jsonobj = JSON.parse(data);
if (typeof jsonobj.node_bin !== "undefined" && fs.existsSync(jsonobj.node_bin)) {
this.log.pass("Node binary found using .appiumconfig.json at " + jsonobj.node_bin, cb);
} 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 () {
this.checkForXcode(cb);
}.bind(this));
}.bind(this));
};
IOSChecker.prototype.installXcodeCommandLineTools = function (cb) {
exec("xcode-select --install", { maxBuffer: 524288}, function () {
this.log.promptToConfirmFix(function () {
this.checkForXcodeCommandLineTools(cb);
}.bind(this));
}.bind(this));
};
IOSChecker.prototype.authorizeIOS = function (outerCb, innerCb) {
var authorizePath = path.resolve(__dirname, "../../bin", "authorize-ios.js");
exec("'" + process.execPath + "' '" + authorizePath + "'", { maxBuffer: 524288}, function (err) {
if (err) {
this.log.error('Could not authorize iOS: ' + err);
}
}.bind(this)).on('exit', function () {
this.log.promptToConfirmFix(function () {
outerCb(innerCb);
}.bind(this));
}.bind(this));
};
IOSChecker.prototype.setupNodeBinaryPath = function (cb) {
var appiumConfigPath = path.resolve(__dirname, "../../", ".appiumconfig.json");
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.json. Error parsing JSON: " + jsonErr);
this.checkForNodeBinary(cb);
}
} else {
this.log.error("Could not setup node binary path in .appiumconfig.json. Error reading config: " + err);
this.checkForNodeBinary(cb);
}
}.bind(this));
} else {
this.log.error('The .appiumconfig.json file was not found at ' + appiumConfigPath);
this.exitDoctor();
}
};