Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
326 lines
10 KiB
JavaScript
Executable file
326 lines
10 KiB
JavaScript
Executable file
'use strict';
|
|
|
|
var utils = require('./utils');
|
|
|
|
var SvoperappsBuilder = function() {
|
|
};
|
|
|
|
SvoperappsBuilder.prototype.STAGE_SVOPERAPPS_FOLDER = '__svoperapps';
|
|
SvoperappsBuilder.prototype.APPS_DATA_FOLDER = 'cached';
|
|
SvoperappsBuilder.prototype.APPS_CONF_FOLDER = 'conf';
|
|
SvoperappsBuilder.prototype.PROFILE_APPS_FOLDER = 'svoperapps';
|
|
SvoperappsBuilder.prototype.APPS_CONF_FILENAME = 'singlevariantconf.json';
|
|
SvoperappsBuilder.prototype.DEFAULT_EMPTY_SCREENS = 2;
|
|
SvoperappsBuilder.prototype.MAX_ICONS_PER_PAGE = 4 * 4;
|
|
|
|
SvoperappsBuilder.prototype.errorObject = null;
|
|
|
|
/*
|
|
* the count of current issued and unprocessed app requests.
|
|
*/
|
|
SvoperappsBuilder.prototype.appRequest = 0;
|
|
|
|
/*
|
|
* check and cut the path parts out from url
|
|
* if the url does not conform to {scheme}://{net_loc}/, this function returns
|
|
* null.
|
|
*/
|
|
SvoperappsBuilder.prototype.getOrigin = function(url, defaultOrigin) {
|
|
if (!url) {
|
|
return null;
|
|
} else {
|
|
var matched = url.match(/^([a-zA-Z]+:\/\/.[^/]+).*/);
|
|
return (matched && matched.length > 1) ? matched[1] : defaultOrigin;
|
|
}
|
|
};
|
|
|
|
/*
|
|
* check if the downloaded app folder contains the following files:
|
|
* 1. metadata.json, 2. update.webapp, 3. application.zip, 4. manifest.webapp.
|
|
*/
|
|
SvoperappsBuilder.prototype.checkDownloadedApp = function(path) {
|
|
var files = utils.listFiles(path, utils.FILE_TYPE_FILE, false);
|
|
var metadataFound = false;
|
|
var updateFound = false;
|
|
var applicationFound = false;
|
|
var manifestFound = false;
|
|
|
|
for (var i = 0; i < files.length; i++) {
|
|
if (files[i].match(/[\/\\]metadata\.json$/)) {
|
|
metadataFound = true;
|
|
} else if (files[i].match(/[\/\\]update\.webapp$/)) {
|
|
updateFound = true;
|
|
} else if (files[i].match(/[\/\\]application\.zip$/)) {
|
|
applicationFound = true;
|
|
} else if (files[i].match(/[\/\\]manifest\.webapp$/)) {
|
|
manifestFound = true;
|
|
}
|
|
}
|
|
return metadataFound && ((updateFound && applicationFound )|| manifestFound);
|
|
};
|
|
|
|
/*
|
|
* Download the packaged app from manifest.package_path to cache path.
|
|
*/
|
|
SvoperappsBuilder.prototype.downloadPackageApp =
|
|
function(manifest, metadata, appPath, cb, errCb) {
|
|
utils.log('svoperapps.js',
|
|
'download packaged app from: ' + manifest.package_path);
|
|
// write update.webapp
|
|
utils.writeContent(utils.getFile(appPath, 'update.webapp'),
|
|
JSON.stringify(manifest));
|
|
// write metadata.json
|
|
utils.writeContent(utils.getFile(appPath, 'metadata.json'),
|
|
JSON.stringify(metadata));
|
|
|
|
// download application
|
|
var origin = utils.getNewURI(metadata.manifestURL, null, null);
|
|
var absolutePkgPath = origin.resolve(manifest.package_path);
|
|
|
|
utils.download(absolutePkgPath, appPath + 'application.zip', cb,
|
|
function err(url, path) {
|
|
errCb(new Error('unable to download: ' + url));
|
|
});
|
|
};
|
|
|
|
SvoperappsBuilder.prototype.prepareHostedApp =
|
|
function(manifest, metadata, appPath, callback, errCallback) {
|
|
// Bug 936028 - appcache needs to be removed from manifest
|
|
if (manifest.appcache_path) {
|
|
delete manifest.appcache_path;
|
|
}
|
|
|
|
// write manifest
|
|
utils.writeContent(utils.getFile(appPath, 'manifest.webapp'),
|
|
JSON.stringify(manifest));
|
|
// write metadata
|
|
utils.writeContent(utils.getFile(appPath, 'metadata.json'),
|
|
JSON.stringify(metadata));
|
|
callback();
|
|
};
|
|
|
|
SvoperappsBuilder.prototype.downloadApp =
|
|
function(manifest, metadata, appPath, callback, errCallback) {
|
|
// Determine if application is hosted or packaged and save manifest
|
|
if (manifest.package_path) { // packaged app
|
|
this.downloadPackageApp(manifest, metadata, appPath, callback, errCallback);
|
|
} else if (metadata.origin) { // hosted app
|
|
this.prepareHostedApp(manifest, metadata, appPath, callback, errCallback);
|
|
} else { // error case
|
|
errCallback(new Error('installOrigin required for app ' + manifest.name +
|
|
' in local_apps.json configuration file'));
|
|
}
|
|
};
|
|
|
|
SvoperappsBuilder.prototype.processApp =
|
|
function(appId, manifestUrl, origin, installOrigin, appPath, cached, done) {
|
|
var self = this;
|
|
var destPath = utils.joinPath(this.profilePath, this.PROFILE_APPS_FOLDER);
|
|
// When everything is ready, we call this function to copy all files in
|
|
// appPath to profilePath.
|
|
function copyAppPathToProfile() {
|
|
try {
|
|
utils.copyFileTo(appPath, destPath, appId);
|
|
} catch (ex) {
|
|
utils.log('svoperapps.js', 'copy file error: ' + ex.message);
|
|
self.errorObject = ex;
|
|
} finally {
|
|
done();
|
|
}
|
|
}
|
|
|
|
function handleError() {
|
|
self.errorObject = new Error('failed to download manifest from ' +
|
|
manifestUrl);
|
|
done();
|
|
}
|
|
|
|
utils.log('svoperapps.js', 'downloading manifest: ' + manifestUrl);
|
|
utils.downloadJSON(manifestUrl, function mandifestHandler(json) {
|
|
if (!json) {
|
|
if (cached) {
|
|
// if json is not downloaded but we already had everything, just copy
|
|
// it.
|
|
copyAppPathToProfile();
|
|
} else {
|
|
handleError();
|
|
}
|
|
return;
|
|
}
|
|
// prepare the folders for this app
|
|
utils.mkdirs(appPath);
|
|
// if the app is cached, we need to compare the manifest file.
|
|
if (cached) {
|
|
// If we already have everything, we still need to compare the
|
|
// update/manifest with downloaded content. If they are identical, we
|
|
// can view them as the same. If not, we need to download and rebuild
|
|
// them.
|
|
var updatePath = utils.joinPath(appPath, 'update.webapp');
|
|
var manifestFile = utils.fileExists(updatePath) ?
|
|
'update.webapp' : 'manifest.webapp';
|
|
var fileJSON = utils.readJSONFromPath(utils.joinPath(appPath,
|
|
manifestFile));
|
|
|
|
// Bug 936028 - Comparation has to be done removing appcache + "\n");
|
|
if (json.appcache_path) {
|
|
delete json.appcache_path;
|
|
}
|
|
|
|
if (JSON.stringify(json) === JSON.stringify(fileJSON)) {
|
|
copyAppPathToProfile();
|
|
return;
|
|
}
|
|
}
|
|
|
|
try {
|
|
// if it is cached or the manifest file is not the same, we need to
|
|
// download that app.
|
|
var metadata = {
|
|
'id': appId,
|
|
'installOrigin': installOrigin,
|
|
'manifestURL': manifestUrl
|
|
};
|
|
metadata.origin = json.package_path ?
|
|
self.getOrigin(json.package_path, installOrigin) :
|
|
(origin || self.getOrigin(json.launch_path));
|
|
self.downloadApp(json, metadata, appPath,
|
|
copyAppPathToProfile, handleError);
|
|
} catch (ex) {
|
|
// error found
|
|
self.errorObject = ex;
|
|
done();
|
|
}
|
|
});
|
|
};
|
|
|
|
SvoperappsBuilder.prototype.fetchApps = function(done) {
|
|
utils.log('svoperapps.js', 'fetch apps');
|
|
var apps = this.variant.apps;
|
|
var distPath = utils.joinPath(this.svoperappsStagePath,
|
|
this.APPS_DATA_FOLDER);
|
|
var cachedApps = utils.listFiles(distPath, utils.FILE_TYPE_DIRECTORY, false);
|
|
var self = this;
|
|
|
|
function checkApp(cachedApp) {
|
|
if (cachedApp.match(new RegExp('[\\\/]' + app + '$'))) {
|
|
cachedApps.splice(cachedApps.indexOf(cachedApp), 1);
|
|
//compare everything.
|
|
if (self.checkDownloadedApp(appPath)) {
|
|
utils.log('svoperapps.js', app + ' is cached.');
|
|
// path exists and everything is ok, make it as cached
|
|
appCached = true;
|
|
} else {
|
|
// path exists but some files are missing, mark it as not cached.
|
|
appCached = false;
|
|
utils.deleteFile(appPath, true);
|
|
}
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function processAppCallback() {
|
|
self.appRequest--;
|
|
if (self.appRequest === 0) {
|
|
// all apps are processed, clean all left cached apps, those are not
|
|
// found with the app list.
|
|
for (var i = 0; i < cachedApps.length; i++) {
|
|
utils.log('svoperapps.js', 'remove useless cache: ' + cachedApps[i]);
|
|
utils.deleteFile(cachedApps[i], true);
|
|
}
|
|
done();
|
|
}
|
|
}
|
|
|
|
for (var app in apps) {
|
|
var appPath = utils.joinPath(distPath, app);
|
|
var appCached = false;
|
|
// check if the appPath already exists in cachedApp list
|
|
cachedApps.every(checkApp);
|
|
var origin = apps[app].origin || null;
|
|
var installOrigin = apps[app].installOrigin || null;
|
|
|
|
this.appRequest++;
|
|
this.processApp(app, apps[app].manifestURL, origin, installOrigin,
|
|
appPath, appCached, processAppCallback);
|
|
}
|
|
|
|
if (Object.keys(apps).length === 0) {
|
|
done();
|
|
}
|
|
};
|
|
|
|
SvoperappsBuilder.prototype.prepareDir = function() {
|
|
var profilePath = this.options.PROFILE_DIR;
|
|
var distributionPath = this.options.GAIA_DISTRIBUTION_DIR;
|
|
var svoperappsStagePath = utils.joinPath(this.options.GAIA_DIR, 'build_stage',
|
|
this.STAGE_SVOPERAPPS_FOLDER);
|
|
utils.mkdirs(svoperappsStagePath);
|
|
|
|
// create the temp folders.
|
|
utils.mkdirs(utils.joinPath(svoperappsStagePath, this.APPS_DATA_FOLDER));
|
|
|
|
// clear profile folder
|
|
utils.deleteFile(utils.joinPath(profilePath, this.PROFILE_APPS_FOLDER), true);
|
|
utils.mkdirs(utils.joinPath(profilePath, this.PROFILE_APPS_FOLDER));
|
|
|
|
this.profilePath = profilePath;
|
|
this.distributionPath = distributionPath;
|
|
this.svoperappsStagePath = svoperappsStagePath;
|
|
};
|
|
|
|
SvoperappsBuilder.prototype.setOptions = function(options) {
|
|
this.options = options;
|
|
this.variant = utils.readJSONFromPath(options.VARIANT_PATH);
|
|
};
|
|
|
|
SvoperappsBuilder.prototype.generateConfig = function() {
|
|
var file = utils.getFile(this.options.PROFILE_DIR, this.PROFILE_APPS_FOLDER,
|
|
this.APPS_CONF_FILENAME);
|
|
var config = {};
|
|
if (this.variant.operators) {
|
|
this.variant.operators.forEach(function(operator) {
|
|
var apps = operator.apps.map(function(app) {
|
|
return app.id;
|
|
});
|
|
operator['mcc-mnc'].forEach(function(mccmnc) {
|
|
config[mccmnc] = apps;
|
|
});
|
|
});
|
|
}
|
|
utils.writeContent(file, JSON.stringify(config));
|
|
};
|
|
|
|
SvoperappsBuilder.prototype.execute = function(options) {
|
|
if (!options.PROFILE_DIR || !options.GAIA_DISTRIBUTION_DIR ||
|
|
!options.VARIANT_PATH) {
|
|
utils.log('svoperapps.js', 'PROFILE_DIR, GAIA_DISTRIBUTION_DIR,' +
|
|
' VARIANT_PATH are all required');
|
|
return;
|
|
}
|
|
|
|
this.setOptions(options);
|
|
this.prepareDir();
|
|
this.generateConfig();
|
|
|
|
// set done flag and start everything.
|
|
var done = false;
|
|
// read variant.json
|
|
// download all apps
|
|
this.fetchApps(function() {
|
|
done = true;
|
|
});
|
|
|
|
// Ensure everything is finished before exit this module
|
|
utils.processEvents(function exitFunc() {
|
|
return {
|
|
'wait': !done,
|
|
'error': this.errorObject
|
|
};
|
|
}.bind(this));
|
|
};
|
|
|
|
exports.execute = function(options) {
|
|
(new SvoperappsBuilder()).execute(options);
|
|
};
|