1 | var 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 |
|
17 | function 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 |
|
27 | function CIA(sourceConrolProviders) {
|
28 | this._hasMissingArguments = false;
|
29 | this._hadInvalidArguments = false;
|
30 | this.getSourceConrolProviders = function () {
|
31 | return sourceConrolProviders;
|
32 | };
|
33 | }
|
34 |
|
35 | CIA.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) {
|
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 |
|
100 | CIA.prototype._hasInvalidOrMissingArguments = function(buildArguments){
|
101 | return this._hasMissingArguments || this._hadInvalidArguments || buildArguments.help;
|
102 | }
|
103 |
|
104 | CIA.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 |
|
143 | CIA.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 |
|
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 |
|
166 | CIA.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 |
|
207 | CIA.prototype._createShortHands = function(){
|
208 | return {
|
209 | "h": ["--help"],
|
210 | "d": ["--dependency"],
|
211 | "r": ["--recursive"]
|
212 | };
|
213 | }
|
214 |
|
215 | CIA.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 |
|
255 | CIA.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 |
|
267 | function 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 |
|
284 | module.exports.CIA = CIA;
|