UNPKG

15.7 kBJavaScriptView Raw
1'use strict';
2
3var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
4
5function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
6
7var utils = require('./utils');
8
9var GIT_COMMIT_FORMAT = ['COMMIT_SHA:%H', 'AUTHOR_NAME:%an', 'AUTHOR_EMAIL:%ae', 'COMMITTER_NAME:%cn', 'COMMITTER_EMAIL:%ce', 'COMMITTED_DATE:%ai',
10// Note: order is important, this must come last because the regex is a multiline match.
11'COMMIT_MESSAGE:%B'].join('%n'); // git show format uses %n for newlines.
12
13var Environment = function () {
14 function Environment(env) {
15 _classCallCheck(this, Environment);
16
17 if (!env) {
18 throw new Error('"env" arg is required to create an Environment.');
19 }
20 this._env = env;
21 }
22
23 _createClass(Environment, [{
24 key: 'gitExec',
25 value: function gitExec(args) {
26 var child_process = require('child_process');
27 try {
28 var result = child_process.spawnSync('git', args);
29 if (result.status == 0) {
30 return result.stdout.toString().trim();
31 } else {
32 return '';
33 }
34 } catch (error) {
35 return '';
36 }
37 }
38 }, {
39 key: 'rawCommitData',
40 value: function rawCommitData(commitSha) {
41 // Make sure commitSha is only alphanumeric characters and ^ to prevent command injection.
42 if (commitSha.length > 100 || !commitSha.match(/^[0-9a-zA-Z^]+$/)) {
43 return '';
44 }
45
46 var args = ['show', commitSha, '--quiet', '--format="' + GIT_COMMIT_FORMAT + '"'];
47 return this.gitExec(args);
48 }
49
50 // If not running in a git repo, allow undefined for certain commit attributes.
51
52 }, {
53 key: 'parse',
54 value: function parse(formattedCommitData, regex) {
55 return (formattedCommitData && formattedCommitData.match(regex) || [])[1];
56 }
57 }, {
58 key: 'rawBranch',
59 value: function rawBranch() {
60 return this.gitExec(['rev-parse', '--abbrev-ref', 'HEAD']);
61 }
62 }, {
63 key: 'ci',
64 get: function get() {
65 if (this._env.TRAVIS_BUILD_ID) {
66 return 'travis';
67 } else if (this._env.JENKINS_URL && this._env.ghprbPullId) {
68 // Pull Request Builder plugin.
69 return 'jenkins-prb';
70 } else if (this._env.JENKINS_URL) {
71 return 'jenkins';
72 } else if (this._env.CIRCLECI) {
73 return 'circle';
74 } else if (this._env.CI_NAME && this._env.CI_NAME == 'codeship') {
75 return 'codeship';
76 } else if (this._env.DRONE == 'true') {
77 return 'drone';
78 } else if (this._env.SEMAPHORE == 'true') {
79 return 'semaphore';
80 } else if (this._env.BUILDKITE == 'true') {
81 return 'buildkite';
82 } else if (this._env.HEROKU_TEST_RUN_ID) {
83 return 'heroku';
84 } else if (this._env.GITLAB_CI == 'true') {
85 return 'gitlab';
86 } else if (this._env.TF_BUILD == 'True') {
87 return 'azure';
88 } else if (this._env.APPVEYOR == 'True' || this._env.APPVEYOR == 'true') {
89 return 'appveyor';
90 } else if (this._env.PROBO_ENVIRONMENT == 'TRUE') {
91 return 'probo';
92 } else if (this._env.BITBUCKET_BUILD_NUMBER) {
93 return 'bitbucket';
94 } else if (this._env.CI) {
95 // this should always be the last branch
96 return 'CI/unknown';
97 }
98
99 return null;
100 }
101 }, {
102 key: 'ciVersion',
103 get: function get() {
104 switch (this.ci) {
105 case 'gitlab':
106 return 'gitlab/' + this._env.CI_SERVER_VERSION;
107 }
108 return this.ci;
109 }
110 }, {
111 key: 'commitData',
112 get: function get() {
113 // Read the result from environment data
114 var result = {
115 branch: this.branch,
116 sha: this.commitSha,
117
118 // These GIT_ environment vars are from the Jenkins Git Plugin, but could be
119 // used generically. This behavior may change in the future.
120 authorName: this._env['GIT_AUTHOR_NAME'],
121 authorEmail: this._env['GIT_AUTHOR_EMAIL'],
122 committerName: this._env['GIT_COMMITTER_NAME'],
123 committerEmail: this._env['GIT_COMMITTER_EMAIL']
124 };
125
126 // Try and get more meta-data from git
127 var formattedCommitData = '';
128 if (this.commitSha) {
129 formattedCommitData = this.rawCommitData(this.commitSha);
130 }
131 if (!formattedCommitData) {
132 formattedCommitData = this.rawCommitData('HEAD');
133 }
134 if (!formattedCommitData) {
135 return result;
136 }
137
138 // If this.commitSha didn't provide a sha, use the one from the commit
139 if (!result.sha) {
140 result.sha = this.parse(formattedCommitData, /COMMIT_SHA:(.*)/);
141 }
142
143 result.message = this.parse(formattedCommitData, /COMMIT_MESSAGE:(.*)/m);
144 result.committedAt = this.parse(formattedCommitData, /COMMITTED_DATE:(.*)/);
145 result.authorName = this.parse(formattedCommitData, /AUTHOR_NAME:(.*)/);
146 result.authorEmail = this.parse(formattedCommitData, /AUTHOR_EMAIL:(.*)/);
147 result.committerName = this.parse(formattedCommitData, /COMMITTER_NAME:(.*)/);
148 result.committerEmail = this.parse(formattedCommitData, /COMMITTER_EMAIL:(.*)/);
149
150 return result;
151 }
152 }, {
153 key: 'jenkinsMergeCommitBuild',
154 get: function get() {
155 var formattedCommitData = this.rawCommitData('HEAD');
156
157 if (!formattedCommitData) {
158 return false;
159 }
160
161 var authorName = this.parse(formattedCommitData, /AUTHOR_NAME:(.*)/);
162 var authorEmail = this.parse(formattedCommitData, /AUTHOR_EMAIL:(.*)/);
163 var message = this.parse(formattedCommitData, /COMMIT_MESSAGE:(.*)/m);
164
165 if (authorName === 'Jenkins' && authorEmail === 'nobody@nowhere') {
166 // Example merge message: Merge commit 'ec4d24c3d22f3c95e34af95c1fda2d462396d885' into HEAD
167 if (message.substring(0, 13) === 'Merge commit ' && message.substring(55) === ' into HEAD') {
168 return true;
169 }
170 }
171
172 return false;
173 }
174 }, {
175 key: 'secondToLastCommitSHA',
176 get: function get() {
177 var formattedCommitData = this.rawCommitData('HEAD^');
178
179 if (!formattedCommitData) {
180 return null;
181 }
182
183 return this.parse(formattedCommitData, /COMMIT_SHA:(.*)/);
184 }
185 }, {
186 key: 'commitSha',
187 get: function get() {
188 if (this._env.PERCY_COMMIT) {
189 return this._env.PERCY_COMMIT;
190 }
191 switch (this.ci) {
192 case 'travis':
193 return this._env.TRAVIS_COMMIT;
194 case 'jenkins-prb':
195 // Pull Request Builder Plugin OR Git Plugin.
196 return this._env.ghprbActualCommit || this._env.GIT_COMMIT;
197 case 'jenkins':
198 if (this.jenkinsMergeCommitBuild) {
199 return this.secondToLastCommitSHA;
200 }
201 return this._env.GIT_COMMIT;
202 case 'circle':
203 return this._env.CIRCLE_SHA1;
204 case 'codeship':
205 return this._env.CI_COMMIT_ID;
206 case 'drone':
207 return this._env.DRONE_COMMIT;
208 case 'semaphore':
209 return this._env.REVISION;
210 case 'buildkite':
211 {
212 var commitSha = this._env.BUILDKITE_COMMIT;
213 // Buildkite mixes SHAs and non-SHAs in BUILDKITE_COMMIT, so we return null if non-SHA.
214 return commitSha !== 'HEAD' ? this._env.BUILDKITE_COMMIT : null;
215 }
216 case 'heroku':
217 return this._env.HEROKU_TEST_RUN_COMMIT_VERSION;
218 case 'gitlab':
219 return this._env.CI_COMMIT_SHA;
220 case 'azure':
221 return this._env.SYSTEM_PULLREQUEST_SOURCECOMMITID || this._env.BUILD_SOURCEVERSION;
222 case 'appveyor':
223 return this._env.APPVEYOR_PULL_REQUEST_HEAD_COMMIT || this._env.APPVEYOR_REPO_COMMIT;
224 case 'probo':
225 return this._env.COMMIT_REF;
226 case 'bitbucket':
227 return this._env.BITBUCKET_COMMIT;
228 }
229
230 return null;
231 }
232 }, {
233 key: 'targetCommitSha',
234 get: function get() {
235 return this._env.PERCY_TARGET_COMMIT || null;
236 }
237 }, {
238 key: 'partialBuild',
239 get: function get() {
240 var partial = this._env.PERCY_PARTIAL_BUILD;
241 return !!partial && partial !== '0';
242 }
243 }, {
244 key: 'branch',
245 get: function get() {
246 if (this._env.PERCY_BRANCH) {
247 return this._env.PERCY_BRANCH;
248 }
249 var result = '';
250 switch (this.ci) {
251 case 'travis':
252 if (this.pullRequestNumber && this._env.TRAVIS_PULL_REQUEST_BRANCH) {
253 result = this._env.TRAVIS_PULL_REQUEST_BRANCH;
254 } else {
255 result = this._env.TRAVIS_BRANCH;
256 }
257 break;
258 case 'jenkins-prb':
259 result = this._env.ghprbSourceBranch;
260 break;
261 case 'jenkins':
262 result = this._env.CHANGE_BRANCH || this._env.GIT_BRANCH;
263 break;
264 case 'circle':
265 result = this._env.CIRCLE_BRANCH;
266 break;
267 case 'codeship':
268 result = this._env.CI_BRANCH;
269 break;
270 case 'drone':
271 result = this._env.DRONE_BRANCH;
272 break;
273 case 'semaphore':
274 result = this._env.BRANCH_NAME;
275 break;
276 case 'buildkite':
277 result = this._env.BUILDKITE_BRANCH;
278 break;
279 case 'heroku':
280 result = this._env.HEROKU_TEST_RUN_BRANCH;
281 break;
282 case 'gitlab':
283 result = this._env.CI_COMMIT_REF_NAME;
284 break;
285 case 'azure':
286 result = this._env.SYSTEM_PULLREQUEST_SOURCEBRANCH || this._env.BUILD_SOURCEBRANCHNAME;
287 break;
288 case 'appveyor':
289 result = this._env.APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH || this._env.APPVEYOR_REPO_BRANCH;
290 break;
291 case 'probo':
292 result = this._env.BRANCH_NAME;
293 break;
294 case 'bitbucket':
295 result = this._env.BITBUCKET_BRANCH;
296 break;
297 }
298
299 if (result == '') {
300 result = this.rawBranch();
301 }
302
303 if (result == '') {
304 // Branch not specified
305 result = null;
306 }
307
308 return result;
309 }
310 }, {
311 key: 'targetBranch',
312 get: function get() {
313 return this._env.PERCY_TARGET_BRANCH || null;
314 }
315 }, {
316 key: 'pullRequestNumber',
317 get: function get() {
318 if (this._env.PERCY_PULL_REQUEST) {
319 return this._env.PERCY_PULL_REQUEST;
320 }
321 switch (this.ci) {
322 case 'travis':
323 return this._env.TRAVIS_PULL_REQUEST !== 'false' ? this._env.TRAVIS_PULL_REQUEST : null;
324 case 'jenkins-prb':
325 return this._env.ghprbPullId;
326 case 'jenkins':
327 return this._env.CHANGE_ID || null;
328 case 'circle':
329 if (this._env.CI_PULL_REQUESTS && this._env.CI_PULL_REQUESTS !== '') {
330 return this._env.CI_PULL_REQUESTS.split('/').slice(-1)[0];
331 }
332 break;
333 case 'codeship':
334 // Unfortunately, codeship always returns 'false' for CI_PULL_REQUEST. For now, return null.
335 break;
336 case 'drone':
337 return this._env.CI_PULL_REQUEST;
338 case 'semaphore':
339 return this._env.PULL_REQUEST_NUMBER;
340 case 'buildkite':
341 return this._env.BUILDKITE_PULL_REQUEST !== 'false' ? this._env.BUILDKITE_PULL_REQUEST : null;
342 case 'gitlab':
343 return this._env.CI_MERGE_REQUEST_IID || null;
344 case 'azure':
345 return this._env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER || null;
346 case 'appveyor':
347 return this._env.APPVEYOR_PULL_REQUEST_NUMBER || null;
348 case 'probo':
349 if (this._env.PULL_REQUEST_LINK && this._env.PULL_REQUEST_LINK !== '') {
350 return this._env.PULL_REQUEST_LINK.split('/').slice(-1)[0];
351 }
352 break;
353 case 'bitbucket':
354 return this._env.BITBUCKET_PR_ID || null;
355 }
356 return null;
357 }
358
359 // A nonce which will be the same for all nodes of a parallel build, used to identify shards
360 // of the same CI build. This is usually just the CI environment build ID.
361
362 }, {
363 key: 'parallelNonce',
364 get: function get() {
365 if (this._env.PERCY_PARALLEL_NONCE) {
366 return this._env.PERCY_PARALLEL_NONCE;
367 }
368 switch (this.ci) {
369 case 'travis':
370 return this._env.TRAVIS_BUILD_NUMBER;
371 case 'jenkins-prb':
372 return this._env.BUILD_NUMBER;
373 case 'jenkins':
374 if (this._env.BUILD_TAG) {
375 return utils.reverseString(this._env.BUILD_TAG).substring(0, 60);
376 }
377 break;
378 case 'circle':
379 return this._env.CIRCLE_WORKFLOW_ID || this._env.CIRCLE_BUILD_NUM;
380 case 'codeship':
381 return this._env.CI_BUILD_NUMBER || this._env.CI_BUILD_ID;
382 case 'drone':
383 return this._env.DRONE_BUILD_NUMBER;
384 case 'semaphore':
385 return this._env.SEMAPHORE_BRANCH_ID + '/' + this._env.SEMAPHORE_BUILD_NUMBER;
386 case 'buildkite':
387 return this._env.BUILDKITE_BUILD_ID;
388 case 'heroku':
389 return this._env.HEROKU_TEST_RUN_ID;
390 case 'gitlab':
391 return this._env.CI_PIPELINE_ID;
392 case 'azure':
393 return this._env.BUILD_BUILDID;
394 case 'appveyor':
395 return this._env.APPVEYOR_BUILD_ID;
396 case 'probo':
397 return this._env.BUILD_ID;
398 case 'bitbucket':
399 return this._env.BITBUCKET_BUILD_NUMBER;
400 }
401 return null;
402 }
403 }, {
404 key: 'parallelTotalShards',
405 get: function get() {
406 if (this._env.PERCY_PARALLEL_TOTAL) {
407 return parseInt(this._env.PERCY_PARALLEL_TOTAL);
408 }
409 switch (this.ci) {
410 case 'travis':
411 // Support for https://github.com/ArturT/knapsack
412 if (this._env.CI_NODE_TOTAL) {
413 return parseInt(this._env.CI_NODE_TOTAL);
414 }
415 break;
416 case 'jenkins-prb':
417 break;
418 case 'jenkins':
419 break;
420 case 'circle':
421 if (this._env.CIRCLE_NODE_TOTAL) {
422 return parseInt(this._env.CIRCLE_NODE_TOTAL);
423 }
424 break;
425 case 'codeship':
426 if (this._env.CI_NODE_TOTAL) {
427 return parseInt(this._env.CI_NODE_TOTAL);
428 }
429 break;
430 case 'drone':
431 break;
432 case 'semaphore':
433 if (this._env.SEMAPHORE_THREAD_COUNT) {
434 return parseInt(this._env.SEMAPHORE_THREAD_COUNT);
435 }
436 break;
437 case 'buildkite':
438 if (this._env.BUILDKITE_PARALLEL_JOB_COUNT) {
439 return parseInt(this._env.BUILDKITE_PARALLEL_JOB_COUNT);
440 }
441 break;
442 case 'heroku':
443 if (this._env.CI_NODE_TOTAL) {
444 return parseInt(this._env.CI_NODE_TOTAL);
445 }
446 break;
447 case 'azure':
448 // SYSTEM_TOTALJOBSINPHASE is set for parallel builds and non-parallel matrix builds, so
449 // check build strategy is parallel by ensuring SYSTEM_PARALLELEXECUTIONTYPE == MultiMachine
450 if (this._env.SYSTEM_PARALLELEXECUTIONTYPE == 'MultiMachine' && this._env.SYSTEM_TOTALJOBSINPHASE) {
451 return parseInt(this._env.SYSTEM_TOTALJOBSINPHASE);
452 }
453 break;
454 }
455 return null;
456 }
457 }]);
458
459 return Environment;
460}();
461
462module.exports = Environment;
\No newline at end of file