UNPKG

12.1 kBJavaScriptView Raw
1var nopt = require("nopt"),
2 noptTypes = require("./nopt-types.js"),
3 noptUsage = require("./nopt-usage-2.js"),
4 path = require("path"),
5 parseDependencies = require('./dependency-parser.js').parseDependencies,
6 BuildDiffProcess = require("./build-diff-process.js").BuildDiffProcess,
7 cfgLoader = require('./configuration-loader.js'),
8 readJsonSync = require('read-json-sync'),
9 loggerFactory = require("./sl-logger.js").logger,
10 expandCommaSeparatedValues = require('./utils').expandCommaSeparatedValues,
11 globalErrorHandler = require('./global-error-handler'),
12 SlJsInfra = require('sl-js-infra').SlJsInfra,
13 DefaultValuesResolver = require('./default-values-resolver');
14
15
16//TODO: [SLDEV-4709] Verify if we can remove this completely. Should check how SlNodeJS invokes it.
17function sendLastLogs(cfg, logger, callback) {
18 if (cfg.sendLogs) {
19 logger.remoteStream.checkBuffer(true, function (err) {
20 callback();
21 })
22 } else {
23 callback();
24 }
25}
26
27function CIA(sourceConrolProviders) {
28 this._hasMissingArguments = false;
29 this._hadInvalidArguments = false;
30 this.getSourceConrolProviders = function () {
31 return sourceConrolProviders;
32 };
33}
34
35CIA.prototype.run = function run(callback) {
36 console.log('SeaLights Build agent ' + require("../package.json").version);
37 nopt.typeDefs.git = noptTypes.git;
38
39 var context = this;
40 nopt.invalidHandler = function (key, val, types) {
41 context._hasMissingArguments = true;
42 console.error('Invalid value specified for ' + key + ': ' + val);
43 };
44
45 var knownOpts = this._createKnownOptions();
46 var shortHands = this._createShortHands();
47 var description = this._createDescription();
48 var defaults = this._createDefaults();
49
50 var buildArguments = nopt(knownOpts, shortHands, process.argv, 2);
51 for (var i in defaults) { //Add default values
52 if (!buildArguments[i]) {
53 buildArguments[i] = defaults[i];
54 }
55 }
56
57 context._validateArguments(buildArguments);
58 var parsedDependencies = context._tryParseDependenciesForIntegrationBuild(buildArguments);
59 if (context._hasInvalidOrMissingArguments(buildArguments)) {
60 try {
61 var usage = noptUsage(knownOpts, shortHands, description, defaults);
62 console.error(usage);
63 } catch (e) {
64 globalErrorHandler.setLastError(e);
65 }
66 return callback(1);
67 }
68
69 buildArguments.includedFiles = expandCommaSeparatedValues(buildArguments.includedFiles);
70 buildArguments.excludedFiles = expandCommaSeparatedValues(buildArguments.excludedFiles);
71 buildArguments.babylonPlugins = expandCommaSeparatedValues(buildArguments.babylonPlugins);
72 cfgLoader(buildArguments.config).then(function (cfg) {
73 cfg.appName = buildArguments.appname;
74
75 if (buildArguments.token)
76 cfg.token = buildArguments.token;
77
78 var logger = loggerFactory(cfg);
79 printConfiguration(cfg, logger)
80
81 var buildMappingProxy = new SlJsInfra.BackendProxy(cfg, logger);
82 var scmProviders = context.getSourceConrolProviders();
83 var buildDiffProcess = new BuildDiffProcess(cfg, buildMappingProxy,scmProviders, parsedDependencies, logger);
84 buildDiffProcess.run(buildArguments).then(function () {
85 sendLastLogs(cfg, logger, function () {
86 callback(0);
87 });
88 }).catch(function (err) {
89 logger.error(err);
90 sendLastLogs(cfg, logger, function () {
91 callback(buildArguments.failBuild ? 1 : 0);
92 });
93 });
94 }).catch(function (err) {
95 console.error(err.stack);
96 callback(buildArguments.failBuild ? 1 : 0);
97 });
98}
99
100CIA.prototype._hasInvalidOrMissingArguments = function(buildArguments){
101 return this._hasMissingArguments || this._hadInvalidArguments || buildArguments.help;
102}
103
104CIA.prototype._tryParseDependenciesForIntegrationBuild = function(buildArguments){
105 var parsedDependencies = [];
106 if (buildArguments.dependency && !this._hadInvalidArguments) {
107 try {
108 parsedDependencies = parseDependencies(buildArguments.dependency);
109 } catch (e) {
110 globalErrorHandler.setLastError(e)
111 this._hadInvalidArguments = true;
112 }
113 }
114
115 if (buildArguments.dependenciesFile && !this._hadInvalidArguments) {
116 try {
117 var parsedJson = readJsonSync(buildArguments.dependenciesFile);
118 if (!parsedJson || !Array.isArray(parsedJson)) {
119 throw 'Invalid file structure';
120 }
121
122 parsedJson.forEach(function (depItem) {
123 if (depItem.appName && depItem.build && depItem.branch) {
124 parsedDependencies.push({
125 appName: depItem.appName.toString(),
126 branch: depItem.branch.toString(),
127 build: depItem.build.toString()
128 });
129 } else {
130 throw 'Invalid dependency item: ' + JSON.stringify(depItem);
131 }
132 });
133
134 } catch (e) {
135 globalErrorHandler.setLastError(e, 'Error reading/parsing ' + buildArguments.dependenciesFile);
136 this._hadInvalidArguments = true;
137 }
138 }
139
140 return parsedDependencies;
141}
142
143CIA.prototype._validateArguments = function(buildArguments){
144 var context = this;
145 ["branch", "build", "appname"].forEach(function (key) {
146 if (!buildArguments[key]) {
147 console.error("Required argument '" + key + "' not specified");
148 context._hasMissingArguments = true;
149 }
150 });
151
152 //workspacepath, scm must all come together or not at all
153 var count = 0;
154 ["workspacepath", "scm"].forEach(function (key) {
155 if (buildArguments[key]) {
156 count++;
157 }
158 });
159
160 if (count != 0 && count != 2) {
161 console.error("The 'workspacepath' and 'scm' command-line arguments must be specified together or not at all");
162 this._hadInvalidArguments = true;
163 }
164}
165
166CIA.prototype._createKnownOptions = function(){
167 return {
168 "workspacepath": path,
169 "outputpath": String,
170 "instrumentForBrowsers": Boolean,
171 "downloadAgent": Boolean,
172 "instrumentationOnly": Boolean,
173 "branch": String,
174 "build": String,
175 "commit": String,
176 "appname": String,
177 "config": String,
178 "scm": "scm",
179 "help": Boolean,
180 "dependency": [String, Array],
181 "dependenciesFile": path,
182 "recursive": Boolean,
183 "author": [String, Array],
184 "logsUrl": String,
185 "jobName": String,
186 "usebranchcoverage": Boolean,
187 "usebuildmappingv3": Boolean,
188 "token": String,
189 "buildsessionid": String,
190 "includedFiles": [String, Array],
191 "excludedFiles": [String, Array],
192 "babylonPlugins": [String, Array],
193 "es6Modules": Boolean,
194 "failBuild": Boolean,
195 "uniqueModuleId": String,
196 "scmBaseUrl": String,
197 "scmVersion": String,
198 "scmProvider": String,
199 "useBabylon":Boolean,
200 "sendContributors": Boolean,
201 "delayShutdownInSeconds": Number,
202 "folderToInstrument": path,
203 "projectRoot": String
204 };
205}
206
207CIA.prototype._createShortHands = function(){
208 return {
209 "h": ["--help"],
210 "d": ["--dependency"],
211 "r": ["--recursive"]
212 };
213}
214
215CIA.prototype._createDescription = function(){
216 return {
217 "workspacepath": " Path to the workspace where the source code exists",
218 "outputpath": " Path where the compiled output exists",
219 "instrumentForBrowsers": " Instrument javascript files for browser coverage",
220 "instrumentationOnly": " Skip sending mapping to the server. Used when instrumenting files on multiple steps",
221 "downloadAgent": " Set this value to 'false' in order to prevent the instrumented javascript to try and download the browser test listener (for example, when using 'Karma').",
222 "copyAllFilesToOutput": " Copy all files from the workspacepath to the outputpath. On by default.",
223 "branch": " Branch name",
224 "build": " Build version",
225 "commit": " commit ID, as provided by the SCM (e.g. SHA1 for git, revision for SVN, changeset for TFS). May be automatically detected by the SCM.",
226 "appname": " Application Name",
227 "scm": " The Source Control Management used (currently only 'git' is supported)",
228 "help": " Show this help page",
229 "dependency": " Project dependencies. Pattern should be a semicolon-separated AppName@Branch@Build list. May be specified more than once.",
230 "dependenciesFile": " A path to a json file that is in the following format: [{\"appName\":\"\",\"branch\":\"\",\"build\":\"\"},{...}]",
231 "recursive": " Starts a recursive search for in a folder specified by the 'workspacepath' option",
232 "author": " Optional. The name or email of the user that should be associated with this build. May be specified more than once.",
233 "logsUrl": " Optional. The logs url of the build.",
234 "jobName": " Optional. The jenkins job name that triggered the build.",
235 "usebranchcoverage": " Optional. Include branches in scan",
236 "usebuildmappingv3": " Optional. Use V3 of build mapping",
237 "token": " Agent token used to authenticate requests by the server",
238 "buildsessionid": " Build Session ID to associate with this build",
239 "includedFiles": " Files glob pattern to include, relative to the workspacepath. Defaults to **/*.js. Comma-separated values are accepted.",
240 "excludedFiles": " Files glob pattern to exclude, relative to the workspacepath. Defaults to node_modules/**/*.js, test/**.js. Comma-separated values are accepted.",
241 "failBuild": " Makes the build scanner exit with non-zero code on success. Off by default",
242 "es6Modules": " Use ES6 'module' mode when scanning files.",
243 "useBabylon":"Use babylon for scanning files",
244 "babylonPlugins": "List of non default babylon parser plugins, separated by comma",
245 "sendContributors": "Send contributor details for advanced committer reports and features",
246 "delayShutdownInSeconds":"Shutdown time in seconds for the browser agent. Default is 30 seconds.",
247 "scmProvider": "The provider name of your Source Control Management (SCM) tool. Supported values are 'Github', 'Bitbucket' and 'Gitlab'. If not used, 'Github' is assumed.",
248 "scmVersion": "The version of your Source Control Management (SCM) tool. If left blank, cloud version is assumed. Otherwise, specify the version of your on-premise server.",
249 "scmBaseUrl": "The URL to the repository which contains the code. If left blank, the url of the remote GIT origin is being used.",
250 "uniqueModuleId":"Unique module id, case-sensitive. This value should remain consistent between runs.",
251 "folderToInstrument": "Determine which folder should be instrumented"
252 };
253}
254
255CIA.prototype._createDefaults = function(){
256 var defaultValuesResolver = new DefaultValuesResolver();
257 return {
258 "help": "",
259 "includedFiles": defaultValuesResolver.getIncludedFiles(),
260 "excludedFiles": defaultValuesResolver.getExcludedFiles(),
261 "prefixesOfExcludedFiles": process.env["SL_prefixesOfExcludedFiles"] ? process.env["SL_prefixesOfExcludedFiles"] : "~, :, webpack, node_modules",
262 "delayShutdownInSeconds":30
263 };
264}
265
266
267function printConfiguration(cfg, logger)
268{
269 logger.info("***************************************************************");
270 logger.info("Current Configuration: ");
271 logger.info("***************************************************************");
272
273 for(p in cfg)
274 {
275 if (typeof cfg[p] != "function"){
276 logger.info(p + ":", cfg[p]);
277 }
278 }
279
280 logger.info("***************************************************************");
281
282}
283
284module.exports.CIA = CIA;