You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by db...@apache.org on 2015/04/30 02:35:55 UTC

[2/8] cordova-medic git commit: [CB-8870] Splitting medic file into subcommands and adding missing commands.

[CB-8870] Splitting medic file into subcommands and adding missing commands.


Project: http://git-wip-us.apache.org/repos/asf/cordova-medic/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-medic/commit/49da31f2
Tree: http://git-wip-us.apache.org/repos/asf/cordova-medic/tree/49da31f2
Diff: http://git-wip-us.apache.org/repos/asf/cordova-medic/diff/49da31f2

Branch: refs/heads/master
Commit: 49da31f2aed90d4c1cff5175a7cb27abc99ae9e3
Parents: 0ad8700
Author: Dmitry Blotsky <dm...@gmail.com>
Authored: Wed Apr 29 15:27:45 2015 -0700
Committer: Dmitry Blotsky <dm...@gmail.com>
Committed: Wed Apr 29 15:27:45 2015 -0700

----------------------------------------------------------------------
 bin/checkout.js         |  53 ------
 bin/medic.js            | 136 ----------------
 lib/util.js             |  56 +++++++
 medic/medic-check.js    |  58 +++++++
 medic/medic-checkout.js |  80 +++++++++
 medic/medic-clean.js    | 111 +++++++++++++
 medic/medic-kill.js     | 110 +++++++++++++
 medic/medic-run.js      | 380 +++++++++++++++++++++++++++++++++++++++++++
 medic/medic.js          |  40 +++++
 9 files changed, 835 insertions(+), 189 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cordova-medic/blob/49da31f2/bin/checkout.js
----------------------------------------------------------------------
diff --git a/bin/checkout.js b/bin/checkout.js
deleted file mode 100644
index 29c6e6f..0000000
--- a/bin/checkout.js
+++ /dev/null
@@ -1,53 +0,0 @@
-#!/usr/bin/env node
-
-// node dependencies
-var fs = require('fs');
-
-// external dependencies
-var shell = require('shelljs');
-
-// parse args
-var argv = require('optimist')
-    .usage('Usage: $0 --config {path} --exclude {name[,name[,...]]}')
-    .demand('config')
-    .argv;
-
-function cloneProject(projectName, projectsConfig) {
-
-    var project  = projectsConfig[projectName];
-    var codebase = project.codebases[project.codebase];
-    var command  = 'git clone ' + codebase.repo + ' --branch=' + codebase.branch + ' --depth 1'
-
-    shell.exec(command, {silent: false}, function (returnCode, output) {
-        if (returnCode !== 0) {
-            throw 'Command \'' + command + '\' failed with code ' + returnCode;
-        }
-    });
-}
-
-function main () {
-
-    // get args
-    var configFile    = argv.config;
-    var excludedNames = [];
-
-    if (argv.exclude) {
-        excludedNames = argv.exclude.split(',');
-    }
-
-    // shell config
-    shell.config.fatal  = true;
-    shell.config.silent = false;
-
-    // read in config
-    var projectsConfig = JSON.parse(fs.readFileSync(configFile, 'utf8'));
-
-    // clone all projects in the config
-    for (projectName in projectsConfig) {
-        if (excludedNames.indexOf(projectName) === (-1)) {
-            cloneProject(projectName, projectsConfig);
-        }
-    }
-}
-
-main();

http://git-wip-us.apache.org/repos/asf/cordova-medic/blob/49da31f2/bin/medic.js
----------------------------------------------------------------------
diff --git a/bin/medic.js b/bin/medic.js
deleted file mode 100755
index eaa5a05..0000000
--- a/bin/medic.js
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/usr/bin/env node
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-"use strict";
-
-// node dependencies
-var fs = require("fs");
-var os = require("os");
-
-// external dependencies
-var shelljs  = require("shelljs");
-var optimist = require("optimist");
-
-// constants
-var MAX_REMOVAL_ATTEMPTS = 3;
-
-// parse args
-var argv = optimist
-    .usage("Usage: $0 [command]")
-    .demand(1)
-    .argv;
-
-// helpers
-function fatal(message) {
-    console.error("ERROR: " + message);
-    process.exit(1);
-}
-
-function contains(collection, item) {
-    return collection.indexOf(item) != (-1);
-}
-
-function exclusiveLs(lsPath, excludes) {
-    var paths = fs.readdirSync(lsPath);
-    return paths.filter(function (pathName) {
-        return !contains(excludes, pathName);
-    });
-}
-
-// subcommands
-function commandClean() {
-
-    // command-specific args
-    argv = optimist
-        .usage("Usage: $0 --exclude {name[,name[,...]]}")
-        .argv;
-
-    // get args
-    var excludeString = argv.exclude;
-    var excludedPaths = [".", ".."];
-
-    if (argv.exclude) {
-        excludedPaths = excludedPaths.concat(excludeString.split(","));
-    }
-
-    // get all directories except excluded ones
-    var pathsToRemove = exclusiveLs(".", excludedPaths);
-
-    console.log("NOT removing the following paths:");
-    console.log(excludedPaths);
-    console.log("removing the following paths:");
-    console.log(pathsToRemove);
-
-    // skip if no paths to delete
-    if (pathsToRemove.length <= 0) {
-        return;
-    }
-
-    // attempt to delete the paths
-    var attempts = 0;
-    var success  = false;
-    while (success === false) {
-
-        // try to remove the paths
-        shelljs.rm("-rf", pathsToRemove);
-
-        // check if any of them remain
-        var remainingPaths = exclusiveLs(".", excludedPaths);
-        if (remainingPaths.length === 0) {
-            success = true;
-        } else {
-            console.error("cleanup attempt #" + attempts + " failed");
-        }
-
-        // limit the number of attempts
-        attempts += 1;
-        if (attempts >= MAX_REMOVAL_ATTEMPTS) {
-            break;
-        }
-    }
-
-    // if loop exited without success, return an error
-    if (success === false) {
-        fatal("failed to remove files");
-    }
-}
-
-// main
-function main() {
-
-    // shell config
-    shelljs.config.fatal  = false;
-    shelljs.config.silent = false;
-
-    // get args
-    var command = argv._[0];
-
-    // run command
-    switch (command) {
-        case "clean":
-            commandClean();
-            break;
-        default:
-            fatal("unknown command " + command);
-    }
-}
-
-main();

http://git-wip-us.apache.org/repos/asf/cordova-medic/blob/49da31f2/lib/util.js
----------------------------------------------------------------------
diff --git a/lib/util.js b/lib/util.js
new file mode 100644
index 0000000..d5d514d
--- /dev/null
+++ b/lib/util.js
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* jshint node: true */
+
+module.exports = function () {
+
+    var os = require("os");
+
+    return {
+
+        ANDROID:    "android",
+        BLACKBERRY: "blackberry10",
+        IOS:        "ios",
+        WINDOWS:    "windows",
+        WP8:        "wp8",
+
+        fatal: function (message) {
+            console.error("FATAL: " + message);
+            process.exit(1);
+        },
+
+        isWindows: function () {
+            // NOTE:
+            //      - including "^" because otherwise "Darwin" matches
+            //      - only "win" and not "windows" because "win32" should also match
+            return /^win/.test(os.platform());
+        },
+
+        medicLog: function (message) {
+            var RED_COLOR = "\033[31m";
+            var NO_COLOR  = "\033[m";
+            console.log(RED_COLOR + "[MEDIC LOG]" + NO_COLOR + " " + message);
+        },
+
+        contains: function (collection, item) {
+            return collection.indexOf(item) != (-1);
+        }
+    };
+}();

http://git-wip-us.apache.org/repos/asf/cordova-medic/blob/49da31f2/medic/medic-check.js
----------------------------------------------------------------------
diff --git a/medic/medic-check.js b/medic/medic-check.js
new file mode 100644
index 0000000..cbff9fb
--- /dev/null
+++ b/medic/medic-check.js
@@ -0,0 +1,58 @@
+#!/usr/bin/env node
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* jshint node: true */
+
+"use strict";
+
+var optimist = require("optimist");
+
+var testcheck = require("../lib/testcheck");
+
+function main() {
+
+    // get args
+    var argv = optimist
+        .usage("Usage: $0 {options}")
+        .demand("id")
+        .demand("couchdb")
+        .argv;
+
+    var buildId    = argv.id;
+    var couchdbURI = argv.couchdb;
+
+    var result = testcheck(buildId, couchdbURI);
+    result.then(
+        function onFulfilled(value) {
+            if (value === true) {
+                process.exitCode = 0;
+            } else {
+                process.exitCode = 1;
+            }
+        },
+        function onRejected(error) {
+            console.error("test check failed: " + error);
+            process.exitCode = 1;
+        }
+    );
+}
+
+main();

http://git-wip-us.apache.org/repos/asf/cordova-medic/blob/49da31f2/medic/medic-checkout.js
----------------------------------------------------------------------
diff --git a/medic/medic-checkout.js b/medic/medic-checkout.js
new file mode 100644
index 0000000..0cb978a
--- /dev/null
+++ b/medic/medic-checkout.js
@@ -0,0 +1,80 @@
+#!/usr/bin/env node
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* jshint node: true */
+
+"use strict";
+
+var fs = require("fs");
+
+var shelljs  = require("shelljs");
+var optimist = require("optimist");
+
+var util = require("../lib/util");
+
+// helpers
+function cloneProject(projectName, projectsConfig) {
+
+    var project  = projectsConfig[projectName];
+    var codebase = project.codebases[project.codebase];
+    var command  = "git clone " + codebase.repo + " --branch=" + codebase.branch + " --depth 1"
+
+    shelljs.exec(command, {silent: false, async: true}, function (returnCode, output) {
+        if (returnCode !== 0) {
+            util.fatal("command \"" + command + "\" failed with code " + returnCode);
+        }
+    });
+}
+
+// main
+function main() {
+
+    // shell config
+    shelljs.config.fatal  = false;
+    shelljs.config.silent = false;
+
+    // get args
+    var argv = optimist
+        .usage("Usage: $0 --config {path} --exclude {name[,name[,...]]}")
+        .demand("config")
+        .describe("exclude", "repositories not to clone")
+        .argv;
+
+    var configFile    = argv.config;
+    var excludedNames = [];
+
+    // parse excludes
+    if (argv.exclude) {
+        excludedNames = argv.exclude.split(",");
+    }
+
+    // read in config
+    var projectsConfig = JSON.parse(fs.readFileSync(configFile, "utf-8"));
+
+    // clone all projects in the config
+    for (var projectName in projectsConfig) {
+        if (excludedNames.indexOf(projectName) === (-1)) {
+            cloneProject(projectName, projectsConfig);
+        }
+    }
+}
+
+main();

http://git-wip-us.apache.org/repos/asf/cordova-medic/blob/49da31f2/medic/medic-clean.js
----------------------------------------------------------------------
diff --git a/medic/medic-clean.js b/medic/medic-clean.js
new file mode 100644
index 0000000..5a93acf
--- /dev/null
+++ b/medic/medic-clean.js
@@ -0,0 +1,111 @@
+#!/usr/bin/env node
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* jshint node: true */
+
+"use strict";
+
+var fs = require("fs");
+
+var shelljs  = require("shelljs");
+var optimist = require("optimist");
+
+var util = require("../lib/util");
+
+// constants
+var MAX_REMOVAL_ATTEMPTS = 3;
+
+// helpers
+function exclusiveLs(lsPath, excludes) {
+    var paths = fs.readdirSync(lsPath);
+    return paths.filter(function (pathName) {
+        return !util.contains(excludes, pathName);
+    });
+}
+
+// main
+function main() {
+
+    // shell config
+    shelljs.config.fatal  = false;
+    shelljs.config.silent = false;
+
+    // get args
+    var argv = optimist
+        .usage("Usage: $0 --exclude {name[,name[,...]]}")
+        .argv;
+
+    var excludeString = argv.exclude;
+    var excludedPaths = [".", ".."];
+
+    // parse excludes
+    if (argv.exclude) {
+        excludedPaths = excludedPaths.concat(excludeString.split(","));
+    }
+
+    // get all directories except excluded ones
+    var pathsToRemove = exclusiveLs(".", excludedPaths);
+
+    util.medicLog("NOT removing the following paths:");
+    util.medicLog("[ " + excludedPaths.join(" , ") + " ]");
+    util.medicLog("removing the following paths:");
+    util.medicLog("[ " + pathsToRemove.join(" , ") + " ]");
+
+    // skip if there are no paths to delete
+    if (pathsToRemove.length <= 0) {
+        return;
+    }
+
+    // attempt to delete the paths
+    var attempts = 0;
+    var success  = false;
+    while (success === false) {
+
+        util.medicLog("cleanup attempt #" + attempts + " started");
+
+        // try to remove the paths
+        shelljs.rm("-rf", pathsToRemove);
+
+        // check if any of them remain
+        var remainingPaths = exclusiveLs(".", excludedPaths);
+        if (remainingPaths.length === 0) {
+            success = true;
+        } else {
+            console.error("cleanup attempt #" + attempts + " failed");
+        }
+
+        // limit the number of attempts
+        attempts += 1;
+        if (attempts >= MAX_REMOVAL_ATTEMPTS) {
+            break;
+        }
+    }
+
+    // if loop exited without success, return an error
+    if (success === false) {
+        console.error("failed to remove files");
+        process.exitCode = 1;
+    } else {
+        util.medicLog("cleanup succeeded");
+    }
+}
+
+main();

http://git-wip-us.apache.org/repos/asf/cordova-medic/blob/49da31f2/medic/medic-kill.js
----------------------------------------------------------------------
diff --git a/medic/medic-kill.js b/medic/medic-kill.js
new file mode 100644
index 0000000..998509f
--- /dev/null
+++ b/medic/medic-kill.js
@@ -0,0 +1,110 @@
+#!/usr/bin/env node
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* jshint node: true */
+
+"use strict";
+
+var shelljs  = require("shelljs");
+var optimist = require("optimist");
+
+var util = require("../lib/util");
+
+// helpers
+function tasksOnPlatform(platformName) {
+    switch (platformName) {
+        case util.WINDOWS:
+            return ["WWAHost.exe"];
+        case util.WP8:
+            return ["Xde.exe"];
+        case util.IOS:
+            return ["iOS Simulator"];
+        case util.ANDROID:
+            if (util.isWindows()) {
+                return ["emulator-arm.exe", "adb.exe"];
+            } else {
+                return ["emulator64-x86"];
+            }
+        case util.BLACKBERRY:
+            return [];
+        default:
+            util.fatal("unknown platform " + platformName);
+    }
+}
+
+function getKillCommand(taskNames) {
+
+    if (util.isWindows()) {
+        var cli  = "taskkill /F";
+        var args = taskNames.map(function (name) { return "/IM \"" + name + "\""; });
+    } else {
+        var cli  = "killall";
+        var args = taskNames.map(function (name) { return "\"" + name + "\""; });
+    }
+
+    return cli + " " + args.join(" ");
+}
+
+function killTasks(taskNames) {
+
+    if (!taskNames || taskNames.length < 1) {
+        return;
+    }
+
+    var command = getKillCommand(taskNames);
+
+    util.medicLog("running the following command:");
+    util.medicLog("    " + command);
+
+    shelljs.exec(command, {silent: false, async: true}, function (returnCode, output) {
+        if (returnCode !== 0) {
+            console.warn("WARNING: kill command returned " + returnCode);
+        }
+    });
+}
+
+// main
+function main() {
+
+    // shell config
+    shelljs.config.fatal  = false;
+    shelljs.config.silent = false;
+
+    // get args
+    var argv = optimist
+        .usage("Usage: $0 --platform {platform}")
+        .demand("platform")
+        .argv;
+
+    var platform = argv.platform;
+
+    // get platform tasks
+    var platformTasks = tasksOnPlatform(platform);
+
+    if (platformTasks.length < 1) {
+        console.warn("no known tasks to kill");
+    }
+
+    // kill them
+    killTasks(platformTasks);
+}
+
+main();

http://git-wip-us.apache.org/repos/asf/cordova-medic/blob/49da31f2/medic/medic-run.js
----------------------------------------------------------------------
diff --git a/medic/medic-run.js b/medic/medic-run.js
new file mode 100644
index 0000000..a953a80
--- /dev/null
+++ b/medic/medic-run.js
@@ -0,0 +1,380 @@
+#!/usr/bin/env node
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* jshint node: true */
+
+"use strict";
+
+var fs   = require("fs");
+var path = require("path");
+
+var shelljs  = require("shelljs");
+var optimist = require("optimist");
+
+var util     = require("../lib/util");
+var testwait = require("../lib/testwait");
+
+// constants
+var DEFAULT_ENCODING        = "utf-8";
+var CORDOVA_MEDIC_DIR       = "cordova-medic";
+var DEFAULT_APP_PATH        = "mobilespec";
+var DEFAULT_APP_ENTRY       = "index.html";
+var ANDROID_TIMEOUT         = 120000; // in milliseconds
+var MEDIC_BUILD_PREFIX      = "medic-cli-build";
+var DEFAULT_WINDOWS_VERSION = "store";
+var WINDOWS_VERSION_CHOICES = ["store", "store80", "phone"];
+var DEFAULT_TIMEOUT         = 600; // in seconds
+
+// helpers
+function currentMillisecond() {
+    // NOTE:
+    //      coercing a Date to a Number returns
+    //      the Date's representation in milliseconds
+    return Number(new Date());
+}
+
+function generateBuildID() {
+    var components = [MEDIC_BUILD_PREFIX, currentMillisecond()];
+    return components.join("-");
+}
+
+function getConfigPath(appPath) {
+    return path.join(appPath, "config.xml");
+}
+
+function getCSPPath(appPath) {
+    return path.join(appPath, "www", "csp-incl.js");
+}
+
+function createMedicJson(appPath, buildId, couchdbURI) {
+
+    util.medicLog("Writing medic.json to " + appPath);
+    util.medicLog("    sha:     " + buildId);
+    util.medicLog("    couchdb: " + couchdbURI);
+
+    // NOTE:
+    //      the "sha" name is a misnomer, but is kept
+    //      to be compatible with plugin-test-framework
+    var medicConfig = {
+        sha:     buildId,
+        couchdb: couchdbURI
+    };
+
+    var medicConfigContents = JSON.stringify(medicConfig) + "\n";
+    var medicConfigPath     = path.join(appPath, "www", "medic.json");
+
+    fs.writeFileSync(medicConfigPath, medicConfigContents, DEFAULT_ENCODING);
+}
+
+function addURIToWhitelist(appPath, uri) {
+
+    var configFile = getConfigPath(appPath);
+    var cspFile    = getCSPPath(appPath);
+
+    var configContent = fs.readFileSync(configFile, DEFAULT_ENCODING);
+    var cspContent    = fs.readFileSync(cspFile, DEFAULT_ENCODING);
+
+    // add whitelisting rule allow access to couch server
+    util.medicLog("Adding whitelist rule for CouchDB host: " + uri);
+    var accessOriginTag = "<access origin=\"" + uri + "\" />";
+    if (!util.contains(configContent, accessOriginTag)) {
+        configContent = configContent.split("</widget>").join("");
+        configContent += "    " + accessOriginTag + "\n</widget>\n";
+        fs.writeFileSync(configFile, configContent, DEFAULT_ENCODING);
+    }
+
+    // add couchdb address to csp rules
+    util.medicLog("Adding CSP rule for CouchDB host: " + uri);
+    var cspRule = "connect-src " + uri;
+    if (!util.contains(cspContent, cspRule)) {
+        cspContent = cspContent.replace("connect-src", cspRule);
+        fs.writeFileSync(cspFile, cspContent, DEFAULT_ENCODING);
+    }
+}
+
+function setEntryPoint(appPath, entryPoint) {
+
+    var configFile = getConfigPath(appPath);
+    var configContent = fs.readFileSync(configFile, DEFAULT_ENCODING);
+
+    // replace/add start page preference
+    // check if config.xml already contains <content /> element
+    util.medicLog("Setting entry point to " + entryPoint + " in config.xml");
+
+    if (configContent.match(/<content\s*src=".*"\s*\/>/gi)) {
+        configContent = configContent.replace(
+            /<content\s*src=".*"\s*\/>/gi,
+            "<content src=\"" + entryPoint + "\" />"
+        );
+
+    } else {
+
+        // add entry point to config
+        configContent = configContent.split("</widget>").join("") +
+            "    <content src=\"" + entryPoint + "\" />\n</widget>";
+    }
+
+    // write the changes
+    fs.writeFileSync(configFile, configContent, DEFAULT_ENCODING);
+}
+
+function changeAndroidLoadTimeout(appPath, timeout) {
+
+    util.medicLog("Increasing url loading timeout for android to " + timeout);
+
+    var timeoutRegex           = /<preference\s*name\s *= \s*"?loadUrlTimeoutValue"?.*?((\/>)|(>.*?<\/\s*preference>))/i;
+    var timeoutTag             = "<preference name=\"loadUrlTimeoutValue\" value=\"" + timeout + "\" />";
+    var timeoutTagWithPlatform = "    <platform name=\"android\">\n        <preference name=\"loadUrlTimeoutValue\" value=\"120000\" />\n    </platform>\n";
+    var platformRegex          = /<platform\s*name\s *= \s*"android"\s*>/i;
+    var widgetRegex            = /<\/s*widget\s*>/i;
+
+    var configFile    = getConfigPath(appPath);
+    var configContent = fs.readFileSync(configFile, DEFAULT_ENCODING);
+
+    if (timeoutRegex.test(configContent)) {
+        configContent = configContent.replace(timeoutRegex, timeoutTag);
+        util.medicLog("Found \"loadUrlTimeoutValue\" preference, replacing with desired value");
+    } else if (platformRegex.test(configContent)) {
+        var oldPlatformTag = platformRegex.exec(configContent)[0];
+        configContent = configContent.replace(platformRegex, oldPlatformTag + "\n        " + timeoutTag);
+        util.medicLog("Found platform tag, appending \"loadUrlTimeoutValue\" preference");
+    } else if (widgetRegex.test(configContent)) {
+        var oldWidgetTag = widgetRegex.exec(configContent)[0];
+        configContent = configContent.replace(widgetRegex, timeoutTagWithPlatform + oldWidgetTag);
+        util.medicLog("Did not find platform tag, adding preference with platform tag");
+    } else {
+        util.medicLog("Warning: could not modify config.xml for android: no <widget> tag found!");
+    }
+
+    // write the changes
+    fs.writeFileSync(configFile, configContent, DEFAULT_ENCODING);
+}
+
+function setWindowsTargetStoreVersion(appPath, version) {
+
+    util.medicLog('setting target store version to ' + version);
+
+    var configFile    = getConfigPath(appPath);
+    var configContent = fs.readFileSync(configFile, DEFAULT_ENCODING);
+
+    var versionPreference = '    <preference name="windows-target-version" value="' + version + '" />';
+    configContent = configContent.replace('</widget>', versionPreference + '\r\n</widget>');
+
+    fs.writeFileSync(configFile, configContent, "utf8");
+}
+
+function androidSpecificPreparation(argv) {
+
+    var appPath   = argv.app;
+    var extraArgs = "--ant";
+
+    changeAndroidLoadTimeout(appPath, ANDROID_TIMEOUT);
+
+    return extraArgs;
+}
+
+function windowsSpecificPreparation(argv) {
+
+    var appPath    = argv.app;
+    var winVersion = argv.winvers;
+    var extraArgs  = "";
+
+    if (!util.contains(WINDOWS_VERSION_CHOICES, winVersion)) {
+        util.fatal("invalid windows version: " + winVersion);
+    }
+
+    // set windows target store version
+    if (winVersion === "store80") {
+        setWindowsTargetStoreVersion(appPath, "8.0");
+        extraArgs = "--win";
+
+    } else if (winVersion === "store") {
+        setWindowsTargetStoreVersion(appPath, "8.1");
+        extraArgs = "--win";
+
+    } else if (winVersion === "phone") {
+        setWindowsTargetStoreVersion(appPath, "8.1");
+        extraArgs = "--phone";
+    }
+
+    // patch WindowsStoreAppUtils script to allow app run w/out active desktop/remote session
+    if (winVersion === "store80" || winVersion === "store") {
+
+        util.medicLog('Patching WindowsStoreAppUtils to allow app to be run in automated mode');
+
+        var platformPath = path.join(appPath, 'platforms', 'windows');
+
+        var libPath        = path.join(platformPath, 'cordova', 'lib');
+        var appUtilsPath   = path.join(libPath, 'WindowsStoreAppUtils.ps1');
+        var srcScriptPath  = path.join('cordova-medic', 'lib', 'patches', 'EnableDebuggingForPackage.ps1');
+        var destScriptPath = path.join(libPath, 'EnableDebuggingForPackage.ps1');
+
+        // copy over the patch
+        shelljs.cp('-f', srcScriptPath, libPath);
+
+        // add extra code to patch
+        shelljs.sed(
+            '-i', /^\s*\$appActivator .*$/gim,
+            '$&\n' +
+            '    powershell ' + destScriptPath + ' $$ID\n' +
+            '    $Ole32 = Add-Type -MemberDefinition \'[DllImport("Ole32.dll")]public static extern int CoAllowSetForegroundWindow(IntPtr pUnk, IntPtr lpvReserved);\' -Name \'Ole32\' -Namespace \'Win32\' -PassThru\n' +
+            '    $Ole32::CoAllowSetForegroundWindow([System.Runtime.InteropServices.Marshal]::GetIUnknownForObject($appActivator), [System.IntPtr]::Zero)',
+            appUtilsPath
+        );
+    }
+
+    return extraArgs;
+}
+
+function wp8SpecificPreparation(argv) {
+
+    var appPath = argv.app;
+
+    // set permanent guid to prevent multiple installations
+    var guid         = '{8449DEEE-16EB-4A4A-AFCC-8446E8F06FF7}';
+    var manifestPath = path.join(appPath, 'platforms', 'wp8', 'Properties', 'WMAppManifest.xml');
+    var xml          = fs.readFileSync(manifestPath).toString().split('\n');
+
+    for (var i in xml) if (xml[i].indexOf('<App') != -1) {
+        if (xml[i].toLowerCase().indexOf('productid') != -1) {
+            var index = xml[i].toLowerCase().indexOf('productid');
+            var spaceIndex = xml[i].indexOf(' ', index);
+            var stringAsArray = xml[i].split('');
+            stringAsArray.splice(index, spaceIndex - index);
+            xml[i] = stringAsArray.join('');
+        }
+        xml[i] = xml[i].substr(0, xml[i].length - 1);
+        xml[i] += ' ProductID="' + guid + '">';
+        break;
+    }
+
+    fs.writeFileSync(manifestPath, xml.join('\n'));
+
+    var extraArgs = "";
+    return extraArgs;
+}
+
+function getLocalCLI() {
+    if (util.isWindows()) {
+        return "cordova.bat";
+    } else {
+        return "./cordova";
+    }
+}
+
+// main
+function main() {
+
+    // shell config
+    shelljs.config.fatal  = false;
+    shelljs.config.silent = false;
+
+    // get args
+    var argv = optimist
+        .usage("Usage: $0 {options}")
+        .demand("platform")
+        .demand("couchdb")
+        .default("entry", DEFAULT_APP_ENTRY)
+        .default("id", generateBuildID())
+        .default("app", DEFAULT_APP_PATH)
+        .default("timeout", DEFAULT_TIMEOUT).describe("timeout", "timeout in seconds")
+        .default("winvers", DEFAULT_WINDOWS_VERSION).describe("winvers", "[" + WINDOWS_VERSION_CHOICES.join("|") + "]")
+        .argv;
+
+    var platform   = argv.platform;
+    var buildId    = argv.id;
+    var appPath    = argv.app;
+    var couchdbURI = argv.couchdb;
+    var entryPoint = argv.entry;
+    var timeout    = argv.timeout;
+
+    var cli = getLocalCLI();
+
+    // check that the app exists
+    if (!fs.existsSync(appPath)) {
+        util.fatal("app " + appPath + " does not exist");
+    }
+
+    // modify the app to run autonomously
+    createMedicJson(appPath, buildId, couchdbURI);
+    setEntryPoint(appPath, entryPoint);
+    addURIToWhitelist(appPath, couchdbURI);
+
+    // do platform-specific preparations
+    var platformArgs = "";
+    if (platform === util.ANDROID) {
+        platformArgs = androidSpecificPreparation(argv);
+    } else if (platform === util.WINDOWS) {
+        platformArgs = windowsSpecificPreparation(argv);
+    } else if (platform === util.WP8) {
+        platformArgs = wp8SpecificPreparation(argv);
+    }
+
+    // enter the app directory
+    shelljs.pushd(appPath);
+
+    // build the code (synchronously)
+    var buildCommand = cli + " build " + platform + " -- " + platformArgs;
+    util.medicLog("building:");
+    util.medicLog("    " + buildCommand);
+
+    var result = shelljs.exec(buildCommand, {silent: false, async: false});
+    if (result.code != 0) {
+        console.error("BUILD FAILED:\n" + result.output);
+        util.fatal("build failed");
+    }
+
+    // run the code (asynchronously)
+    var runCommand = cli + " run " + platform + " -- " + platformArgs;
+    util.medicLog("running:");
+    util.medicLog("    " + runCommand);
+
+    shelljs.exec(runCommand, {silent: false, async: true}, function (returnCode, output) {
+        if (returnCode != 0) {
+            console.error("RUN FAILED:\n" + result.output);
+            util.fatal("run failed");
+        }
+    });
+
+    // exit the app directory
+    shelljs.popd();
+
+    // wait for test results
+    // NOTE:
+    //      timeout needs to be in milliseconds, but it's
+    //      given in seconds, so we multiply by 1000
+    util.medicLog("watching for test results");
+    testwait.init(couchdbURI);
+    testwait.waitTestsCompleted(buildId, timeout * 1000).then(
+        function onFulfilled(value) {
+            util.medicLog("got test results");
+            process.exit(process.exitCode);
+        },
+        function onRejected(error) {
+            console.error("didn't get test results: " + error);
+            process.exitCode = 1;
+        }
+    );
+
+    util.medicLog("waiting ...");
+}
+
+main();

http://git-wip-us.apache.org/repos/asf/cordova-medic/blob/49da31f2/medic/medic.js
----------------------------------------------------------------------
diff --git a/medic/medic.js b/medic/medic.js
new file mode 100755
index 0000000..f58a39a
--- /dev/null
+++ b/medic/medic.js
@@ -0,0 +1,40 @@
+#!/usr/bin/env node
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/* jshint node: true */
+
+"use strict";
+
+var commander = require("commander");
+
+function main() {
+
+    // delegate work to subcommands
+    commander
+        .command("clean",    "clean the current directory")
+        .command("kill",     "kill tasks spawned by a platform run")
+        .command("checkout", "check out repositories")
+        .command("run",      "run a cordova app in automated mode")
+        .command("check",    "analyze test runs from a medic DB")
+        .parse(process.argv);
+}
+
+main();


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cordova.apache.org
For additional commands, e-mail: commits-help@cordova.apache.org