You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2014/05/21 16:29:42 UTC
git commit: Add pushing via zip file logic.
Repository: cordova-app-harness
Updated Branches:
refs/heads/master d58c684e9 -> 4a366039f
Add pushing via zip file logic.
Project: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/repo
Commit: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/commit/4a366039
Tree: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/tree/4a366039
Diff: http://git-wip-us.apache.org/repos/asf/cordova-app-harness/diff/4a366039
Branch: refs/heads/master
Commit: 4a366039f8a1ac343ace5eb5bdaabf5880d08ab2
Parents: d58c684
Author: Andrew Grieve <ag...@chromium.org>
Authored: Wed May 21 10:29:05 2014 -0400
Committer: Andrew Grieve <ag...@chromium.org>
Committed: Wed May 21 10:29:05 2014 -0400
----------------------------------------------------------------------
harness-push/harness-push.js | 521 +++++++++++++++++++++----------------
harness-push/package.json | 7 +-
www/cdvah/js/HarnessServer.js | 102 +++++---
3 files changed, 373 insertions(+), 257 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/4a366039/harness-push/harness-push.js
----------------------------------------------------------------------
diff --git a/harness-push/harness-push.js b/harness-push/harness-push.js
index d505ea0..07e6ff1 100755
--- a/harness-push/harness-push.js
+++ b/harness-push/harness-push.js
@@ -25,34 +25,90 @@ var fs = require('fs'),
Q = require('q'),
request = require('request'),
nopt = require('nopt'),
- shelljs = require('shelljs');
+ shelljs = require('shelljs'),
+ JSZip = require('jszip');
+
+var IGNORE_PATH_FOR_PUSH_REGEXP = /www\/(?:plugins\/|cordova\.js)/;
+
+function getDerivedWwwDir(dir, platformId) {
+ if (platformId == 'android') {
+ return path.join(dir, 'platforms', platformId, 'assets', 'www');
+ } else if (platformId == 'ios') {
+ return path.join(dir, 'platforms', platformId, 'www');
+ }
+ throw new Error('Platform not supported: ' + platformId);
+}
+
+function getDerivedConfigXmlPath(dir, platformId) {
+ if (platformId == 'android') {
+ return path.join(dir, 'platforms', platformId, 'res', 'xml', 'config.xml');
+ } else if (platformId == 'ios') {
+ var base = path.join(dir, 'platforms', platformId);
+ var ret = null;
+ fs.readdirSync(base).forEach(function(a) {
+ if (a != 'www' && a != 'cordova' && a != 'CordovaLib') {
+ var fullPath = path.join(base, a, 'config.xml');
+ if (fs.existsSync(fullPath)) {
+ ret = fullPath;
+ }
+ }
+ });
+ return ret;
+ }
+ throw new Error('Platform not supported: ' + platformId);
+}
+
+function discoverAppId(dir) {
+ var configXmlPath = path.join(dir, 'config.xml');
+ if (!fs.existsSync(configXmlPath)) {
+ configXmlPath = path.join(dir, 'www', 'config.xml');
+ }
+ if (!fs.existsSync(configXmlPath)) {
+ throw new Error('Not a Cordova project: ' + dir);
+ }
+
+ var configData = fs.readFileSync(configXmlPath, 'utf8');
+ var m = /\bid="(.*?)"/.exec(configData);
+ var appId = m && m[1];
+ if (!appId) {
+ throw new Error('Could not find app ID within: ' + configXmlPath);
+ }
+ return appId;
+}
// Takes a Node-style callback: function(err).
exports.push = function(target, dir, pretend) {
- var appId;
- var appType = 'cordova';
- var configXmlPath = path.join(dir, 'config.xml');
- dir = path.join(dir, 'www');
- if (!fs.existsSync(configXmlPath)) {
- configXmlPath = path.join(dir, 'config.xml');
- }
- if (!fs.existsSync(configXmlPath)) {
- throw new Error('Not a Cordova project.');
- }
- var configData = fs.readFileSync(configXmlPath, { encoding: 'utf-8' });
- var m = /\bid="(.*?)"/.exec(configData);
- appId = m && m[1];
-
- // ToDo - Add ability to bootstrap with a zip file.
- return doFileSync(target, appId, appType, configXmlPath, dir, pretend);
+ var appId = discoverAppId(dir);
+ var appType = 'cordova';
+
+ return exports.assetmanifest(target, appId)
+ .then(function(result) {
+ var derivedWwwDir = getDerivedWwwDir(dir, result.body['platform']);
+ var derivedConfigXmlPath = getDerivedConfigXmlPath(dir, result.body['platform']);
+ console.log(derivedConfigXmlPath);
+ var cordovaPluginsPath = path.join(derivedWwwDir, 'cordova_plugins.js');
+ if (!fs.existsSync(cordovaPluginsPath)) {
+ throw new Error('Could not find: ' + cordovaPluginsPath);
+ }
+ if (!fs.existsSync(derivedConfigXmlPath)) {
+ throw new Error('Could not find: ' + derivedConfigXmlPath);
+ }
+
+ var existingAssetManifest = result.body['assetManifest'];
+ if (existingAssetManifest) {
+ return doFileSync(target, appId, appType, derivedWwwDir, derivedConfigXmlPath, existingAssetManifest, pretend);
+ }
+ // TODO: It might be faster to use Zip even when some files exist.
+ return doZipPush(target, appId, appType, derivedWwwDir, derivedConfigXmlPath);
+ });
}
function calculateMd5(fileName) {
- var BUF_LENGTH = 64*1024,
- buf = new Buffer(BUF_LENGTH),
- bytesRead = BUF_LENGTH,
- pos = 0,
- fdr = fs.openSync(fileName, 'r');
+ var BUF_LENGTH = 64*1024;
+ var buf = new Buffer(BUF_LENGTH);
+ var bytesRead = BUF_LENGTH;
+ var pos = 0;
+ var fdr = fs.openSync(fileName, 'r');
try {
var md5sum = crypto.createHash('md5');
@@ -67,280 +123,299 @@ function calculateMd5(fileName) {
return md5sum.digest('hex');
}
-function buildAssetManifest(configXmlPath, dir) {
- var fileList = shelljs.find(dir).filter(function(a) {
- return !fs.statSync(a).isDirectory();
- });
-
- var ret = Object.create(null);
- for (var i = 0; i < fileList.length; ++i) {
- // TODO: convert windows slash to unix slash here.
- var appPath = 'www' + fileList[i].slice(dir.length);
- ret[appPath] = {
- path: appPath,
- realPath: fileList[i],
- etag: calculateMd5(fileList[i]),
- };
- }
- if (configXmlPath && path.dirname(configXmlPath) != dir) {
+function buildAssetManifest(dir, configXmlPath) {
+ var fileList = shelljs.find(dir).filter(function(a) {
+ return !IGNORE_PATH_FOR_PUSH_REGEXP.exec(a) && !fs.statSync(a).isDirectory();
+ });
+
+ var ret = Object.create(null);
+ for (var i = 0; i < fileList.length; ++i) {
+ // TODO: convert windows slash to unix slash here.
+ var appPath = 'www' + fileList[i].slice(dir.length);
+ ret[appPath] = {
+ path: appPath,
+ realPath: fileList[i],
+ etag: calculateMd5(fileList[i]),
+ };
+ }
ret['config.xml'] = {
path: 'config.xml',
realPath: configXmlPath,
etag: calculateMd5(configXmlPath)
};
- }
- return ret;
+ return ret;
}
function buildDeleteList(existingAssetManifest, newAssetManifest) {
- var toDelete = [];
- for (var k in existingAssetManifest) {
- // Don't delete top-level files ever.
- if (k.slice(0, 4) != 'www/') {
- continue;
- }
- if (!newAssetManifest[k]) {
- toDelete.push(k);
+ var toDelete = [];
+ for (var k in existingAssetManifest) {
+ // Don't delete top-level files ever.
+ if (k.slice(0, 4) != 'www/') {
+ continue;
+ }
+ if (!newAssetManifest[k]) {
+ toDelete.push(k);
+ }
}
- }
- return toDelete;
+ return toDelete;
}
function buildPushList(existingAssetManifest, newAssetManifest) {
- var ret = [];
- for (var k in newAssetManifest) {
- var entry = newAssetManifest[k];
- if (entry.etag != existingAssetManifest[k]) {
- if (entry.path == 'config.xml' || entry.path == 'www/config.xml') {
- ret.unshift(entry);
- } else {
- ret.push(entry);
- }
+ var ret = [];
+ for (var k in newAssetManifest) {
+ var entry = newAssetManifest[k];
+ if (entry.etag != existingAssetManifest[k]) {
+ if (entry.path == 'config.xml') {
+ ret.unshift(entry);
+ } else {
+ ret.push(entry);
+ }
+ }
}
- }
- return ret;
+ return ret;
}
function calculatePushBytes(pushList) {
- var ret = 0;
- for (var i = 0; i < pushList.length; ++i) {
- ret += fs.statSync(pushList[i].realPath).size;
- }
- return ret;
+ var ret = 0;
+ for (var i = 0; i < pushList.length; ++i) {
+ ret += fs.statSync(pushList[i].realPath).size;
+ }
+ return ret;
}
-function doFileSync(target, appId, appType, configXmlPath, dir, pretend) {
- return exports.assetmanifest(target, appId)
- .then(function(result) {
- var existingAssetManifest = result.body['assetManifest'] || {};
- var newAssetManifest = buildAssetManifest(configXmlPath, dir);
+function doFileSync(target, appId, appType, dir, configXmlPath, existingAssetManifest, pretend) {
+ var newAssetManifest = buildAssetManifest(dir, configXmlPath);
var deleteList = buildDeleteList(existingAssetManifest, newAssetManifest);
var pushList = buildPushList(existingAssetManifest, newAssetManifest);
var totalPushBytes = calculatePushBytes(pushList);
if (pretend) {
- console.log('AppId=' + appId);
- console.log('Would delete: ' + JSON.stringify(deleteList));
- console.log('Would upload: ' + JSON.stringify(pushList));
- console.log('Upload bytes: ' + totalPushBytes);
+ console.log('AppId=' + appId);
+ console.log('Would delete: ' + JSON.stringify(deleteList));
+ console.log('Would upload: ' + JSON.stringify(pushList));
+ console.log('Upload bytes: ' + totalPushBytes);
} else if (deleteList.length === 0 && pushList.length === 0) {
- console.log('Application already up-to-date.');
+ console.log('Application already up-to-date.');
} else {
- return doRequest('POST', target, 'prepupdate', { appId: appId, appType: appType, json: {'transferSize': totalPushBytes}})
- .then(function() {
- if (deleteList.length > 0) {
- return doRequest('POST', target, 'deletefiles', { appId: appId, appType: appType, json: {'paths': deleteList}})
- }
- })
- .then(function pushNextFile() {
- if (pushList.length === 0) {
- console.log('Push complete.');
- return;
- }
- var curPushEntry = pushList.shift();
- return doRequest('PUT', target, '/putfile', {
- appId: appId,
- appType: appType,
- body: fs.readFileSync(curPushEntry.realPath),
- query: {
- path: curPushEntry.path,
- etag: curPushEntry.etag
- }
- }).then(pushNextFile);
- });
+ return doRequest('POST', target, 'prepupdate', { appId: appId, appType: appType, json: {'transferSize': totalPushBytes}})
+ .then(function() {
+ if (deleteList.length > 0) {
+ return doRequest('POST', target, 'deletefiles', { appId: appId, appType: appType, json: {'paths': deleteList}})
+ }
+ })
+ .then(function pushNextFile() {
+ if (pushList.length === 0) {
+ console.log('Push complete.');
+ return;
+ }
+ var curPushEntry = pushList.shift();
+ return doRequest('PUT', target, '/putfile', {
+ appId: appId,
+ appType: appType,
+ body: fs.readFileSync(curPushEntry.realPath),
+ query: {
+ path: curPushEntry.path,
+ etag: curPushEntry.etag
+ }
+ }).then(pushNextFile);
+ });
}
- });
+}
+
+function zipDir(dir, zip) {
+ var contents = fs.readdirSync(dir);
+ contents.forEach(function(f) {
+ var fullPath = path.join(dir, f);
+ if (!IGNORE_PATH_FOR_PUSH_REGEXP.exec(fullPath)) {
+ if (fs.statSync(fullPath).isDirectory()) {
+ var inner = zip.folder(f);
+ zipDir(path.join(dir, f), inner);
+ } else {
+ zip.file(f, fs.readFileSync(fullPath, 'binary'), { binary: true });
+ }
+ }
+ });
+}
+
+function doZipPush(target, appId, appType, dir, configXmlPath) {
+ var zip = new JSZip();
+ zipDir(dir, zip.folder('www'));
+ zip.file('config.xml', fs.readFileSync(configXmlPath, 'binary'), { binary: true });
+ var newAssetManifest = buildAssetManifest(dir, configXmlPath);
+ zip.file('zipassetmanifest.json', JSON.stringify(newAssetManifest));
+ var zipData = new Buffer(zip.generate({ type: 'base64' }), 'base64');
+ return doRequest('POST', target, '/zippush', {appId:appId, appType: appType, body: zipData});
}
function doRequest(method, target, action, options) {
- var ret = Q.defer();
- var targetParts = target.split(':');
- var host = targetParts[0];
- var port = +(targetParts[1] || 2424);
- options = options || {};
-
- var queryParams = {};
- if (options.query) {
- Object.keys(options.query).forEach(function(k) {
- queryParams[k] = options.query[k];
+ var ret = Q.defer();
+ var targetParts = target.split(':');
+ var host = targetParts[0];
+ var port = +(targetParts[1] || 2424);
+ options = options || {};
+
+ var queryParams = {};
+ if (options.query) {
+ Object.keys(options.query).forEach(function(k) {
+ queryParams[k] = options.query[k];
+ });
+ }
+ if (options.appId) {
+ queryParams['appId'] = options.appId;
+ }
+ if (options.appType) {
+ queryParams['appType'] = options.appType;
+ }
+
+ // Send the HTTP request. crxContents is a Node Buffer, which is the payload.
+ // Prepare the form data for upload.
+ var uri = url.format({
+ protocol: 'http',
+ hostname: host,
+ port: port,
+ pathname: action,
+ query: queryParams
});
- }
- if (options.appId) {
- queryParams['appId'] = options.appId;
- }
- if (options.appType) {
- queryParams['appType'] = options.appType;
- }
-
- // Send the HTTP request. crxContents is a Node Buffer, which is the payload.
- // Prepare the form data for upload.
- var uri = url.format({
- protocol: 'http',
- hostname: host,
- port: port,
- pathname: action,
- query: queryParams
- });
-
- process.stdout.write(method + ' ' + uri);
-
- var headers = {};
- if (options.json) {
- options.body = JSON.stringify(options.json);
- headers['Content-Type'] = 'application/json';
- }
-
- function statusCheck(err, res, body) {
- if (err) {
- err.statusCode = res && res.statusCode;
- } else if (res) {
- process.stdout.write(' ==> ' + (res && res.statusCode) + '\n');
- if (res.statusCode != 200) {
- err = new Error('Server returned status code: ' + res.statusCode);
- } else if (options.expectJson) {
- try {
- body = JSON.parse(body);
- } catch (e) {
- err = new Error('Invalid JSON: ' + body.slice(500));
+
+ process.stdout.write(method + ' ' + uri);
+
+ var headers = {};
+ if (options.json) {
+ options.body = JSON.stringify(options.json);
+ headers['Content-Type'] = 'application/json';
+ }
+
+ function statusCheck(err, res, body) {
+ if (err) {
+ err.statusCode = res && res.statusCode;
+ } else if (res) {
+ process.stdout.write(' ==> ' + (res && res.statusCode) + '\n');
+ if (res.statusCode != 200) {
+ err = new Error('Server returned status code: ' + res.statusCode);
+ } else if (options.expectJson) {
+ try {
+ body = JSON.parse(body);
+ } catch (e) {
+ err = new Error('Invalid JSON: ' + body.slice(500));
+ }
+ }
+ }
+ if (err) {
+ ret.reject(err);
+ } else {
+ ret.resolve({res:res, body:body});
}
- }
}
- if (err) {
- ret.reject(err);
- } else {
- ret.resolve({res:res, body:body});
+ var req = request({
+ uri: uri,
+ headers: headers,
+ method: method,
+ body: options.body
+ }, statusCheck);
+
+ if (options.form) {
+ var f = options.form;
+ req.form().append(f.key, f.formBody, { filename: f.filename, contentType: f.contentType });
}
- }
- var req = request({
- uri: uri,
- headers: headers,
- method: method,
- body: options.body
- }, statusCheck);
-
- if (options.form) {
- var f = options.form;
- req.form().append(f.key, f.formBody, { filename: f.filename, contentType: f.contentType });
- }
- return ret.promise;
+ return ret.promise;
};
exports.info = function(target) {
- return doRequest('GET', target, '/info', { expectJson: true });
+ return doRequest('GET', target, '/info', { expectJson: true });
};
exports.assetmanifest = function(target, appId) {
- return doRequest('GET', target, '/assetmanifest', {expectJson: true, appId: appId});
+ return doRequest('GET', target, '/assetmanifest', {expectJson: true, appId: appId});
};
exports.menu = function(target) {
- return doRequest('POST', target, '/menu');
+ return doRequest('POST', target, '/menu');
};
exports.eval = function(target, someJs) {
- return doRequest('POST', target, '/exec', { query: {code: someJs} });
+ return doRequest('POST', target, '/exec', { query: {code: someJs} });
};
exports.launch = function(target, appId) {
- return doRequest('POST', target, '/launch', { appId: appId});
+ return doRequest('POST', target, '/launch', { appId: appId});
};
exports.deleteAllApps = function(target) {
- return doRequest('POST', target, '/deleteapp', { query: {'all': 1} });
+ return doRequest('POST', target, '/deleteapp', { query: {'all': 1} });
};
exports.deleteApp = function(target, appId) {
- return doRequest('POST', target, '/deleteApp', { appId: appId});
+ return doRequest('POST', target, '/deleteApp', { appId: appId});
};
function parseArgs(argv) {
var opts = {
- 'help': Boolean,
- 'target': String
+ 'help': Boolean,
+ 'target': String
};
var ret = nopt(opts, null, argv);
if (!ret.target) {
- ret.target = 'localhost:2424';
+ ret.target = 'localhost:2424';
}
return ret;
}
function usage() {
- console.log('Usage: harness-push push path/to/chrome_app --target=IP_ADDRESS:PORT');
- console.log('Usage: harness-push menu');
- console.log('Usage: harness-push eval "alert(1)"');
- console.log('Usage: harness-push info');
- console.log('Usage: harness-push launch [appId]');
- console.log();
- console.log('--target defaults to localhost:2424');
- console.log('To deploy to Android over USB, use: adb forward tcp:2424 tcp:2424');
- process.exit(1);
+ console.log('Usage: harness-push push path/to/chrome_app --target=IP_ADDRESS:PORT');
+ console.log('Usage: harness-push menu');
+ console.log('Usage: harness-push eval "alert(1)"');
+ console.log('Usage: harness-push info');
+ console.log('Usage: harness-push launch [appId]');
+ console.log();
+ console.log('--target defaults to localhost:2424');
+ console.log('To deploy to Android over USB, use: adb forward tcp:2424 tcp:2424');
+ process.exit(1);
}
function main() {
- var args = parseArgs(process.argv);
-
- function onFailure(err) {
- console.error(err);
- }
- function onSuccess(result) {
- if (typeof result.body == 'object') {
- console.log(JSON.stringify(result.body, null, 4));
- } else if (result.body) {
- console.log(result.body);
- }
- }
+ var args = parseArgs(process.argv);
- var cmd = args.argv.remain[0];
- if (cmd == 'push') {
- if (!args.argv.remain[1]) {
- usage();
+ function onFailure(err) {
+ console.error(err);
}
- exports.push(args.target, args.argv.remain[1], args.pretend).then(onSuccess, onFailure);
- } else if (cmd == 'deleteall') {
- exports.deleteAllApps(args.target);
- } else if (cmd == 'delete') {
- if (!args.argv.remain[1]) {
- usage();
+ function onSuccess(result) {
+ if (typeof result.body == 'object') {
+ console.log(JSON.stringify(result.body, null, 4));
+ } else if (result.body) {
+ console.log(result.body);
+ }
}
- exports.deleteApp(args.target).then(onSuccess, onFailure);
- } else if (cmd == 'menu') {
- exports.menu(args.target).then(onSuccess, onFailure);
- } else if (cmd == 'eval') {
- if (!args.argv.remain[1]) {
- usage();
+
+ var cmd = args.argv.remain[0];
+ if (cmd == 'push') {
+ if (!args.argv.remain[1]) {
+ usage();
+ }
+ exports.push(args.target, args.argv.remain[1], args.pretend).then(onSuccess, onFailure);
+ } else if (cmd == 'deleteall') {
+ exports.deleteAllApps(args.target);
+ } else if (cmd == 'delete') {
+ if (!args.argv.remain[1]) {
+ usage();
+ }
+ exports.deleteApp(args.target).then(onSuccess, onFailure);
+ } else if (cmd == 'menu') {
+ exports.menu(args.target).then(onSuccess, onFailure);
+ } else if (cmd == 'eval') {
+ if (!args.argv.remain[1]) {
+ usage();
+ }
+ exports.eval(args.target, args.argv.remain[1]).then(onSuccess, onFailure);
+ } else if (cmd == 'assetmanifest') {
+ exports.assetmanifest(args.target, args.appid).then(onSuccess, onFailure);
+ } else if (cmd == 'info') {
+ exports.info(args.target).then(onSuccess, onFailure);
+ } else if (cmd == 'launch') {
+ exports.launch(args.target, args.argv.remain[1]).then(onSuccess, onFailure);
+ } else {
+ usage();
}
- exports.eval(args.target, args.argv.remain[1]).then(onSuccess, onFailure);
- } else if (cmd == 'assetmanifest') {
- exports.assetmanifest(args.target, args.appid).then(onSuccess, onFailure);
- } else if (cmd == 'info') {
- exports.info(args.target).then(onSuccess, onFailure);
- } else if (cmd == 'launch') {
- exports.launch(args.target, args.argv.remain[1]).then(onSuccess, onFailure);
- } else {
- usage();
- }
}
if (require.main === module) {
- main();
+ main();
}
http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/4a366039/harness-push/package.json
----------------------------------------------------------------------
diff --git a/harness-push/package.json b/harness-push/package.json
index 4357bd4..e58b848 100644
--- a/harness-push/package.json
+++ b/harness-push/package.json
@@ -24,9 +24,10 @@
"harness-push": "./harness-push.js"
},
"dependencies": {
- "request": "~2.34",
+ "jszip": "~2.1",
"nopt": "~2.2",
- "shelljs": "0.1.x",
- "q": "~0.9"
+ "q": "~0.9",
+ "request": "~2.34",
+ "shelljs": "0.1.x"
}
}
http://git-wip-us.apache.org/repos/asf/cordova-app-harness/blob/4a366039/www/cdvah/js/HarnessServer.js
----------------------------------------------------------------------
diff --git a/www/cdvah/js/HarnessServer.js b/www/cdvah/js/HarnessServer.js
index a596ab4..51a5c59 100644
--- a/www/cdvah/js/HarnessServer.js
+++ b/www/cdvah/js/HarnessServer.js
@@ -53,6 +53,7 @@
myApp.factory('HarnessServer', ['$q', 'HttpServer', 'ResourcesLoader', 'AppHarnessUI', 'AppsService', 'notifier', function($q, HttpServer, ResourcesLoader, AppHarnessUI, AppsService, notifier) {
+ var PROTOCOL_VER = 2;
var server = null;
var listenAddress = null;
@@ -130,7 +131,10 @@
return null;
}).then(function(assetManifest) {
resp.sendJsonResponse({
- 'assetManifest': assetManifest
+ 'assetManifest': assetManifest,
+ 'platform': cordova.platformId,
+ 'cordovaVer': cordova.version,
+ 'protocolVer': PROTOCOL_VER,
});
});
}
@@ -198,35 +202,10 @@
var tmpUrl = ResourcesLoader.createTmpFileUrl();
return pipeRequestToFile(req, tmpUrl)
.then(function() {
- var ret = $q.when();
- if (path == 'www/cordova_plugins.js') {
- path = 'orig-cordova_plugins.js';
- }
- if (path == 'www/config.xml') {
- ret = ret.then(function() {
- return ResourcesLoader.downloadFromUrl(tmpUrl, tmpUrl + '-2');
- });
- }
- ret = ret.then(function() {
- return app.directoryManager.addFile(tmpUrl, path, etag);
- });
- if (path == 'www/config.xml') {
- ret = ret.then(function() {
- return app.directoryManager.addFile(tmpUrl + '-2', 'config.xml', etag);
- });
- }
- if (path == 'config.xml' || path == 'www/config.xml') {
- ret = ret.then(function() {
- return app.readConfigXml();
- });
- } else if (path == 'orig-cordova_plugins.js') {
- ret = ret.then(function() {
- return app.readCordovaPluginsFile();
- });
- }
- return ret;
+ return importFile(tmpUrl, path, app, etag);
})
.then(function() {
+ // TODO: Add a timeout that resets updatingStatus if no more requests come in.
app.updateBytesSoFar += +req.headers['content-length'];
app.updatingStatus = app.updateBytesTotal / app.updateBytesSoFar;
if (app.updatingStatus === 1) {
@@ -239,11 +218,71 @@
});
}
+ function importFile(fileUrl, destPath, app, etag) {
+ console.log('Adding file: ' + destPath);
+ var ret = $q.when();
+ if (destPath == 'www/cordova_plugins.js') {
+ destPath = 'orig-cordova_plugins.js';
+ }
+ ret = ret.then(function() {
+ return app.directoryManager.addFile(fileUrl, destPath, etag);
+ });
+ if (destPath == 'config.xml') {
+ ret = ret.then(function() {
+ return app.readConfigXml();
+ });
+ } else if (destPath == 'orig-cordova_plugins.js') {
+ ret = ret.then(function() {
+ return app.readCordovaPluginsFile();
+ });
+ }
+ return ret;
+ }
+
+ function handleZipPush(req, resp) {
+ var appId = req.getQueryParam('appId');
+ var appType = req.getQueryParam('appType') || 'cordova';
+ return AppsService.getAppById(appId, appType)
+ .then(function(app) {
+ var tmpZipUrl = ResourcesLoader.createTmpFileUrl();
+ var tmpDirUrl = ResourcesLoader.createTmpFileUrl() + '/';
+ return pipeRequestToFile(req, tmpZipUrl)
+ .then(function() {
+ console.log('Extracting update zip');
+ return ResourcesLoader.extractZipFile(tmpZipUrl, tmpDirUrl);
+ })
+ .then(function() {
+ return ResourcesLoader.readJSONFileContents(tmpDirUrl + 'zipassetmanifest.json');
+ })
+ .then(function(zipAssetManifest) {
+ var keys = Object.keys(zipAssetManifest);
+ return $q.when()
+ .then(function next() {
+ var k = keys.shift();
+ if (k) {
+ return importFile(tmpDirUrl + k, k, app, zipAssetManifest[k]['etag'])
+ .then(next);
+ }
+ });
+ })
+ .then(function() {
+ app.lastUpdated = new Date();
+ notifier.success('Update complete.');
+ return resp.sendTextResponse(200, '');
+ })
+ .finally(function() {
+ app.updatingStatus = null;
+ ResourcesLoader.delete(tmpZipUrl);
+ ResourcesLoader.delete(tmpDirUrl);
+ });
+ });
+ }
+
function handleInfo(req, resp) {
var json = {
'platform': cordova.platformId,
'cordovaVer': cordova.version,
- 'protocolVer': 2,
+ 'protocolVer': PROTOCOL_VER,
'userAgent': navigator.userAgent,
'appList': AppsService.getAppListAsJson()
};
@@ -262,8 +301,9 @@
.addRoute('/assetmanifest', ensureMethodDecorator('GET', handleAssetManifest))
.addRoute('/prepupdate', ensureMethodDecorator('POST', handlePrepUpdate))
.addRoute('/deletefiles', ensureMethodDecorator('POST', handleDeleteFiles))
- .addRoute('/deleteapp', ensureMethodDecorator('POST', handleDeleteApp))
- .addRoute('/putfile', ensureMethodDecorator('PUT', handlePutFile));
+ .addRoute('/putfile', ensureMethodDecorator('PUT', handlePutFile))
+ .addRoute('/zippush', ensureMethodDecorator('POST', handleZipPush))
+ .addRoute('/deleteapp', ensureMethodDecorator('POST', handleDeleteApp));
return server.start();
}