@webos-tools/cli
Version:
Command Line Interface for development webOS application and service
348 lines (304 loc) • 11.5 kB
JavaScript
/*
* Copyright (c) 2020-2024 LG Electronics Inc.
*
* SPDX-License-Identifier: Apache-2.0
*/
const async = require('async'),
nopt = require('nopt'),
log = require('npmlog'),
path = require('path'),
packageLib = require('../lib/package'),
commonTools = require('./../lib/base/common-tools'),
errHndl = require('./../lib/base/error-handler');
const cliControl = commonTools.cliControl,
version = commonTools.version,
help = commonTools.help,
appdata = commonTools.appdata;
const processName = path.basename(process.argv[1]).replace(/.js/, '');
process.on('uncaughtException', function(err) {
log.error('uncaughtException', err.toString());
log.verbose('uncaughtException', err.stack);
cliControl.end(-1);
});
if (process.argv.length === 2) {
process.argv.splice(2, 0, '--help');
}
function PalmPackage() {
this.destination = '.';
this.options = {};
this.appCnt = 0;
this.dirCounts = {};
const knownOpts = {
"help": Boolean,
"hidden-help": Boolean,
"version": Boolean,
"level": ['silly', 'verbose', 'info', 'http', 'warn', 'error'],
"outdir": path,
"check": Boolean,
"no-minify": Boolean,
"app-exclude": [String, Array],
"rom": Boolean,
"encrypt": Boolean,
"sign": String,
"certificate": String,
"force": Boolean,
"pkgid": String,
"pkgversion": String,
"pkginfofile": String,
"info": String,
"info-detail": String,
"remainplainipk": Boolean
};
const shortHands = {
"h": "--help",
"hh": "--hidden-help",
"V": "--version",
"o": "--outdir",
"c": "--check",
"n": "--no-minify",
"e": "--app-exclude",
"r": "--rom",
"enc": "--encrypt",
"s": "--sign",
"crt": "--certificate",
"f": "--force",
"pi": "--pkgid",
"pv": "--pkgversion",
"pf": "--pkginfofile",
"i": "--info",
"I": "--info-detail",
"v": ["--level", "verbose"],
"rpi": "--remainplainipk"
};
this.argv = nopt(knownOpts, shortHands, process.argv, 2 /* drop 'node' & basename*/);
this.hiddenhelpString = [
"",
"EXTRA-OPTION",
help.format("-f, --force", "Make .ipk package forcibly with same file structure in APP_DIR"),
help.format("","If file/directories in APP_DIR consists of the following structure"),
help.format("\t (ex) APP_DIR/"),
help.format("\t +-- usr/"),
help.format("\t +-- usr/bin"),
help.format("\t +-- usr/bin/foo"),
help.format("\t +-- etc/"),
help.format("\t +-- etc/boo.conf"),
help.format("","'-f, --force' option will keep this structure in .ipk"),
"",
help.format("-pn, --pkgname <NAME>", "Set package name"),
help.format("-pv, --pkgversion <VERSION>", "Set package version"),
"EXAMPLES",
"",
"# Create a package although directory has no appinfo.json and no services.json",
" make a ipk file which of package name is 'foopkg' and package version is '1.0.1'",
" the following command should generate a foopkg_1.0.1.ipk",
processName+" APP_DIR -f -pn foopkg -pv 1.0.1",
""
];
log.heading = processName;
log.level = this.argv.level || 'warn';
log.verbose("argv", this.argv);
/**
* For consistent of "$command -v", argv is used.
* By nopt, argv is parsed and set key-value in argv object.
* If -v or --level option is input with command, it is set key-value in argv.
* After it is deleted, If remained key is only one in argv object
* (If any other are remained, it's mean another options is input)
* and there is no remaining after parsing the input command by nopt
* (If any other are remained, it's mean another parameters ares input),
* each command of webOS CLI print help message with log message.
*/
if (this.argv.level) {
delete this.argv.level;
if (this.argv.argv.remain.length === 0 && (Object.keys(this.argv)).length === 1) {
this.argv.help = true;
}
}
}
PalmPackage.prototype = {
unsupportedOptions: {
"noclean": 1, // Do not cleanup temporary directories - For debug only
"force": 1
},
showUsage: function(hiddenFlag, exitCode) {
if (exitCode === undefined) {
exitCode = 0;
}
if (hiddenFlag) {
help.display(processName, appdata.getConfig(true).profile, hiddenFlag);
} else {
help.display(processName, appdata.getConfig(true).profile);
}
cliControl.end(exitCode);
},
checkAndShowHelp: function() {
if (this.argv.help) {
this.showUsage(false, 0);
} else if (this.argv["hidden-help"]) {
this.showUsage(true, 0);
}
},
handleOptions: function() {
this.options.level = log.level;
// Pass unsupported options verbatim thru the options Object -- TODO: TBR
for (const key in this.argv) {
if (this.unsupportedOptions[key]) {
this.options[key] = this.argv[key];
}
}
const lowerCaseKeyArgv = {};
for (const key in this.argv) {
lowerCaseKeyArgv[key.toLocaleLowerCase()] = this.argv[key];
}
if (Object.hasOwnProperty.call(this.argv, 'minify')) {
this.options.minify = this.argv.minify;
} else {
this.options.minify = true;
}
if (Object.hasOwnProperty.call(this.argv, 'app-exclude')) {
this.options.excludefiles = this.argv['app-exclude'];
}
if (Object.hasOwnProperty.call(this.argv, 'rom')) {
this.options.rom = this.argv.rom;
} else {
this.options.rom = false;
}
if (Object.hasOwnProperty.call(this.argv, 'encrypt')) {
this.options.encrypt = this.argv.encrypt;
} else {
this.options.encrypt = false;
}
if (Object.hasOwnProperty.call(this.argv, 'sign')) {
this.options.sign = this.argv.sign;
}
if (Object.hasOwnProperty.call(this.argv, 'certificate')) {
this.options.certificate = this.argv.certificate;
}
if (Object.hasOwnProperty.call(this.argv, 'pkgid')) {
this.options.pkgid = this.argv.pkgid;
}
if (Object.hasOwnProperty.call(this.argv, 'pkgversion')) {
this.options.pkgversion = this.argv.pkgversion;
}
if (Object.hasOwnProperty.call(this.argv, 'pkginfofile')) {
this.options.pkginfofile = this.argv.pkginfofile;
}
if (Object.hasOwnProperty.call(this.argv, 'info-detail')) {
this.options.infodetail = this.argv['info-detail'];
}
if (Object.hasOwnProperty.call(this.argv, 'info')) {
this.options.info = this.argv.info;
}
if (Object.hasOwnProperty.call(lowerCaseKeyArgv, 'remainplainipk')) {
this.options.remainPlainIPK = lowerCaseKeyArgv.remainplainipk;
}
},
setOutputDir: function(next) {
log.info("setOutputDir()");
if (this.argv.outdir) {
this.destination = this.argv.outdir;
}
if (this.destination === '.') {
this.destination = process.cwd();
}
next();
},
checkInputDir: function(next) {
log.info("checkInputDir()");
const packager = new packageLib.Packager();
this.dirCounts = packager.checkInputDirectories(this.argv.argv.remain, this.options, next);
},
packageApp: function(next) {
log.info("packageApp()");
const packager = new packageLib.Packager();
if (this.dirCounts.app === 0) { // packaging without app
if (Object.prototype.hasOwnProperty.call(this.options, 'pkginfofile') && Object.prototype.hasOwnProperty.call(this.options, 'pkgid')) {
this.finish(errHndl.getErrMsg("NOT_USE_WITH_OPTIONS", "pkginfofile, pkgid"));
cliControl.end(-1);
} else if (Object.prototype.hasOwnProperty.call(this.options, 'pkgid') || Object.prototype.hasOwnProperty.call(this.options, 'pkginfofile')) {
if (this.dirCounts.resource > 0) {
packager.resourcePackaging(this.argv.argv.remain, this.destination, this.options, this.outputTxt, next);
} else {
packager.servicePackaging(this.argv.argv.remain, this.destination, this.options, this.outputTxt, next);
}
} else {
this.finish(errHndl.getErrMsg("USE_PKGID_PKGINFO"));
cliControl.end(-1);
}
} else { // packaging with app
if (Object.prototype.hasOwnProperty.call(this.options, 'pkgversion')) {
this.finish(errHndl.getErrMsg("NOT_PACKAGE_WITH_OPTION", "pkgversion"));
}
packager.generatePackage(this.argv.argv.remain, this.destination, this.options, this.outputTxt, next);
}
},
packageProject: function() {
async.series([
version.checkNodeVersion,
this.setOutputDir.bind(this),
this.checkInputDir.bind(this),
this.packageApp.bind(this)
], this.finish.bind(this));
},
checkApplication: function() {
async.series([
version.checkNodeVersion,
this.checkInputDir.bind(this)
], function(err) {
if (err) {
return this.finish(err);
}
return this.finish(null, {msg: "no problems detected"});
}.bind(this));
},
showPkgInfo: function() {
log.info("showPkgInfo()");
const packager = new packageLib.Packager();
packager.analyzeIPK(this.options, this.finish);
},
finish: function(err, value) {
log.info("finish()");
if (err) {
// handle err from getErrMsg()
if (Array.isArray(err) && err.length > 0) {
for (const index in err) {
log.error(err[index].heading, err[index].message);
}
log.verbose(err[0].stack);
} else {
// handle general err (string & object)
log.error(err.toString());
log.verbose(err.stack);
}
cliControl.end(-1);
} else {
log.verbose("finish()", "value:", value);
if (value && value[value.length-1] && value[value.length-1].msg) {
console.log(value[value.length-1].msg);
} else if (value && value.msg) {
console.log(value.msg);
}
cliControl.end();
}
},
outputTxt: function(value) {
log.info("outputTxt()");
console.log(value);
},
exec: function() {
this.handleOptions();
this.checkAndShowHelp();
if (this.argv.check) {
this.checkApplication();
} else if (this.argv.version) {
version.showVersionAndExit();
} else if (this.argv.info || this.argv['info-detail']) {
this.showPkgInfo();
} else {
this.packageProject();
}
}
};
// Main
const cmd = new PalmPackage();
cmd.exec();