UNPKG

16.2 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.GITHUB_ACTIONS == 'true') {
95 return 'github';
96 } else if (this._env.CI) {
97 // this should always be the last branch
98 return 'CI/unknown';
99 }
100
101 return null;
102 }
103 }, {
104 key: 'ciVersion',
105 get: function get() {
106 switch (this.ci) {
107 case 'gitlab':
108 return 'gitlab/' + this._env.CI_SERVER_VERSION;
109 case 'github':
110 return 'github/' + (this._env.PERCY_GITHUB_ACTION || 'unkown');
111 }
112 return this.ci;
113 }
114 }, {
115 key: 'commitData',
116 get: function get() {
117 // Read the result from environment data
118 var result = {
119 branch: this.branch,
120 sha: this.commitSha,
121
122 // These GIT_ environment vars are from the Jenkins Git Plugin, but could be
123 // used generically. This behavior may change in the future.
124 authorName: this._env['GIT_AUTHOR_NAME'],
125 authorEmail: this._env['GIT_AUTHOR_EMAIL'],
126 committerName: this._env['GIT_COMMITTER_NAME'],
127 committerEmail: this._env['GIT_COMMITTER_EMAIL']
128 };
129
130 // Try and get more meta-data from git
131 var formattedCommitData = '';
132 if (this.commitSha) {
133 formattedCommitData = this.rawCommitData(this.commitSha);
134 }
135 if (!formattedCommitData) {
136 formattedCommitData = this.rawCommitData('HEAD');
137 }
138 if (!formattedCommitData) {
139 return result;
140 }
141
142 // If this.commitSha didn't provide a sha, use the one from the commit
143 if (!result.sha) {
144 result.sha = this.parse(formattedCommitData, /COMMIT_SHA:(.*)/);
145 }
146
147 result.message = this.parse(formattedCommitData, /COMMIT_MESSAGE:(.*)/m);
148 result.committedAt = this.parse(formattedCommitData, /COMMITTED_DATE:(.*)/);
149 result.authorName = this.parse(formattedCommitData, /AUTHOR_NAME:(.*)/);
150 result.authorEmail = this.parse(formattedCommitData, /AUTHOR_EMAIL:(.*)/);
151 result.committerName = this.parse(formattedCommitData, /COMMITTER_NAME:(.*)/);
152 result.committerEmail = this.parse(formattedCommitData, /COMMITTER_EMAIL:(.*)/);
153
154 return result;
155 }
156 }, {
157 key: 'jenkinsMergeCommitBuild',
158 get: function get() {
159 var formattedCommitData = this.rawCommitData('HEAD');
160
161 if (!formattedCommitData) {
162 return false;
163 }
164
165 var authorName = this.parse(formattedCommitData, /AUTHOR_NAME:(.*)/);
166 var authorEmail = this.parse(formattedCommitData, /AUTHOR_EMAIL:(.*)/);
167 var message = this.parse(formattedCommitData, /COMMIT_MESSAGE:(.*)/m);
168
169 if (authorName === 'Jenkins' && authorEmail === 'nobody@nowhere') {
170 // Example merge message: Merge commit 'ec4d24c3d22f3c95e34af95c1fda2d462396d885' into HEAD
171 if (message.substring(0, 13) === 'Merge commit ' && message.substring(55) === ' into HEAD') {
172 return true;
173 }
174 }
175
176 return false;
177 }
178 }, {
179 key: 'secondToLastCommitSHA',
180 get: function get() {
181 var formattedCommitData = this.rawCommitData('HEAD^');
182
183 if (!formattedCommitData) {
184 return null;
185 }
186
187 return this.parse(formattedCommitData, /COMMIT_SHA:(.*)/);
188 }
189 }, {
190 key: 'commitSha',
191 get: function get() {
192 if (this._env.PERCY_COMMIT) {
193 return this._env.PERCY_COMMIT;
194 }
195 switch (this.ci) {
196 case 'travis':
197 return this._env.TRAVIS_COMMIT;
198 case 'jenkins-prb':
199 // Pull Request Builder Plugin OR Git Plugin.
200 return this._env.ghprbActualCommit || this._env.GIT_COMMIT;
201 case 'jenkins':
202 if (this.jenkinsMergeCommitBuild) {
203 return this.secondToLastCommitSHA;
204 }
205 return this._env.GIT_COMMIT;
206 case 'circle':
207 return this._env.CIRCLE_SHA1;
208 case 'codeship':
209 return this._env.CI_COMMIT_ID;
210 case 'drone':
211 return this._env.DRONE_COMMIT;
212 case 'semaphore':
213 return this._env.REVISION;
214 case 'buildkite':
215 {
216 var commitSha = this._env.BUILDKITE_COMMIT;
217 // Buildkite mixes SHAs and non-SHAs in BUILDKITE_COMMIT, so we return null if non-SHA.
218 return commitSha !== 'HEAD' ? this._env.BUILDKITE_COMMIT : null;
219 }
220 case 'heroku':
221 return this._env.HEROKU_TEST_RUN_COMMIT_VERSION;
222 case 'gitlab':
223 return this._env.CI_COMMIT_SHA;
224 case 'azure':
225 return this._env.SYSTEM_PULLREQUEST_SOURCECOMMITID || this._env.BUILD_SOURCEVERSION;
226 case 'appveyor':
227 return this._env.APPVEYOR_PULL_REQUEST_HEAD_COMMIT || this._env.APPVEYOR_REPO_COMMIT;
228 case 'probo':
229 return this._env.COMMIT_REF;
230 case 'bitbucket':
231 return this._env.BITBUCKET_COMMIT;
232 case 'github':
233 return this._env.GITHUB_SHA;
234 }
235
236 return null;
237 }
238 }, {
239 key: 'targetCommitSha',
240 get: function get() {
241 return this._env.PERCY_TARGET_COMMIT || null;
242 }
243 }, {
244 key: 'partialBuild',
245 get: function get() {
246 var partial = this._env.PERCY_PARTIAL_BUILD;
247 return !!partial && partial !== '0';
248 }
249 }, {
250 key: 'branch',
251 get: function get() {
252 if (this._env.PERCY_BRANCH) {
253 return this._env.PERCY_BRANCH;
254 }
255 var result = '';
256 switch (this.ci) {
257 case 'travis':
258 if (this.pullRequestNumber && this._env.TRAVIS_PULL_REQUEST_BRANCH) {
259 result = this._env.TRAVIS_PULL_REQUEST_BRANCH;
260 } else {
261 result = this._env.TRAVIS_BRANCH;
262 }
263 break;
264 case 'jenkins-prb':
265 result = this._env.ghprbSourceBranch;
266 break;
267 case 'jenkins':
268 result = this._env.CHANGE_BRANCH || this._env.GIT_BRANCH;
269 break;
270 case 'circle':
271 result = this._env.CIRCLE_BRANCH;
272 break;
273 case 'codeship':
274 result = this._env.CI_BRANCH;
275 break;
276 case 'drone':
277 result = this._env.DRONE_BRANCH;
278 break;
279 case 'semaphore':
280 result = this._env.BRANCH_NAME;
281 break;
282 case 'buildkite':
283 result = this._env.BUILDKITE_BRANCH;
284 break;
285 case 'heroku':
286 result = this._env.HEROKU_TEST_RUN_BRANCH;
287 break;
288 case 'gitlab':
289 result = this._env.CI_COMMIT_REF_NAME;
290 break;
291 case 'azure':
292 result = this._env.SYSTEM_PULLREQUEST_SOURCEBRANCH || this._env.BUILD_SOURCEBRANCHNAME;
293 break;
294 case 'appveyor':
295 result = this._env.APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH || this._env.APPVEYOR_REPO_BRANCH;
296 break;
297 case 'probo':
298 result = this._env.BRANCH_NAME;
299 break;
300 case 'bitbucket':
301 result = this._env.BITBUCKET_BRANCH;
302 break;
303 case 'github':
304 if (this._env.GITHUB_REF && this._env.GITHUB_REF.match(/^refs\//)) {
305 result = this._env.GITHUB_REF.replace(/^refs\/\w+?\//, '');
306 } else {
307 result = this._env.GITHUB_REF;
308 }
309 break;
310 }
311
312 if (result == '') {
313 result = this.rawBranch();
314 }
315
316 if (result == '') {
317 // Branch not specified
318 result = null;
319 }
320
321 return result;
322 }
323 }, {
324 key: 'targetBranch',
325 get: function get() {
326 return this._env.PERCY_TARGET_BRANCH || null;
327 }
328 }, {
329 key: 'pullRequestNumber',
330 get: function get() {
331 if (this._env.PERCY_PULL_REQUEST) {
332 return this._env.PERCY_PULL_REQUEST;
333 }
334 switch (this.ci) {
335 case 'travis':
336 return this._env.TRAVIS_PULL_REQUEST !== 'false' ? this._env.TRAVIS_PULL_REQUEST : null;
337 case 'jenkins-prb':
338 return this._env.ghprbPullId;
339 case 'jenkins':
340 return this._env.CHANGE_ID || null;
341 case 'circle':
342 if (this._env.CI_PULL_REQUESTS && this._env.CI_PULL_REQUESTS !== '') {
343 return this._env.CI_PULL_REQUESTS.split('/').slice(-1)[0];
344 }
345 break;
346 case 'codeship':
347 // Unfortunately, codeship always returns 'false' for CI_PULL_REQUEST. For now, return null.
348 break;
349 case 'drone':
350 return this._env.CI_PULL_REQUEST;
351 case 'semaphore':
352 return this._env.PULL_REQUEST_NUMBER;
353 case 'buildkite':
354 return this._env.BUILDKITE_PULL_REQUEST !== 'false' ? this._env.BUILDKITE_PULL_REQUEST : null;
355 case 'gitlab':
356 return this._env.CI_MERGE_REQUEST_IID || null;
357 case 'azure':
358 return this._env.SYSTEM_PULLREQUEST_PULLREQUESTNUMBER || null;
359 case 'appveyor':
360 return this._env.APPVEYOR_PULL_REQUEST_NUMBER || null;
361 case 'probo':
362 if (this._env.PULL_REQUEST_LINK && this._env.PULL_REQUEST_LINK !== '') {
363 return this._env.PULL_REQUEST_LINK.split('/').slice(-1)[0];
364 }
365 break;
366 case 'bitbucket':
367 return this._env.BITBUCKET_PR_ID || null;
368 }
369 return null;
370 }
371
372 // A nonce which will be the same for all nodes of a parallel build, used to identify shards
373 // of the same CI build. This is usually just the CI environment build ID.
374
375 }, {
376 key: 'parallelNonce',
377 get: function get() {
378 if (this._env.PERCY_PARALLEL_NONCE) {
379 return this._env.PERCY_PARALLEL_NONCE;
380 }
381 switch (this.ci) {
382 case 'travis':
383 return this._env.TRAVIS_BUILD_NUMBER;
384 case 'jenkins-prb':
385 return this._env.BUILD_NUMBER;
386 case 'jenkins':
387 if (this._env.BUILD_TAG) {
388 return utils.reverseString(this._env.BUILD_TAG).substring(0, 60);
389 }
390 break;
391 case 'circle':
392 return this._env.CIRCLE_WORKFLOW_ID || this._env.CIRCLE_BUILD_NUM;
393 case 'codeship':
394 return this._env.CI_BUILD_NUMBER || this._env.CI_BUILD_ID;
395 case 'drone':
396 return this._env.DRONE_BUILD_NUMBER;
397 case 'semaphore':
398 return this._env.SEMAPHORE_BRANCH_ID + '/' + this._env.SEMAPHORE_BUILD_NUMBER;
399 case 'buildkite':
400 return this._env.BUILDKITE_BUILD_ID;
401 case 'heroku':
402 return this._env.HEROKU_TEST_RUN_ID;
403 case 'gitlab':
404 return this._env.CI_PIPELINE_ID;
405 case 'azure':
406 return this._env.BUILD_BUILDID;
407 case 'appveyor':
408 return this._env.APPVEYOR_BUILD_ID;
409 case 'probo':
410 return this._env.BUILD_ID;
411 case 'bitbucket':
412 return this._env.BITBUCKET_BUILD_NUMBER;
413 }
414 return null;
415 }
416 }, {
417 key: 'parallelTotalShards',
418 get: function get() {
419 if (this._env.PERCY_PARALLEL_TOTAL) {
420 return parseInt(this._env.PERCY_PARALLEL_TOTAL);
421 }
422 switch (this.ci) {
423 case 'travis':
424 // Support for https://github.com/ArturT/knapsack
425 if (this._env.CI_NODE_TOTAL) {
426 return parseInt(this._env.CI_NODE_TOTAL);
427 }
428 break;
429 case 'jenkins-prb':
430 break;
431 case 'jenkins':
432 break;
433 case 'circle':
434 if (this._env.CIRCLE_NODE_TOTAL) {
435 return parseInt(this._env.CIRCLE_NODE_TOTAL);
436 }
437 break;
438 case 'codeship':
439 if (this._env.CI_NODE_TOTAL) {
440 return parseInt(this._env.CI_NODE_TOTAL);
441 }
442 break;
443 case 'drone':
444 break;
445 case 'semaphore':
446 if (this._env.SEMAPHORE_THREAD_COUNT) {
447 return parseInt(this._env.SEMAPHORE_THREAD_COUNT);
448 }
449 break;
450 case 'buildkite':
451 if (this._env.BUILDKITE_PARALLEL_JOB_COUNT) {
452 return parseInt(this._env.BUILDKITE_PARALLEL_JOB_COUNT);
453 }
454 break;
455 case 'heroku':
456 if (this._env.CI_NODE_TOTAL) {
457 return parseInt(this._env.CI_NODE_TOTAL);
458 }
459 break;
460 case 'azure':
461 // SYSTEM_TOTALJOBSINPHASE is set for parallel builds and non-parallel matrix builds, so
462 // check build strategy is parallel by ensuring SYSTEM_PARALLELEXECUTIONTYPE == MultiMachine
463 if (this._env.SYSTEM_PARALLELEXECUTIONTYPE == 'MultiMachine' && this._env.SYSTEM_TOTALJOBSINPHASE) {
464 return parseInt(this._env.SYSTEM_TOTALJOBSINPHASE);
465 }
466 break;
467 }
468 return null;
469 }
470 }]);
471
472 return Environment;
473}();
474
475module.exports = Environment;
\No newline at end of file