@percy/env 1.32.11-beta.1 → 1.32.11-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,14 @@
1
+ import os from 'os';
1
2
  import { getCommitData, getJenkinsSha, github } from './utils.js';
2
3
  import logger from '@percy/logger';
4
+
5
+ // machine ids are capped and restricted to header-safe characters on both
6
+ // sides; percy-api rejects anything longer or outside this alphabet
7
+ const MACHINE_ID_MAX_LENGTH = 128;
8
+ function machineToken(value) {
9
+ if (value == null || value === '') return null;
10
+ return String(value).replace(/[^A-Za-z0-9._-]/g, '-');
11
+ }
3
12
  export class PercyEnv {
4
13
  constructor(vars = process.env) {
5
14
  this.vars = vars;
@@ -384,6 +393,70 @@ export class PercyEnv {
384
393
  return !!partial && partial !== '0';
385
394
  }
386
395
 
396
+ // machine identity for slow-build diagnostics (dead-CI-machine detection on
397
+ // parallel builds). percy-api validates all of these server-side and may
398
+ // discard any of them. Deliberately excluded from the getter debug logging
399
+ // below (like `token`) — env debug logs are uploaded with build logs, and
400
+ // hostnames should not ride along in them.
401
+ get machine() {
402
+ let hostname = null;
403
+ try {
404
+ hostname = os.hostname() || null;
405
+ } catch {
406
+ hostname = null;
407
+ }
408
+
409
+ // the per-shard index for providers that can run several shards on one
410
+ // host; without it every shard on that host would share a machine id and a
411
+ // dead shard would look alive as long as any sibling kept uploading
412
+ let index = null;
413
+ let runUrl = null;
414
+ switch (this.ci) {
415
+ case 'circle':
416
+ index = this.vars.CIRCLE_NODE_INDEX;
417
+ runUrl = this.vars.CIRCLE_BUILD_URL;
418
+ break;
419
+ case 'buildkite':
420
+ index = this.vars.BUILDKITE_PARALLEL_JOB;
421
+ // the build url is shared by every parallel job; the job id anchor is
422
+ // what lets the "stopped responding" link land on the dead agent's log
423
+ runUrl = this.vars.BUILDKITE_BUILD_URL && this.vars.BUILDKITE_JOB_ID ? `${this.vars.BUILDKITE_BUILD_URL}#${this.vars.BUILDKITE_JOB_ID}` : this.vars.BUILDKITE_BUILD_URL;
424
+ break;
425
+ case 'github':
426
+ runUrl = this.vars.GITHUB_SERVER_URL && this.vars.GITHUB_REPOSITORY && this.vars.GITHUB_RUN_ID ? `${this.vars.GITHUB_SERVER_URL}/${this.vars.GITHUB_REPOSITORY}/actions/runs/${this.vars.GITHUB_RUN_ID}` : null;
427
+ break;
428
+ case 'gitlab':
429
+ index = this.vars.CI_NODE_INDEX;
430
+ runUrl = this.vars.CI_JOB_URL;
431
+ break;
432
+ case 'jenkins':
433
+ case 'jenkins-prb':
434
+ index = this.vars.EXECUTOR_NUMBER;
435
+ break;
436
+ }
437
+
438
+ // stable id: sanitized hostname, suffixed with the sanitized shard index.
439
+ // The id travels as an HTTP header value on every snapshot POST, so every
440
+ // part of it must be header-safe — an unsanitized index with a stray
441
+ // newline would reject the whole upload.
442
+ let id = machineToken(hostname);
443
+ let shard = machineToken(index);
444
+ if (id && shard) id = `${id}.n${shard}`;
445
+ if (id) id = id.slice(0, MACHINE_ID_MAX_LENGTH);
446
+ // a hostname with no ASCII alphanumerics sanitizes to dashes alone, which
447
+ // identifies nothing and would collide across hosts — better no id at all
448
+ if (id && !/[A-Za-z0-9]/.test(id)) id = null;
449
+ return {
450
+ id,
451
+ hostname,
452
+ runUrl: runUrl || null,
453
+ // which CI product the agent belongs to ("jenkins", "buildkite", ...);
454
+ // percy-web maps it to a display name in the stopped-responding copy.
455
+ // The generic CI/unknown marker is not a product and is dropped.
456
+ platform: this.ci && this.ci !== 'CI/unknown' ? this.ci : null
457
+ };
458
+ }
459
+
387
460
  // percy token
388
461
  get token() {
389
462
  return this.vars.PERCY_TOKEN || null;
@@ -422,7 +495,9 @@ Object.defineProperties(PercyEnv.prototype, Object.entries(Object.getOwnProperty
422
495
  Object.defineProperty(this, key, {
423
496
  value
424
497
  });
425
- if (key !== 'token') {
498
+ // `machine` carries a hostname and these debug logs are uploaded
499
+ // with build logs — keep it out, like the token.
500
+ if (key !== 'token' && key !== 'machine') {
426
501
  this.log.debug(`Detected ${key} as ${JSON.stringify(value)}`);
427
502
  }
428
503
  return value;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@percy/env",
3
- "version": "1.32.11-beta.1",
3
+ "version": "1.32.11-beta.2",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -32,7 +32,7 @@
32
32
  "test:coverage": "yarn test --coverage"
33
33
  },
34
34
  "dependencies": {
35
- "@percy/logger": "1.32.11-beta.1"
35
+ "@percy/logger": "1.32.11-beta.2"
36
36
  },
37
- "gitHead": "c72c103d81636a59792393576a82c9471686c3c0"
37
+ "gitHead": "a05b0ac1efc0d136fa15c2330b55e1e207c6e797"
38
38
  }