UNPKG

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