diff --git a/.github/workflows/checkmarx-sast-scan-push.yml b/.github/workflows/checkmarx-sast-scan-push.yml new file mode 100644 index 0000000..06b8e36 --- /dev/null +++ b/.github/workflows/checkmarx-sast-scan-push.yml @@ -0,0 +1,55 @@ +# This workflow is to automate Checkmarx SAST scans. It runs on a push to the main branch. +# +# The following GitHub Secrets must be first defined: +# - CHECKMARX_URL +# - CHECKMARX_USER +# - CHECKMARX_PASSWORD +# - CHECKMARX_CLIENT_SECRET +# +# The following variables must be inserted below: +# - + +# +# Update the 'team' field to reflect the team name used in Checkmarx. +# +# For the full documentation, including a list of all inputs, please refer to the README https://github.com/checkmarx-ts/checkmarx-cxflow-github-action + +name: Checkmarx SAST Scan +on: + push: + branches: + - develop + +jobs: + build: + environment: Dev + runs-on: + - self-hosted + -label-1 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Checkmarx CxFlow Action + uses: checkmarx-ts/checkmarx-cxflow-github-action@v1.4 #Github Action version + with: + project: ${{ secrets.CHECKMARX_PROJECT }} # <-- Insert Checkmarx SAST Project Name + team: ${{ secrets.CHECKMARX_TEAMS }} + checkmarx_url: ${{ secrets.CHECKMARX_URL }} # To be stored in GitHub Secrets. + checkmarx_username: ${{ secrets.CHECKMARX_USER }} # To be stored in GitHub Secrets. + checkmarx_password: ${{ secrets.CHECKMARX_PASSWORD }} # To be stored in GitHub Secrets. + checkmarx_client_secret: ${{ secrets.CHECKMARX_CLIENT_SECRET }} # To be stored in GitHub Secrets. + #sca_api_url : ${{ secrets.SCA_API_URL }} # To be stored in GitHub Secrets. + #sca_app_url: ${{ secrets.SCA_APP_URL }} # To be stored in GitHub Secrets. + #sca_access_control_url: ${{ secrets.SCA_ACCESS_CONTROL_URL }} # To be stored in GitHub Secrets. + #sca_tenant: ${{ secrets.SCA_TENANT }} # To be stored in GitHub Secrets. + #sca_username: ${{ secrets.SCA_USERNAME }} # To be stored in GitHub Secrets. + #sca_password: ${{ secrets.SCA_PASSWORD }} # To be stored in GitHub Secrets. + break_build: false + scanners: sast + incremental: false + bug_tracker: Sarif + params: --namespace=${{ github.repository_owner }} --repo-name=${{ github.event.repository.name }} --branch=${{ github.ref }} --cx-flow.filterSeverity --cx-flow.filterCategory --checkmarx.disable-clubbing=true + - name: Upload SARIF file + uses: github/codeql-action/upload-sarif@v1 + with: + sarif_file: cx.sarif diff --git a/README.md b/README.md index 0e136f5..8e27ce2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ Run `npm` at the command line to check whether npm is installed. -2. *lithium-sdk* supports Node.js version => 4.8.4, 6.11.2 and 8.3.0. +2. *lithium-sdk* supports Node.js version => 4.8.4, 6.11.2, 8.3.0 and 10.24.1. 3. Install gulp by issuing the following statement in a command line: `npm install gulp -g`. diff --git a/bin/cmd/create-skin.js b/bin/cmd/create-skin.js index 5a2d997..a3e5514 100644 --- a/bin/cmd/create-skin.js +++ b/bin/cmd/create-skin.js @@ -7,23 +7,7 @@ var fs = require('fs'); module.exports = { run: function () { - //Success call back function - var cb = require('../../gulp/createskin')(gulp, gutil).createNewSkin; - - //Check if we are creating skin on a sdk project - if (!fs.existsSync('./server.conf.json')) { - console.log(gutil.colors.red('Please run create-skin under your Lithium SDK project directory.' + - '\n or create a new project with ' + gutil.colors.bold('li create-project') + ' before creating a new skin.')); - process.exit(1); - return true; - } - var server = require('../../lib/server.js')(gulp, gutil); - var versionCheck = require('../../lib/version-check.js')(gulp, gutil); - var serverVersion = versionCheck.createVersion("16.4"); - versionCheck.process(server, { - debugMode: gutil.env.debug, - version: serverVersion - }, cb); + require('../../lib/spawn-gulp')(['create-new-skin'].concat(process.argv.splice(3))); }, help: 'Creates a new skin.' + gutil.colors.bold('\nOptions:') + '\n--clearCore: Gets the most recent versions of parent skins from the server. Running without --clearCore is faster, '+ diff --git a/bin/li.js b/bin/li.js index ff5a782..168da24 100755 --- a/bin/li.js +++ b/bin/li.js @@ -4,6 +4,11 @@ var gutil = require('gulp-util'); var path = require('path'); var fs = require('fs'); +process.on('uncaughtException', err => { + console.error('li.js: uncaught error:\n', err); + process.exit(1); +}); + function addSpaces (word, length) { word = ' ' + word; while (word.length < length + 2) { @@ -31,6 +36,7 @@ if (cmds.indexOf(cmd) > -1) { require('./cmd/' + cmd).run(); } else { if (cmd != 'help') { + process.exitCode = 9; console.log(gutil.colors.red(gutil.colors.bold('Command not found: ' + cmd))); } console.log(gutil.colors.bold('\nUsage:') + "li command [--options]"); diff --git a/gulp/checkThemes.js b/gulp/checkThemes.js new file mode 100644 index 0000000..665631c --- /dev/null +++ b/gulp/checkThemes.js @@ -0,0 +1,28 @@ +'use strict'; + +module.exports = function (gulp, gutil) { + /** + * Checks if a theme skin is enabled + */ + + gulp.task('check-themes', ['version-check'], function(cb, errorCallback) { + var server = require('../lib/server.js')(gulp, gutil); + if (server.useLocalCompile()) { + var version = require('../lib/version-check')(gulp, gutil).getVersion(); + var serverVersionThemeCheck = require('../lib/server-version-theme-check.json'); + var versionPattern = /(\d+)\.(\d{0,2})*/i; + var matches = version.toString().match(versionPattern); + var versionOnServer = parseInt(matches[1]); + var minorVersionOnServer = parseInt(matches[2]); + if (versionOnServer >= serverVersionThemeCheck.supportedVersionMajor || + (versionOnServer == serverVersionThemeCheck.supportedVersionMajor && + minorVersionOnServer >= serverVersionThemeCheck.supportedVersionMinor)) { + require('../lib/check-themes.js')(gulp, gutil).process(server, { + debugMode: gutil.env.debug + }, cb, errorCallback); + } else { + return cb(); + } + } + }); +}; diff --git a/gulp/clean.js b/gulp/clean.js index b93f4e1..20279b9 100644 --- a/gulp/clean.js +++ b/gulp/clean.js @@ -44,6 +44,7 @@ module.exports = function (gulp, gutil) { }); }); }).catch(err => { + process.exitCode = 1; gutil.log(gutil.colors.red(err)); }); }); diff --git a/gulp/createskin.js b/gulp/createskin.js index c26f005..9d4b934 100644 --- a/gulp/createskin.js +++ b/gulp/createskin.js @@ -8,6 +8,7 @@ var through = lazyReq('through2'); var putils = require('../lib/plugin-utils'); module.exports = function (gulp, gutil) { + var skinLib = require('../lib/skin')(gulp, gutil); /** * This function should match all validations for studio skin creation. As of 16.4, studio skin creation does not @@ -32,6 +33,7 @@ module.exports = function (gulp, gutil) { */ function normalizeSkinId(value) { if (value === '') { + process.exitCode = 9; throw Error('Bad input value'); } return value.trim().replace(/ /g, '_').toLowerCase(); @@ -77,7 +79,6 @@ module.exports = function (gulp, gutil) { * skin directory structure under skin base dir of sdk project. */ function createNewSkin() { - var skinLib = require('../lib/skin')(gulp, gutil); var putils = require('../lib/plugin-utils'); var options = { @@ -125,6 +126,11 @@ module.exports = function (gulp, gutil) { choices: function(repo) { var otherCommunitySkins = skinLib.getOtherCommunitySkins(); + var version = require('../lib/version-check')(gulp, gutil).getVersion(); + skinLib.setLiaVersion(version); + var isThemeEnabled = require('../lib/check-themes')(gulp, gutil).getThemeEnabled(); + skinLib.setThemesMap(isThemeEnabled); + if (repo.isResponsive) { var communityResponsiveSkinIds = otherCommunitySkins.filter(function(skin) { return skin.isResponsive(); @@ -138,7 +144,7 @@ module.exports = function (gulp, gutil) { } else { //All core and community skins plus all sdk local skins are valid parents var communitySkinIds = otherCommunitySkins.filter(function(skin) { - return !skin.isResponsive(); + return !skin.isResponsive() && !skin.getIsTheme(); }).map(function(skin) { return skin.getId(); }); @@ -192,13 +198,23 @@ module.exports = function (gulp, gutil) { try { skinUtil.createNewSkin(options.skinInfo); } catch(err) { + process.exitCode = 1; options.skinInfo.errorCb(err); } }); } - gulp.task('create-new-skin', ['version-check'], function () { - return createNewSkin(); + gulp.task('create-new-skin', ['check-themes'], function () { + //Check if we are creating skin on a sdk project + if (!fs.existsSync('./server.conf.json')) { + console.log(gutil.colors.red('Please run create-skin under your Lithium SDK project directory.' + + '\n or create a new project with ' + gutil.colors.bold('li create-project') + ' before creating a new skin.')); + process.exit(1); + return null; + } + var stream = through().obj(); + downloadCorePlugins(); + return stream; }); return { diff --git a/gulp/includes.js b/gulp/includes.js index e717ff2..7e271d6 100644 --- a/gulp/includes.js +++ b/gulp/includes.js @@ -39,12 +39,14 @@ module.exports = function (req) { require('./local-server')(gulp, gutil); require('./skins')(gulp, gutil); require('./text')(gulp, gutil); + require('./checkThemes')(gulp, gutil); Object.getOwnPropertyNames(extensions).forEach(function (ext) { try { req(ext + '/gulp/includes')(gulp, gutil); } catch (err) { if (err.code !== 'MODULE_NOT_FOUND') { + process.exitCode = 1; throw err; } else { extensions[ext].forEach(function (task) { diff --git a/gulp/plugin.js b/gulp/plugin.js index 6ad095c..619b835 100644 --- a/gulp/plugin.js +++ b/gulp/plugin.js @@ -18,6 +18,7 @@ module.exports = function (gulp, gutil) { runSequence = runSequence.use(gulp); function handleRsyncError(e) { + process.exitCode = 1; gutil.log(gutil.colors.red('Failed to sync files: ' + e)); } @@ -169,5 +170,9 @@ module.exports = function (gulp, gutil) { 'skins' ]); - gulp.task('serve-sass', ['skins-compile', 'watch-res-sass', 'local-server']); + gulp.task('serve-sass', function(done) { + runSequence('skins-compile', 'watch-res-sass', 'local-server', function() { + done(); + }); + }); }; diff --git a/gulp/responsiveoptions.js b/gulp/responsiveoptions.js index 2857f25..c3cec64 100644 --- a/gulp/responsiveoptions.js +++ b/gulp/responsiveoptions.js @@ -40,7 +40,7 @@ module.exports = function (gulp, gutil) { return putils.validate(val, /^[0-9]+$/); } - gulp.task('responsive-options', ['version-check'], function () { + gulp.task('responsive-options', ['check-themes'], function () { var stream = through().obj(); var server = getPluginServer().getServer(); if ((gutil.env.force || server.force()) && !gutil.env.prompt) { @@ -49,16 +49,19 @@ module.exports = function (gulp, gutil) { var anon = gutil.env.anon; if (typeof skinId === 'undefined' || typeof port === 'undefined') { + process.exitCode = 9; throw new Error('must pass both --skin and --port parameters when using the --force flag'); } if (!validateSkinId(skinId)) { + process.exitCode = 9; throw new Error('the value passed for skin is not valid.'); } var portValidate = validatePort(port); if (portValidate !== true) { + process.exitCode = 9; throw new Error('port: ' + portValidate); } @@ -73,11 +76,20 @@ module.exports = function (gulp, gutil) { debugMode: gutil.env.debug, configDir: gutil.env.configdir || server.configDir() }, function(err) { - if (err) throw err; + if (err) { + process.exitCode = 1; + throw err; + } }).pipe(stream); } else { - var skins = getSkinLib().getResponsiveSkinIds(); + var version = require('../lib/version-check')(gulp, gutil).getVersion(); + var skinLibVar = getSkinLib(); + skinLibVar.setLiaVersion(version); + var isThemeEnabled = require('../lib/check-themes')(gulp, gutil).getThemeEnabled(); + skinLibVar.setThemesMap(isThemeEnabled); + var skins = skinLibVar.getResponsiveSkinIds(); if (skins.length < 1) { + process.exitCode = 1; throw new Error('There are no responsive skins. You should create a skin in the res/skins folder and make ' + 'its parent a responsive skin (such as the responsive_peak skin) by setting the parent property in the ' + 'skin.properties file'); @@ -112,7 +124,10 @@ module.exports = function (gulp, gutil) { debugMode: gutil.env.debug, configDir: gutil.env.configdir || server.configDir() }, function(err) { - if (err) throw err; + if (err) { + process.exitCode = 1; + throw err; + } }).pipe(stream); }); } diff --git a/gulp/skins.js b/gulp/skins.js index 686e223..739abff 100644 --- a/gulp/skins.js +++ b/gulp/skins.js @@ -5,8 +5,8 @@ module.exports = function (gulp, gutil) { gulp.task('skins', ['skins-vars-template', 'skins-compile']); - gulp.task('skins-compile', function (cb) { - skins.compile().then(cb); + gulp.task('skins-compile', ['check-themes'], function (cb) { + skins.compile().then(cb); }); gulp.task('skins-vars-template', function () { diff --git a/lib/check-themes.js b/lib/check-themes.js new file mode 100644 index 0000000..d5bcbf4 --- /dev/null +++ b/lib/check-themes.js @@ -0,0 +1,120 @@ +'use strict'; + +var request = require('request'); +var pluginUtils = require('../lib/plugin-utils'); +var putils = require('./plugin-utils'); +let themesEnabledMap = new Map(); +const themesArray = ['theme_base', 'theme_support', 'theme_hermes', 'theme_marketing']; +var themesCount = 0; + +module.exports = function (gulp, gutil) { + + function validate(serverUrl, pluginToken, opts, cb, errorCallback) { + var options = { + headers: { + Authorization: 'Bearer ' + pluginToken + }, + rejectUnauthorized: false + }; + + for(const theme of themesArray) { + var themeEnabledCheckUrl = pluginUtils.urlBldr(serverUrl + '/restapi/ldntool/plugins/checkThemeEnabled?format=json').query("themeId", theme).build(); + if (opts.debugMode) { + putils.logDebug(gutil, 'making theme enabled check call to ' + themeEnabledCheckUrl); + putils.logDebug(gutil, 'theme enabled check callOpts: ' + JSON.stringify(options)); + } + request(themeEnabledCheckUrl, options, function (error, response, body) { + if (opts.debugMode) { + putils.logDebug(gutil, 'response from theme enabled check: \n' + body); + } + if (error || response.statusCode > 201) { + process.exitCode = 1; + putils.logError(gutil, 'got an error when trying to make the call: \n' + error); + parseResponse(body, opts, errorCallback, theme); + return; + } + try { + var themeEnabledCheckResponse = parseResponse(body, opts, errorCallback, theme); + themesCount++; + + //On success invoke callback function + if (themesCount == 4 && typeof cb === 'function') { + return cb(); + } + } catch (error) { + process.exitCode = 1; + callbackOrThrowError(errorCallback, error.message); + } + + }); + } + } + + function process(server, opts, cb, errorCallback) { + if (server.serverUrl() === undefined) { + var errMessage = 'A server URL is required in your configuration. '; + callbackOrThrowError(errorCallback, errMessage); + } + validate(server.serverUrl(), server.pluginToken(), opts, cb, errorCallback); + } + + function callbackOrThrowError(cb, errorMsg) { + if (typeof cb === 'function') { + cb(new Error(errorMsg), null); + } else { + var error = new Error(errorMsg); + Error.captureStackTrace(error, callbackOrThrowError); + throw error; + } + } + + function parseResponse(body, opts, errorCallback, themeId) { + var errorMessage = 'Empty theme enabled check response'; + var badResponseErrMessage = 'Invalid response from server. Check your server url and version.'; + if ( !body ) { + process.exitCode = 1; + pluginUtils.logError(gutil, badResponseErrMessage); + callbackOrThrowError(errorCallback, badResponseErrMessage); + return; + } + + var responseBody; + try { + responseBody = JSON.parse(body); + } catch (error) { + process.exitCode = 1; + pluginUtils.logError(gutil, badResponseErrMessage); + callbackOrThrowError(errorCallback, badResponseErrMessage); + return; + } + if (!responseBody.isThemeEnabled) { + process.exitCode = 1; + pluginUtils.logError(gutil, errorMessage); + callbackOrThrowError(errorCallback, errorMessage); + return; + } + var themeEnabledCheckResponse = responseBody; + if (typeof themeEnabledCheckResponse !== 'undefined' && 'status' in themeEnabledCheckResponse && 'OK' === themeEnabledCheckResponse.status ) { + themesEnabledMap.set(themeId, themeEnabledCheckResponse.isThemeEnabled); + return themeEnabledCheckResponse.isThemeEnabled; + } + process.exitCode = 1; + pluginUtils.logError(gutil, errorMessage); + throw new Error(badResponseErrMessage); + } + + function getThemeEnabled() { + return themesEnabledMap; + } + + return { + process: function (server, opts, cb, errorCallback) { + process(server, opts, cb, errorCallback); + }, + validate: function (serverUrl, pluginToken, opts, cb, errorCallback) { + validate(serverUrl, pluginToken, opts, cb, errorCallback); + }, + themesEnabledMap: themesEnabledMap, + getThemeEnabled: getThemeEnabled + }; +}; \ No newline at end of file diff --git a/lib/local-server.js b/lib/local-server.js index 6e7454f..eb9bdf6 100644 --- a/lib/local-server.js +++ b/lib/local-server.js @@ -143,7 +143,10 @@ module.exports = function (gulp, gutil) { if (server.useResponsiveConfigsFromServer()) { return responsiveOptions.getOptions(server, opts, function (err, config) { - if (err) throw err; + if (err) { + process.exitCode = 1; + throw err; + } opts = skins.setOptionsFromConfig(config, opts); return runServer(); }); diff --git a/lib/plugin-export.js b/lib/plugin-export.js index 7795cf7..644ac43 100644 --- a/lib/plugin-export.js +++ b/lib/plugin-export.js @@ -107,6 +107,7 @@ module.exports = function (gulp, gutil) { handleCallback(cb); } } catch (err) { + process.exitCode = 1; if (res.statusCode > 201) { putils.logErrorResponseStatusCode(gutil, res); } else { @@ -180,6 +181,7 @@ module.exports = function (gulp, gutil) { } } } catch (err) { + process.exitCode = 1; if (res.statusCode > 201) { putils.logErrorResponseStatusCode(gutil, res); } else { @@ -195,6 +197,7 @@ module.exports = function (gulp, gutil) { res = resp; resp.pipe(stream); }).on('error', function(err) { + process.exitCode = 1; handleErrorCallback(cb, new Error(putils.logPluginRequestError(gutil, opts.doClear ? 'clear' : 'export', err))); }); diff --git a/lib/plugin-upload.js b/lib/plugin-upload.js index 43a34c5..7c6b4c2 100644 --- a/lib/plugin-upload.js +++ b/lib/plugin-upload.js @@ -24,6 +24,7 @@ module.exports = function (gulp, gutil) { if (typeof cb === 'function') { cb(err, null); } else { + process.exitCode = 1; throw err; } } @@ -38,6 +39,7 @@ module.exports = function (gulp, gutil) { if (fs.existsSync(pluginDir)) { dive(pluginDir, { recursive: true, directories: true }, function (err, file) { if (err) { + process.exitCode = 1; throw err; } @@ -121,6 +123,7 @@ module.exports = function (gulp, gutil) { pluginUtils.logHardFailures(serviceResponse, gutil, 'submit-plugin'); var errMsg = 'Plugin upload failed'; gutil.log(gutil.colors.red(errMsg)); + process.exitCode = 1; callbackOrThrowError(new Error(errMsg)); } else { pluginUtils.logSoftFailures(serviceResponse, gutil); @@ -137,11 +140,13 @@ module.exports = function (gulp, gutil) { req.on('response', function (res) { if (res.statusCode > 201) { + process.exitCode = 1; callbackOrThrowError(new Error(pluginUtils.logErrorResponseStatusCode(gutil, res))); } else { res.pipe(stream); } }).on('error', function(err) { + process.exitCode = 1; pluginUtils.logPluginRequestError(gutil, 'upload', err); callbackOrThrowError(err); }); diff --git a/lib/project.js b/lib/project.js index 3bcb7df..03cf936 100644 --- a/lib/project.js +++ b/lib/project.js @@ -523,6 +523,7 @@ module.exports = function (gulp, gutil) { if (noToken) { var skins = skinLib.getResponsiveSkinIds(); if (skins.length < 1) { + process.exitCode = 1; throw new Error('There are no responsive skins. You should create a skin in the res/skins folder and make its ' + 'parent a responsive skin (such as the responsive_peak skin) by setting the parent property in the ' + 'skin.properties file'); diff --git a/lib/responsive-options.js b/lib/responsive-options.js index 566bcee..ccd3b19 100644 --- a/lib/responsive-options.js +++ b/lib/responsive-options.js @@ -83,6 +83,7 @@ module.exports = function (gulp, gutil) { putils.logHardFailures(serviceResponse, gutil, scriptName); var errorMsg = 'Error making request to ' + (opts.doPut ? 'save' : 'get') + ' responsive options'; + process.exitCode = 1; return callbackOrThrowErrorMessage(cb, putils.logError(gutil, errorMsg)); } @@ -98,14 +99,17 @@ module.exports = function (gulp, gutil) { try { respBody = getResponse(buf); if (res.statusCode > 201) { + process.exitCode = 1; putils.logErrorResponseStatusCode(gutil, res); return writeError(respBody, 'responsive-' + opts.pluginType + '-plugin', cb); } else if (respBody.status.toLowerCase() === 'error') { + process.exitCode = 1; return writeError(respBody, 'responsive-' + opts.pluginType + '-plugin', cb); } else { return cb(null, respBody); } } catch (err) { + process.exitCode = 1; if (res.statusCode > 201) { putils.logErrorResponseStatusCode(gutil, res); } else { @@ -119,7 +123,10 @@ module.exports = function (gulp, gutil) { function logPostSuccess(res, buf) { handleResponse(res, buf, function(err) { - if (err) return callbackOrThrowError(cb, err); + if (err) { + process.exitCode = 1; + return callbackOrThrowError(cb, err); + } var successMsg = 'set resonsive options'; putils.logSuccess(gutil, successMsg); @@ -131,7 +138,10 @@ module.exports = function (gulp, gutil) { function writeResponsiveOptions(res, buf) { return handleResponse(res, buf, function(err, respBody) { - if (err) return callbackOrThrowError(cb, err); + if (err) { + process.exitCode = 1; + return callbackOrThrowError(cb, err); + } var responsiveConfig = {}; responsiveConfig.features = respBody.features; @@ -149,7 +159,10 @@ module.exports = function (gulp, gutil) { putils.logDebug(gutil, 'writing out responsive options to ' + responsiveConfigPath); } fs.writeFile(responsiveConfigPath, JSON.stringify(responsiveConfig), function(err) { - if (err) return callbackOrThrowError(cb, err); + if (err) { + process.exitCode = 1; + return callbackOrThrowError(cb, err); + } var successMsg = 'saved ' + responsiveConfigPath; putils.logSuccess(gutil, successMsg); @@ -163,12 +176,14 @@ module.exports = function (gulp, gutil) { var stream = through(onData).on('finish', onEnd); req.on('response', function (resp) { if (resp.statusCode > 201) { + process.exitCode = 1; callbackOrThrowErrorMessage(cb, new Error(putils.logErrorResponseStatusCode(gutil, resp))); } else { res = resp; resp.pipe(stream); } }).on('error', function(err) { + process.exitCode = 1; var errorMsg = putils.logRequestError(gutil, opts.doPut ? 'save responsive options' : 'get responsive options', err); callbackOrThrowErrorMessage(cb, errorMsg); }); @@ -206,6 +221,7 @@ module.exports = function (gulp, gutil) { return JSON.parse(configJson); } + process.exitCode = 1; throw new Error('no responsive config found at ' + configFilePath); } @@ -216,7 +232,10 @@ module.exports = function (gulp, gutil) { } opts.doPut = true; return handleRequest(server, opts, function(err) { - if (err) return callbackOrThrowError(cb, err) + if (err) { + process.exitCode = 1; + return callbackOrThrowError(cb, err); + } opts.noCache = true; return getOptions(server, opts, cb); @@ -232,7 +251,10 @@ module.exports = function (gulp, gutil) { var configFilePath = getConfigFilePath(opts); if (opts.noCache || !fs.existsSync(configFilePath)) { return handleRequest(server, opts, function(err, config, successMsg) { - if (err) return callbackOrThrowError(cb, err); + if (err) { + process.exitCode = 1; + return callbackOrThrowError(cb, err); + } return cb(null, getConfig(opts), successMsg); }); diff --git a/lib/sandbox-api-hack.js b/lib/sandbox-api-hack.js index 5d15b83..695f8de 100644 --- a/lib/sandbox-api-hack.js +++ b/lib/sandbox-api-hack.js @@ -101,6 +101,7 @@ module.exports = function(gulp, gutil) { } }) .catch(function() { + process.exitCode = 1; gutil.log( gutil.colors.red( 'Failed to reload plugin, please make sure your server.conf.json is configured ' + @@ -113,6 +114,7 @@ module.exports = function(gulp, gutil) { function syncPlugin() { var pluginDir = gutil.env.newStructure ? 'dist/plugin/' : 'plugin/'; return rsync()(pluginDir, getServer().sandboxPluginDir()).catch(function(e) { + process.exitCode = 1; gutil.log(gutil.colors.red('Failed to sync files: ' + e)); }); } diff --git a/lib/scripts.js b/lib/scripts.js index 991f7c6..16ccd1c 100644 --- a/lib/scripts.js +++ b/lib/scripts.js @@ -222,6 +222,7 @@ module.exports = function (gulp, gutil) { if (dependencies !== undefined && moduleDependencies[module] !== undefined && JSON.stringify(moduleDependencies[module]) !== JSON.stringify(['ng'])) { + process.exitCode = 1; throw new Error ( 'Duplicate module declaration for [' + module + '] in [' + diff --git a/lib/server-version-theme-check.json b/lib/server-version-theme-check.json new file mode 100644 index 0000000..b209030 --- /dev/null +++ b/lib/server-version-theme-check.json @@ -0,0 +1,4 @@ +{ + "supportedVersionMajor": "22", + "supportedVersionMinor": "7" +} \ No newline at end of file diff --git a/lib/server-version.json b/lib/server-version.json index bcdef36..5d7433e 100644 --- a/lib/server-version.json +++ b/lib/server-version.json @@ -1,4 +1,4 @@ { "supportedVersionMajor": "16", - "supportedVersionMinor": "3" + "supportedVersionMinor": "4" } \ No newline at end of file diff --git a/lib/server.js b/lib/server.js index f922128..e026450 100644 --- a/lib/server.js +++ b/lib/server.js @@ -44,6 +44,7 @@ module.exports = function (gulp, gutil) { try { serverConfig = gutil.env.useServerDefaults ? {} : require(path.join(process.cwd(), serverConfigPath)); } catch (err) { + process.exitCode = 1; throw new Error('Error reading server.conf.json at [' + path.join(process.cwd(), serverConfigPath) + ']. Please use template.server.conf.json to create server.conf.json.'); diff --git a/lib/skin.js b/lib/skin.js index 0b62bee..018e5ad 100644 --- a/lib/skin.js +++ b/lib/skin.js @@ -15,16 +15,22 @@ module.exports = function (gulp, gutil) { var skinPropertyCopySass = 'copy_sass_files_to_child'; var baseFeaturePath = gutil.env.newStructure ? 'plugin/res/feature/responsivebase/' : 'res/feature/responsivebase/'; var peakFeaturePath = gutil.env.newStructure ? 'plugin/res/feature/responsivepeak/' : 'res/feature/responsivepeak/'; + var themeFeaturePath = 'other/res/skins' var skinsBaseDir = 'res/skins'; var communityPluginsDir = 'other'; var propFileName = 'skin.properties'; - + var checkTheme, version; + var themesMap; + var serverVersionThemeCheck = require('../lib/server-version-theme-check.json'); + var versionPattern = /(\d+)\.(\d{0,2})*/i; + function isEmpty(obj) { return Object.keys(obj).length === 0; } function checkString(valName, val) { if (!(typeof val === 'string' || val instanceof String)) { + process.exitCode = 9; throw Error(valName + ' must be a string!'); } } @@ -60,7 +66,7 @@ module.exports = function (gulp, gutil) { var skinDir = path.resolve(skinsBaseDir, skinId); return createSkin(path.basename(skinDir), skinDir); }).filter(function(skin) { - return !skin.isResponsive(); + return !skin.isResponsive() && !skin.getIsTheme(); }); } @@ -170,6 +176,7 @@ module.exports = function (gulp, gutil) { getResponsiveFeatureVersionFromCore(baseFeaturePath, server.localSkinCompileVersion()), skinsBaseDir, 'bootstrap_base'); } else { + process.exitCode = 1; throw new Error('Invalid parent core responsive skin=' + skinId); } } @@ -185,6 +192,7 @@ module.exports = function (gulp, gutil) { return likelyPath; } //Should we error here? or just create a skin with unknown skinId + process.exitCode = 1; throw new Error('Invalid skin=' + skinId); } @@ -194,6 +202,7 @@ module.exports = function (gulp, gutil) { var skinProperties = {}; var parent = null; var attribs = {}; + var isTheme = false; var getAttrib = function(key, fPopulator) { if (attribs[key] === undefined) { @@ -266,6 +275,14 @@ module.exports = function (gulp, gutil) { return skinProperties; } + this.getIsTheme = function() { + return isTheme; + } + + this.setIsTheme = function(isThemeSkin) { + isTheme = isThemeSkin; + } + this.getId = function() { return id; }; @@ -286,7 +303,28 @@ module.exports = function (gulp, gutil) { if (!parent) { var parentId = this.getParentId(); if (parentId) { - parent = new Skin(parentId, findBaseDirForSkin(parentId)); + if (parentId.includes("theme")) { + var matches = version.toString().match(versionPattern); + var versionOnServer = parseInt(matches[1]); + var minorVersionOnServer = parseInt(matches[2]); + if (versionOnServer < serverVersionThemeCheck.supportedVersionMajor || + (versionOnServer == serverVersionThemeCheck.supportedVersionMajor && + minorVersionOnServer < serverVersionThemeCheck.supportedVersionMinor)) { + isTheme = true; + return null; + } + + var isEnabled = themesMap.get(parentId); + if(isEnabled === 'true') { + parent = new Skin(parentId, findBaseDirForSkin(parentId)); + } else { + isTheme = true; + parent = null; + } + + } else { + parent = new Skin(parentId, findBaseDirForSkin(parentId)); + } } } return parent; @@ -326,6 +364,7 @@ module.exports = function (gulp, gutil) { var parent = skin.getParent(); if (parent != null) { responsiveSkin = parent.isResponsive(); + skin.setIsTheme(parent.getIsTheme()); } } @@ -337,6 +376,14 @@ module.exports = function (gulp, gutil) { var responsiveSkins = []; var responsiveSkinByIdMap = {}; + function setLiaVersion(liaVersion) { + version = liaVersion; + } + + function setThemesMap(themesEnabledMap) { + themesMap = themesEnabledMap; + } + return { Skin: Skin, getResponsiveSkins: function() { @@ -371,7 +418,10 @@ module.exports = function (gulp, gutil) { baseFeaturePath: baseFeaturePath, skinPropertyCopySass: skinPropertyCopySass, skinsBaseDir : skinsBaseDir, + themeFeaturePath: themeFeaturePath, findBaseDirForSkin: findBaseDirForSkin, + setLiaVersion: setLiaVersion, + setThemesMap: setThemesMap, sassDir : 'sass', componentsDir : 'components', skinSubFolder : ['desktop', 'mobile'], diff --git a/lib/skins.js b/lib/skins.js index b74c38f..4dd9370 100644 --- a/lib/skins.js +++ b/lib/skins.js @@ -73,6 +73,10 @@ module.exports = function (gulp, gutil) { function findCoreSkin(skin) { if (skin) { + var version = require('../lib/version-check')(gulp, gutil).getVersion(); + skinLib.setLiaVersion(version); + var isThemeEnabled = require('../lib/check-themes')(gulp, gutil).getThemeEnabled(); + skinLib.setThemesMap(isThemeEnabled); var responsiveSkin = skinLib.lookupResponsiveSkin(skin); if (responsiveSkin) { return responsiveSkin.getResponsiveCoreId(); @@ -136,6 +140,16 @@ module.exports = function (gulp, gutil) { if (skinInfo.isResponsive) { //Create sass folder fs().mkdirSync(path.join(skinDir, skinLib.sassDir), skinInfo.errorCb); + const parentSkinSassDir = path.join(skinInfo.parentSkin.getDir(), skinLib.sassDir) + if (fs().existsSync(parentSkinSassDir)) { + const subDirs = fs().readdirSync(parentSkinSassDir) + .filter(function (file) { + return fs().statSync(path.join(parentSkinSassDir, file)).isDirectory(); + }); + subDirs.forEach(function(folder) { + fs().mkdirSync(path.join(skinDir, skinLib.sassDir, folder), skinInfo.errorCb); + }); + } //Copy all sass files - wait until copy if (sassFilesToCopy) { sassFilesToCopy.forEach(function (key) { @@ -166,6 +180,7 @@ module.exports = function (gulp, gutil) { createSkinDirectoryStructure(); skinInfo.cb(); } catch (err) { + process.exitCode = 1; skinInfo.errorCb(err); } }); @@ -229,7 +244,8 @@ module.exports = function (gulp, gutil) { skinLib.peakFeaturePath + version + '/' + skinLib.skinsBaseDir, skinLib.baseFeaturePath + version + '/' + skinLib.skinsBaseDir, skinLib.baseFeaturePath + 'common/res/skins', - skinLib.peakFeaturePath + 'common/res/skins' + skinLib.peakFeaturePath + 'common/res/skins', + skinLib.themeFeaturePath ], doClear: false }; @@ -238,6 +254,7 @@ module.exports = function (gulp, gutil) { return new Promise(function (resolve) { responsiveOptions.getOptions(server, opts, function (err, config) { if (err) { + process.exitCode = 1; throw err; } @@ -248,6 +265,7 @@ module.exports = function (gulp, gutil) { opts.includePathsPrefix + 'res/feature/responsivebase/' + opts.version + '/res/skins/', opts.includePathsPrefix + 'res/feature/responsivebase/common/res/skins/', opts.includePathsPrefix + 'res/feature/responsivepeak/common/res/skins/', + opts.includePathsPrefix + 'other/res/skins/', 'res/skins' ]; setOptionsFromConfig(config, opts); diff --git a/lib/spawn-gulp.js b/lib/spawn-gulp.js index 728b2e8..7845642 100644 --- a/lib/spawn-gulp.js +++ b/lib/spawn-gulp.js @@ -10,5 +10,7 @@ module.exports = function (args) { { stdio: 'inherit' } - ); + ).on('exit', function(code) { + process.exitCode = code; + }); }; \ No newline at end of file diff --git a/lib/version-check.js b/lib/version-check.js index 7b0fd54..49b9f1a 100644 --- a/lib/version-check.js +++ b/lib/version-check.js @@ -5,6 +5,8 @@ var pluginUtils = require('../lib/plugin-utils'); var defaultVersion = require('./server-version.json'); var sdkVersion = '1.0.2'; var putils = require('./plugin-utils'); +var liaVersion; +var serverVersionThemeCheck = require('./server-version-theme-check.json'); module.exports = function (gulp, gutil) { var versionPattern = /(\d+)\.(\d{0,2})*/i; @@ -18,6 +20,11 @@ module.exports = function (gulp, gutil) { }; var serverVersion = opts.version === undefined ? defaultVersion : opts.version; + if (pluginToken == null) { + putils.logDebug(gutil, 'Either sdk is not enabled or plugin token is not provided, pluginToken should be provided for sdk plugin projects'); + return; + } + var versionCheckUrl = pluginUtils.urlBldr(serverUrl + '/restapi/ldntool/plugins/version?format=json').build(); if (opts.debugMode) { putils.logDebug(gutil, 'making version check call to ' + versionCheckUrl); @@ -28,6 +35,7 @@ module.exports = function (gulp, gutil) { putils.logDebug(gutil, 'response from version check: \n' + body); } if (error || response.statusCode > 201) { + process.exitCode = 1; putils.logError(gutil, 'got an error when trying to make the call: \n' + error); parseResponse(body, opts, errorCallback); return; @@ -37,6 +45,7 @@ module.exports = function (gulp, gutil) { var version = versionResponse.version; var matches = version.toString().match(versionPattern); if (!matches || matches.length < 3) { + process.exitCode = 1; callbackOrThrowError(errorCallback, 'Invalid version check response ' + version); return; } @@ -45,6 +54,7 @@ module.exports = function (gulp, gutil) { if (versionOnServer < serverVersion.supportedVersionMajor || (versionOnServer === serverVersion.supportedVersionMajor && minorVersionOnServer < serverVersion.supportedVersionMinor)) { + process.exitCode = 1; var errorMessage = 'Supported minimum version on server is ' + serverVersion.supportedVersionMajor + '.' + serverVersion.supportedVersionMinor + '.'; pluginUtils.logError(gutil, errorMessage); @@ -54,11 +64,19 @@ module.exports = function (gulp, gutil) { callbackOrThrowError(errorCallback, errorMessage); } + if (versionOnServer < serverVersionThemeCheck.supportedVersionMajor || + (versionOnServer == serverVersionThemeCheck.supportedVersionMajor && + minorVersionOnServer < serverVersionThemeCheck.supportedVersionMinor)) { + putils.logWarning(gutil, "This version of community doesn't support rendering custom theme skins! Pls upgrade your community to version " + + serverVersionThemeCheck.supportedVersionMajor + '.' + serverVersionThemeCheck.supportedVersionMinor + " or newer to get this feature."); + } + //On success invoke callback function if (typeof cb === 'function') { return cb(); } } catch (error) { + process.exitCode = 1; callbackOrThrowError(errorCallback, error.message); } @@ -87,6 +105,7 @@ module.exports = function (gulp, gutil) { var errorMessage = 'Empty version check response'; var badResponseErrMessage = 'Invalid response from server. Check your server url and version.'; if ( !body ) { + process.exitCode = 1; pluginUtils.logError(gutil, badResponseErrMessage); callbackOrThrowError(errorCallback, badResponseErrMessage); return; @@ -96,11 +115,13 @@ module.exports = function (gulp, gutil) { try { responseBody = JSON.parse(body); } catch (error) { + process.exitCode = 1; pluginUtils.logError(gutil, badResponseErrMessage); callbackOrThrowError(errorCallback, badResponseErrMessage); return; } if (!responseBody.version) { + process.exitCode = 1; errorMessage = responseBody['service-response'] ? responseBody['service-response'].message : badResponseErrMessage; pluginUtils.logError(gutil, errorMessage); @@ -109,8 +130,10 @@ module.exports = function (gulp, gutil) { } var versionResponse = responseBody; if (typeof versionResponse !== 'undefined' && 'status' in versionResponse && 'OK' === versionResponse.status ) { + liaVersion = versionResponse.version; return versionResponse; } + process.exitCode = 1; pluginUtils.logError(gutil, errorMessage); throw new Error(badResponseErrMessage); } @@ -118,6 +141,7 @@ module.exports = function (gulp, gutil) { function createVersion(versionStr) { var matches = versionStr.toString().match(versionPattern); if (!matches || matches.length < 3) { + process.exitCode = 9; throw new Error('Invalid version=' + versionStr + '. Should be of format . '); } @@ -128,6 +152,10 @@ module.exports = function (gulp, gutil) { return serverVersion; } + function getVersion() { + return liaVersion; + } + return { process: function (server, opts, cb, errorCallback) { process(server, opts, cb, errorCallback); @@ -135,6 +163,8 @@ module.exports = function (gulp, gutil) { validate: function (serverUrl, pluginToken, opts, cb, errorCallback) { validate(serverUrl, pluginToken, opts, cb, errorCallback); }, - createVersion: createVersion + createVersion: createVersion, + liaVersion: liaVersion, + getVersion: getVersion }; }; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index d04784f..c12e033 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "lithium-sdk", - "version": "1.9.0", + "version": "1.10.1", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index b5edd8d..254bc1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "lithium-sdk", - "version": "1.9.0", + "version": "1.11.0", "author": "peterasher", "bin": { "li": "bin/li.js"