Publish Vulpes Preview 15 sources for desktop, Android and Tundra

Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
This commit is contained in:
OverSu 2026-09-30 23:13:03 +02:00
commit b38dea0597
25901 changed files with 9982925 additions and 369 deletions

326
gaia/build/svoperapps.js Executable file
View file

@ -0,0 +1,326 @@
'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);
};