@dash0/sdk-web 0.20.0 → 0.21.0
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.
- package/dist/dash0.iife.js +1 -1
- package/dist/dash0.iife.js.map +1 -1
- package/dist/dash0.js +1 -1
- package/dist/dash0.js.map +1 -1
- package/dist/dash0.umd.cjs +1 -1
- package/dist/dash0.umd.cjs.map +1 -1
- package/dist/modules/api/init.js +2 -0
- package/dist/modules/api/init_test.js +220 -1
- package/dist/modules/api/vcs.js +196 -0
- package/dist/modules/semantic-conventions.js +8 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/api/vcs.d.ts +10 -0
- package/dist/types/semantic-conventions.d.ts +7 -0
- package/dist/types/types/options.d.ts +53 -0
- package/package.json +1 -1
- package/src/api/init.ts +3 -0
- package/src/api/init_test.ts +288 -1
- package/src/api/vcs.ts +359 -0
- package/src/semantic-conventions.ts +9 -0
- package/src/types/options.ts +56 -0
package/src/api/init_test.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
2
|
import { InitOptions, InstrumentationName } from "../types/options";
|
|
3
3
|
import { PropagatorConfig, Vars } from "../vars";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
SERVICE_NAME,
|
|
6
|
+
SERVICE_NAMESPACE,
|
|
7
|
+
VCS_CHANGE_ID,
|
|
8
|
+
VCS_OWNER_NAME,
|
|
9
|
+
VCS_PROVIDER_NAME,
|
|
10
|
+
VCS_REF_HEAD_NAME,
|
|
11
|
+
VCS_REF_HEAD_REVISION,
|
|
12
|
+
VCS_REPOSITORY_NAME,
|
|
13
|
+
VCS_REPOSITORY_URL_FULL,
|
|
14
|
+
} from "../semantic-conventions";
|
|
5
15
|
import { init as initFun } from "./init";
|
|
6
16
|
|
|
7
17
|
// Mock all the instrumentation modules
|
|
@@ -397,4 +407,281 @@ describe("init", () => {
|
|
|
397
407
|
expect(serviceNameAttr?.value.stringValue).toBe("unknown");
|
|
398
408
|
});
|
|
399
409
|
});
|
|
410
|
+
|
|
411
|
+
describe("vcs auto-detection", () => {
|
|
412
|
+
afterEach(() => {
|
|
413
|
+
vi.unstubAllEnvs();
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
const stringAttr = (key: string) => vars.resource.attributes.find((attr) => attr.key === key)?.value.stringValue;
|
|
417
|
+
|
|
418
|
+
it("derives full vcs resource attributes from Vercel env vars", () => {
|
|
419
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_PROVIDER", "github");
|
|
420
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER", "dash0hq");
|
|
421
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG", "dash0");
|
|
422
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF", "main");
|
|
423
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA", "abc123");
|
|
424
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID", "42");
|
|
425
|
+
|
|
426
|
+
init(baseOptions);
|
|
427
|
+
|
|
428
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBe("github");
|
|
429
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBe("dash0hq");
|
|
430
|
+
expect(stringAttr(VCS_REPOSITORY_NAME)).toBe("dash0");
|
|
431
|
+
expect(stringAttr(VCS_REPOSITORY_URL_FULL)).toBe("https://github.com/dash0hq/dash0");
|
|
432
|
+
expect(stringAttr(VCS_REF_HEAD_NAME)).toBe("main");
|
|
433
|
+
expect(stringAttr(VCS_REF_HEAD_REVISION)).toBe("abc123");
|
|
434
|
+
expect(stringAttr(VCS_CHANGE_ID)).toBe("42");
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
// The 9 framework prefixes Vercel auto-prefixes its VERCEL_GIT_* system
|
|
438
|
+
// vars under (per https://vercel.com/docs/environment-variables/framework-environment-variables).
|
|
439
|
+
// A passing test for each confirms the literal-accessor enumeration in
|
|
440
|
+
// detectVcsFromVercel is actually wired up.
|
|
441
|
+
const FRAMEWORK_PREFIX_SAMPLES: Array<[label: string, prefix: string]> = [
|
|
442
|
+
["Next.js", "NEXT_PUBLIC_"],
|
|
443
|
+
["Nuxt", "NUXT_ENV_"],
|
|
444
|
+
["Create React App", "REACT_APP_"],
|
|
445
|
+
["Gatsby", "GATSBY_"],
|
|
446
|
+
["Vite", "VITE_"],
|
|
447
|
+
["Astro / SvelteKit / Hydrogen", "PUBLIC_"],
|
|
448
|
+
["Vue CLI", "VUE_APP_"],
|
|
449
|
+
["RedwoodJS", "REDWOOD_ENV_"],
|
|
450
|
+
["Sanity Studio", "SANITY_STUDIO_"],
|
|
451
|
+
];
|
|
452
|
+
|
|
453
|
+
it.each(FRAMEWORK_PREFIX_SAMPLES)(
|
|
454
|
+
"derives Vercel vcs attributes from %s framework prefix (%s)",
|
|
455
|
+
(_label, prefix) => {
|
|
456
|
+
vi.stubEnv(`${prefix}VERCEL_GIT_PROVIDER`, "github");
|
|
457
|
+
vi.stubEnv(`${prefix}VERCEL_GIT_REPO_OWNER`, "dash0hq");
|
|
458
|
+
vi.stubEnv(`${prefix}VERCEL_GIT_REPO_SLUG`, "dash0");
|
|
459
|
+
vi.stubEnv(`${prefix}VERCEL_GIT_COMMIT_REF`, "main");
|
|
460
|
+
vi.stubEnv(`${prefix}VERCEL_GIT_COMMIT_SHA`, "abc123");
|
|
461
|
+
vi.stubEnv(`${prefix}VERCEL_GIT_PULL_REQUEST_ID`, "42");
|
|
462
|
+
|
|
463
|
+
init(baseOptions);
|
|
464
|
+
|
|
465
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBe("github");
|
|
466
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBe("dash0hq");
|
|
467
|
+
expect(stringAttr(VCS_REPOSITORY_NAME)).toBe("dash0");
|
|
468
|
+
expect(stringAttr(VCS_REPOSITORY_URL_FULL)).toBe("https://github.com/dash0hq/dash0");
|
|
469
|
+
expect(stringAttr(VCS_REF_HEAD_NAME)).toBe("main");
|
|
470
|
+
expect(stringAttr(VCS_REF_HEAD_REVISION)).toBe("abc123");
|
|
471
|
+
expect(stringAttr(VCS_CHANGE_ID)).toBe("42");
|
|
472
|
+
}
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
it.each(FRAMEWORK_PREFIX_SAMPLES)(
|
|
476
|
+
"derives Netlify vcs attributes from %s framework prefix (%s)",
|
|
477
|
+
(_label, prefix) => {
|
|
478
|
+
vi.stubEnv(`${prefix}REPOSITORY_URL`, "https://github.com/dash0hq/dash0");
|
|
479
|
+
vi.stubEnv(`${prefix}BRANCH`, "feature/x");
|
|
480
|
+
vi.stubEnv(`${prefix}COMMIT_REF`, "def456");
|
|
481
|
+
vi.stubEnv(`${prefix}REVIEW_ID`, "99");
|
|
482
|
+
|
|
483
|
+
init(baseOptions);
|
|
484
|
+
|
|
485
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBe("github");
|
|
486
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBe("dash0hq");
|
|
487
|
+
expect(stringAttr(VCS_REPOSITORY_NAME)).toBe("dash0");
|
|
488
|
+
expect(stringAttr(VCS_REF_HEAD_NAME)).toBe("feature/x");
|
|
489
|
+
expect(stringAttr(VCS_REF_HEAD_REVISION)).toBe("def456");
|
|
490
|
+
expect(stringAttr(VCS_CHANGE_ID)).toBe("99");
|
|
491
|
+
}
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
it("derives vcs resource attributes from Netlify env vars (parsed REPOSITORY_URL)", () => {
|
|
495
|
+
vi.stubEnv("NEXT_PUBLIC_REPOSITORY_URL", "https://github.com/dash0hq/dash0.git");
|
|
496
|
+
vi.stubEnv("NEXT_PUBLIC_BRANCH", "feature/x");
|
|
497
|
+
vi.stubEnv("NEXT_PUBLIC_COMMIT_REF", "def456");
|
|
498
|
+
vi.stubEnv("NEXT_PUBLIC_REVIEW_ID", "99");
|
|
499
|
+
|
|
500
|
+
init(baseOptions);
|
|
501
|
+
|
|
502
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBe("github");
|
|
503
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBe("dash0hq");
|
|
504
|
+
expect(stringAttr(VCS_REPOSITORY_NAME)).toBe("dash0");
|
|
505
|
+
expect(stringAttr(VCS_REPOSITORY_URL_FULL)).toBe("https://github.com/dash0hq/dash0.git");
|
|
506
|
+
expect(stringAttr(VCS_REF_HEAD_NAME)).toBe("feature/x");
|
|
507
|
+
expect(stringAttr(VCS_REF_HEAD_REVISION)).toBe("def456");
|
|
508
|
+
expect(stringAttr(VCS_CHANGE_ID)).toBe("99");
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
it("prefers Vercel values over Netlify values when both are present", () => {
|
|
512
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_PROVIDER", "github");
|
|
513
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER", "vercel-owner");
|
|
514
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG", "vercel-repo");
|
|
515
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF", "vercel-branch");
|
|
516
|
+
vi.stubEnv("NEXT_PUBLIC_REPOSITORY_URL", "https://github.com/netlify-owner/netlify-repo");
|
|
517
|
+
vi.stubEnv("NEXT_PUBLIC_BRANCH", "netlify-branch");
|
|
518
|
+
|
|
519
|
+
init(baseOptions);
|
|
520
|
+
|
|
521
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBe("vercel-owner");
|
|
522
|
+
expect(stringAttr(VCS_REPOSITORY_NAME)).toBe("vercel-repo");
|
|
523
|
+
expect(stringAttr(VCS_REF_HEAD_NAME)).toBe("vercel-branch");
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
it("opts.vcs overrides individual auto-detected fields", () => {
|
|
527
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_PROVIDER", "github");
|
|
528
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER", "dash0hq");
|
|
529
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG", "dash0");
|
|
530
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF", "main");
|
|
531
|
+
|
|
532
|
+
init({
|
|
533
|
+
...baseOptions,
|
|
534
|
+
vcs: {
|
|
535
|
+
refHeadName: "release/2026.06",
|
|
536
|
+
changeId: "1234",
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBe("dash0hq");
|
|
541
|
+
expect(stringAttr(VCS_REF_HEAD_NAME)).toBe("release/2026.06");
|
|
542
|
+
expect(stringAttr(VCS_CHANGE_ID)).toBe("1234");
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
it("disableVcsDetection: true emits no vcs.* resource attributes even when env vars are set", () => {
|
|
546
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_PROVIDER", "github");
|
|
547
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER", "dash0hq");
|
|
548
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA", "abc123");
|
|
549
|
+
|
|
550
|
+
init({ ...baseOptions, disableVcsDetection: true });
|
|
551
|
+
|
|
552
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBeUndefined();
|
|
553
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBeUndefined();
|
|
554
|
+
expect(stringAttr(VCS_REF_HEAD_REVISION)).toBeUndefined();
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
it("disableVcsDetection: true still applies opts.vcs manual overrides", () => {
|
|
558
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_PROVIDER", "github");
|
|
559
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER", "dash0hq");
|
|
560
|
+
|
|
561
|
+
init({
|
|
562
|
+
...baseOptions,
|
|
563
|
+
disableVcsDetection: true,
|
|
564
|
+
vcs: { providerName: "gitlab", refHeadName: "release/1.0" },
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
// env-var values are suppressed
|
|
568
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBeUndefined();
|
|
569
|
+
// manual overrides still win
|
|
570
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBe("gitlab");
|
|
571
|
+
expect(stringAttr(VCS_REF_HEAD_NAME)).toBe("release/1.0");
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
it("applies opts.vcs fields as the sole source when no env vars are set", () => {
|
|
575
|
+
init({
|
|
576
|
+
...baseOptions,
|
|
577
|
+
vcs: {
|
|
578
|
+
providerName: "github",
|
|
579
|
+
ownerName: "my-org",
|
|
580
|
+
repositoryName: "my-repo",
|
|
581
|
+
repositoryUrlFull: "https://github.com/my-org/my-repo",
|
|
582
|
+
refHeadName: "main",
|
|
583
|
+
refHeadRevision: "deadbeef",
|
|
584
|
+
changeId: "7",
|
|
585
|
+
},
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBe("github");
|
|
589
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBe("my-org");
|
|
590
|
+
expect(stringAttr(VCS_REPOSITORY_NAME)).toBe("my-repo");
|
|
591
|
+
expect(stringAttr(VCS_REPOSITORY_URL_FULL)).toBe("https://github.com/my-org/my-repo");
|
|
592
|
+
expect(stringAttr(VCS_REF_HEAD_NAME)).toBe("main");
|
|
593
|
+
expect(stringAttr(VCS_REF_HEAD_REVISION)).toBe("deadbeef");
|
|
594
|
+
expect(stringAttr(VCS_CHANGE_ID)).toBe("7");
|
|
595
|
+
});
|
|
596
|
+
|
|
597
|
+
it("emits nothing when no env vars and no opts.vcs are provided", () => {
|
|
598
|
+
init(baseOptions);
|
|
599
|
+
|
|
600
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBeUndefined();
|
|
601
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBeUndefined();
|
|
602
|
+
expect(stringAttr(VCS_REPOSITORY_NAME)).toBeUndefined();
|
|
603
|
+
expect(stringAttr(VCS_REPOSITORY_URL_FULL)).toBeUndefined();
|
|
604
|
+
expect(stringAttr(VCS_REF_HEAD_NAME)).toBeUndefined();
|
|
605
|
+
expect(stringAttr(VCS_REF_HEAD_REVISION)).toBeUndefined();
|
|
606
|
+
expect(stringAttr(VCS_CHANGE_ID)).toBeUndefined();
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
it("emits only the partial attributes that are available", () => {
|
|
610
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA", "abc123");
|
|
611
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF", "main");
|
|
612
|
+
|
|
613
|
+
init(baseOptions);
|
|
614
|
+
|
|
615
|
+
expect(stringAttr(VCS_REF_HEAD_NAME)).toBe("main");
|
|
616
|
+
expect(stringAttr(VCS_REF_HEAD_REVISION)).toBe("abc123");
|
|
617
|
+
expect(stringAttr(VCS_REPOSITORY_URL_FULL)).toBeUndefined();
|
|
618
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBeUndefined();
|
|
619
|
+
});
|
|
620
|
+
|
|
621
|
+
it("does not derive repository URL when provider is unknown", () => {
|
|
622
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_PROVIDER", "someproprietary");
|
|
623
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER", "owner");
|
|
624
|
+
vi.stubEnv("NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG", "repo");
|
|
625
|
+
|
|
626
|
+
init(baseOptions);
|
|
627
|
+
|
|
628
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBe("someproprietary");
|
|
629
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBe("owner");
|
|
630
|
+
expect(stringAttr(VCS_REPOSITORY_NAME)).toBe("repo");
|
|
631
|
+
expect(stringAttr(VCS_REPOSITORY_URL_FULL)).toBeUndefined();
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it("accepts gist.github.com as a github host alias", () => {
|
|
635
|
+
vi.stubEnv("NEXT_PUBLIC_REPOSITORY_URL", "https://gist.github.com/owner/abc123");
|
|
636
|
+
|
|
637
|
+
init(baseOptions);
|
|
638
|
+
|
|
639
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBe("github");
|
|
640
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBe("owner");
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
it("rejects arbitrary github.com subdomains", () => {
|
|
644
|
+
vi.stubEnv("NEXT_PUBLIC_REPOSITORY_URL", "https://evil.github.com/attacker/repo");
|
|
645
|
+
|
|
646
|
+
init(baseOptions);
|
|
647
|
+
|
|
648
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBeUndefined();
|
|
649
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBeUndefined();
|
|
650
|
+
expect(stringAttr(VCS_REPOSITORY_NAME)).toBeUndefined();
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
it("ignores a Netlify REPOSITORY_URL on an unknown host", () => {
|
|
654
|
+
vi.stubEnv("NEXT_PUBLIC_REPOSITORY_URL", "https://internal-git.example.com/team/app");
|
|
655
|
+
vi.stubEnv("NEXT_PUBLIC_BRANCH", "main");
|
|
656
|
+
|
|
657
|
+
init(baseOptions);
|
|
658
|
+
|
|
659
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBeUndefined();
|
|
660
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBeUndefined();
|
|
661
|
+
expect(stringAttr(VCS_REPOSITORY_NAME)).toBeUndefined();
|
|
662
|
+
expect(stringAttr(VCS_REPOSITORY_URL_FULL)).toBe("https://internal-git.example.com/team/app");
|
|
663
|
+
expect(stringAttr(VCS_REF_HEAD_NAME)).toBe("main");
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
it("parses GitLab nested-group repository URLs to the last path segment", () => {
|
|
667
|
+
vi.stubEnv("NEXT_PUBLIC_REPOSITORY_URL", "https://gitlab.com/group/subgroup/app.git");
|
|
668
|
+
|
|
669
|
+
init(baseOptions);
|
|
670
|
+
|
|
671
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBe("gitlab");
|
|
672
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBe("group");
|
|
673
|
+
expect(stringAttr(VCS_REPOSITORY_NAME)).toBe("app");
|
|
674
|
+
});
|
|
675
|
+
|
|
676
|
+
it("ignores a malformed REPOSITORY_URL", () => {
|
|
677
|
+
vi.stubEnv("NEXT_PUBLIC_REPOSITORY_URL", "not-a-url");
|
|
678
|
+
|
|
679
|
+
init(baseOptions);
|
|
680
|
+
|
|
681
|
+
expect(stringAttr(VCS_REPOSITORY_URL_FULL)).toBe("not-a-url");
|
|
682
|
+
expect(stringAttr(VCS_PROVIDER_NAME)).toBeUndefined();
|
|
683
|
+
expect(stringAttr(VCS_OWNER_NAME)).toBeUndefined();
|
|
684
|
+
expect(stringAttr(VCS_REPOSITORY_NAME)).toBeUndefined();
|
|
685
|
+
});
|
|
686
|
+
});
|
|
400
687
|
});
|
package/src/api/vcs.ts
ADDED
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
import { vars } from "../vars";
|
|
2
|
+
import {
|
|
3
|
+
VCS_CHANGE_ID,
|
|
4
|
+
VCS_OWNER_NAME,
|
|
5
|
+
VCS_PROVIDER_NAME,
|
|
6
|
+
VCS_REF_HEAD_NAME,
|
|
7
|
+
VCS_REF_HEAD_REVISION,
|
|
8
|
+
VCS_REPOSITORY_NAME,
|
|
9
|
+
VCS_REPOSITORY_URL_FULL,
|
|
10
|
+
} from "../semantic-conventions";
|
|
11
|
+
import { InitOptions, VcsAttributes } from "../types/options";
|
|
12
|
+
import { addAttribute } from "../utils/otel";
|
|
13
|
+
|
|
14
|
+
// Module-local typing for the `process` global. The SDK does not depend on
|
|
15
|
+
// @types/node, and bundler substitution requires literal `process.env.NAME`
|
|
16
|
+
// accessors (dot notation, not bracket lookup). We enumerate the exact set of
|
|
17
|
+
// env var names the readers below access via template-literal types — typos
|
|
18
|
+
// are caught at compile time, and `noPropertyAccessFromIndexSignature` does
|
|
19
|
+
// not fire because each key is a known union member, not an index signature.
|
|
20
|
+
//
|
|
21
|
+
// Adding a new framework prefix here automatically permits every combination
|
|
22
|
+
// with every known suffix. Adding a vendor suffix permits every combination
|
|
23
|
+
// with every known prefix.
|
|
24
|
+
type FrameworkPrefix =
|
|
25
|
+
| "NEXT_PUBLIC_"
|
|
26
|
+
| "NUXT_ENV_"
|
|
27
|
+
| "REACT_APP_"
|
|
28
|
+
| "GATSBY_"
|
|
29
|
+
| "VITE_"
|
|
30
|
+
| "PUBLIC_"
|
|
31
|
+
| "VUE_APP_"
|
|
32
|
+
| "REDWOOD_ENV_"
|
|
33
|
+
| "SANITY_STUDIO_";
|
|
34
|
+
|
|
35
|
+
type VercelGitSuffix =
|
|
36
|
+
| "VERCEL_GIT_PROVIDER"
|
|
37
|
+
| "VERCEL_GIT_REPO_OWNER"
|
|
38
|
+
| "VERCEL_GIT_REPO_SLUG"
|
|
39
|
+
| "VERCEL_GIT_COMMIT_REF"
|
|
40
|
+
| "VERCEL_GIT_COMMIT_SHA"
|
|
41
|
+
| "VERCEL_GIT_PULL_REQUEST_ID";
|
|
42
|
+
|
|
43
|
+
type NetlifyGitSuffix = "REPOSITORY_URL" | "BRANCH" | "COMMIT_REF" | "REVIEW_ID";
|
|
44
|
+
|
|
45
|
+
type BrowserBuildEnvKey = `${FrameworkPrefix}${VercelGitSuffix | NetlifyGitSuffix}`;
|
|
46
|
+
|
|
47
|
+
type BrowserBuildEnv = { readonly [K in BrowserBuildEnvKey]?: string };
|
|
48
|
+
|
|
49
|
+
declare const process: { env?: BrowserBuildEnv } | undefined;
|
|
50
|
+
|
|
51
|
+
// Single source of truth mapping each VcsAttributes field to its OTel
|
|
52
|
+
// resource attribute name. Typed as `Record<keyof VcsAttributes, string>` so
|
|
53
|
+
// adding a field to VcsAttributes without adding an entry here is a compile
|
|
54
|
+
// error — no silent "I forgot to apply that field" bugs.
|
|
55
|
+
const VCS_FIELD_TO_ATTRIBUTE: Record<keyof VcsAttributes, string> = {
|
|
56
|
+
providerName: VCS_PROVIDER_NAME,
|
|
57
|
+
ownerName: VCS_OWNER_NAME,
|
|
58
|
+
repositoryName: VCS_REPOSITORY_NAME,
|
|
59
|
+
repositoryUrlFull: VCS_REPOSITORY_URL_FULL,
|
|
60
|
+
refHeadName: VCS_REF_HEAD_NAME,
|
|
61
|
+
refHeadRevision: VCS_REF_HEAD_REVISION,
|
|
62
|
+
changeId: VCS_CHANGE_ID,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Derive VCS (version control) context from the build environment and apply
|
|
67
|
+
* the resulting `vcs.*` resource attributes to `vars.resource.attributes`.
|
|
68
|
+
*
|
|
69
|
+
* Detection precedence per attribute: `opts.vcs` → Vercel env vars → Netlify
|
|
70
|
+
* env vars. Manual overrides via `opts.vcs` always win, even when
|
|
71
|
+
* `opts.disableVcsDetection` is set.
|
|
72
|
+
*/
|
|
73
|
+
export function applyVcsResourceAttributes(opts: InitOptions) {
|
|
74
|
+
const vcs = detectVcs(opts);
|
|
75
|
+
for (const field of Object.keys(VCS_FIELD_TO_ATTRIBUTE) as (keyof VcsAttributes)[]) {
|
|
76
|
+
const value = vcs[field];
|
|
77
|
+
if (value) {
|
|
78
|
+
addAttribute(vars.resource.attributes, VCS_FIELD_TO_ATTRIBUTE[field], value);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function detectVcs(opts: InitOptions): VcsAttributes {
|
|
84
|
+
// opts.vcs manual overrides always win, even when auto-detection is disabled.
|
|
85
|
+
// This lets callers on non-Vercel/Netlify platforms supply context explicitly
|
|
86
|
+
// while still opting out of any env-var reading.
|
|
87
|
+
const override = opts.vcs ?? {};
|
|
88
|
+
|
|
89
|
+
if (opts.disableVcsDetection) {
|
|
90
|
+
return override;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const vercel = detectVcsFromVercel();
|
|
94
|
+
const netlify = detectVcsFromNetlify();
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
providerName: override.providerName ?? vercel.providerName ?? netlify.providerName,
|
|
98
|
+
ownerName: override.ownerName ?? vercel.ownerName ?? netlify.ownerName,
|
|
99
|
+
repositoryName: override.repositoryName ?? vercel.repositoryName ?? netlify.repositoryName,
|
|
100
|
+
repositoryUrlFull: override.repositoryUrlFull ?? vercel.repositoryUrlFull ?? netlify.repositoryUrlFull,
|
|
101
|
+
refHeadName: override.refHeadName ?? vercel.refHeadName ?? netlify.refHeadName,
|
|
102
|
+
refHeadRevision: override.refHeadRevision ?? vercel.refHeadRevision ?? netlify.refHeadRevision,
|
|
103
|
+
changeId: override.changeId ?? vercel.changeId ?? netlify.changeId,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Frameworks expose env vars to the browser bundle using a per-framework
|
|
108
|
+
// prefix (Next.js `NEXT_PUBLIC_*`, Vite `VITE_*`, etc.). For each known
|
|
109
|
+
// vendor suffix (e.g. Vercel's `VERCEL_GIT_PROVIDER`, Netlify's
|
|
110
|
+
// `REPOSITORY_URL`) we enumerate the prefixed variants below.
|
|
111
|
+
//
|
|
112
|
+
// IMPORTANT: each `process.env.NAME` MUST be a LITERAL accessor. Webpack
|
|
113
|
+
// DefinePlugin, Next.js, Gatsby, and equivalents substitute env vars at
|
|
114
|
+
// build time only when they see the literal form. Dynamic lookups like
|
|
115
|
+
// `process.env[name]` or iterating a string array are NOT substituted —
|
|
116
|
+
// they would resolve to `undefined` in the browser bundle.
|
|
117
|
+
//
|
|
118
|
+
// The 9 prefixes below are the framework presets Vercel auto-prefixes
|
|
119
|
+
// `VERCEL_*` system vars under (https://vercel.com/docs/environment-variables/framework-environment-variables).
|
|
120
|
+
// Users on platforms without auto-prefixing (Netlify, Cloudflare Pages, custom
|
|
121
|
+
// CI) can manually expose any env var under any of these prefixes and get
|
|
122
|
+
// the same auto-detection.
|
|
123
|
+
//
|
|
124
|
+
// Adding a new framework prefix = one new literal accessor per attribute in
|
|
125
|
+
// each `detectVcsFrom*` function below.
|
|
126
|
+
|
|
127
|
+
function detectVcsFromVercel(): VcsAttributes {
|
|
128
|
+
let provider: string | undefined;
|
|
129
|
+
let owner: string | undefined;
|
|
130
|
+
let repo: string | undefined;
|
|
131
|
+
let ref: string | undefined;
|
|
132
|
+
let revision: string | undefined;
|
|
133
|
+
let changeId: string | undefined;
|
|
134
|
+
try {
|
|
135
|
+
provider = pickFirstString(
|
|
136
|
+
process?.env?.NEXT_PUBLIC_VERCEL_GIT_PROVIDER,
|
|
137
|
+
process?.env?.NUXT_ENV_VERCEL_GIT_PROVIDER,
|
|
138
|
+
process?.env?.REACT_APP_VERCEL_GIT_PROVIDER,
|
|
139
|
+
process?.env?.GATSBY_VERCEL_GIT_PROVIDER,
|
|
140
|
+
process?.env?.VITE_VERCEL_GIT_PROVIDER,
|
|
141
|
+
process?.env?.PUBLIC_VERCEL_GIT_PROVIDER,
|
|
142
|
+
process?.env?.VUE_APP_VERCEL_GIT_PROVIDER,
|
|
143
|
+
process?.env?.REDWOOD_ENV_VERCEL_GIT_PROVIDER,
|
|
144
|
+
process?.env?.SANITY_STUDIO_VERCEL_GIT_PROVIDER
|
|
145
|
+
);
|
|
146
|
+
owner = pickFirstString(
|
|
147
|
+
process?.env?.NEXT_PUBLIC_VERCEL_GIT_REPO_OWNER,
|
|
148
|
+
process?.env?.NUXT_ENV_VERCEL_GIT_REPO_OWNER,
|
|
149
|
+
process?.env?.REACT_APP_VERCEL_GIT_REPO_OWNER,
|
|
150
|
+
process?.env?.GATSBY_VERCEL_GIT_REPO_OWNER,
|
|
151
|
+
process?.env?.VITE_VERCEL_GIT_REPO_OWNER,
|
|
152
|
+
process?.env?.PUBLIC_VERCEL_GIT_REPO_OWNER,
|
|
153
|
+
process?.env?.VUE_APP_VERCEL_GIT_REPO_OWNER,
|
|
154
|
+
process?.env?.REDWOOD_ENV_VERCEL_GIT_REPO_OWNER,
|
|
155
|
+
process?.env?.SANITY_STUDIO_VERCEL_GIT_REPO_OWNER
|
|
156
|
+
);
|
|
157
|
+
repo = pickFirstString(
|
|
158
|
+
process?.env?.NEXT_PUBLIC_VERCEL_GIT_REPO_SLUG,
|
|
159
|
+
process?.env?.NUXT_ENV_VERCEL_GIT_REPO_SLUG,
|
|
160
|
+
process?.env?.REACT_APP_VERCEL_GIT_REPO_SLUG,
|
|
161
|
+
process?.env?.GATSBY_VERCEL_GIT_REPO_SLUG,
|
|
162
|
+
process?.env?.VITE_VERCEL_GIT_REPO_SLUG,
|
|
163
|
+
process?.env?.PUBLIC_VERCEL_GIT_REPO_SLUG,
|
|
164
|
+
process?.env?.VUE_APP_VERCEL_GIT_REPO_SLUG,
|
|
165
|
+
process?.env?.REDWOOD_ENV_VERCEL_GIT_REPO_SLUG,
|
|
166
|
+
process?.env?.SANITY_STUDIO_VERCEL_GIT_REPO_SLUG
|
|
167
|
+
);
|
|
168
|
+
ref = pickFirstString(
|
|
169
|
+
process?.env?.NEXT_PUBLIC_VERCEL_GIT_COMMIT_REF,
|
|
170
|
+
process?.env?.NUXT_ENV_VERCEL_GIT_COMMIT_REF,
|
|
171
|
+
process?.env?.REACT_APP_VERCEL_GIT_COMMIT_REF,
|
|
172
|
+
process?.env?.GATSBY_VERCEL_GIT_COMMIT_REF,
|
|
173
|
+
process?.env?.VITE_VERCEL_GIT_COMMIT_REF,
|
|
174
|
+
process?.env?.PUBLIC_VERCEL_GIT_COMMIT_REF,
|
|
175
|
+
process?.env?.VUE_APP_VERCEL_GIT_COMMIT_REF,
|
|
176
|
+
process?.env?.REDWOOD_ENV_VERCEL_GIT_COMMIT_REF,
|
|
177
|
+
process?.env?.SANITY_STUDIO_VERCEL_GIT_COMMIT_REF
|
|
178
|
+
);
|
|
179
|
+
revision = pickFirstString(
|
|
180
|
+
process?.env?.NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA,
|
|
181
|
+
process?.env?.NUXT_ENV_VERCEL_GIT_COMMIT_SHA,
|
|
182
|
+
process?.env?.REACT_APP_VERCEL_GIT_COMMIT_SHA,
|
|
183
|
+
process?.env?.GATSBY_VERCEL_GIT_COMMIT_SHA,
|
|
184
|
+
process?.env?.VITE_VERCEL_GIT_COMMIT_SHA,
|
|
185
|
+
process?.env?.PUBLIC_VERCEL_GIT_COMMIT_SHA,
|
|
186
|
+
process?.env?.VUE_APP_VERCEL_GIT_COMMIT_SHA,
|
|
187
|
+
process?.env?.REDWOOD_ENV_VERCEL_GIT_COMMIT_SHA,
|
|
188
|
+
process?.env?.SANITY_STUDIO_VERCEL_GIT_COMMIT_SHA
|
|
189
|
+
);
|
|
190
|
+
changeId = pickFirstString(
|
|
191
|
+
process?.env?.NEXT_PUBLIC_VERCEL_GIT_PULL_REQUEST_ID,
|
|
192
|
+
process?.env?.NUXT_ENV_VERCEL_GIT_PULL_REQUEST_ID,
|
|
193
|
+
process?.env?.REACT_APP_VERCEL_GIT_PULL_REQUEST_ID,
|
|
194
|
+
process?.env?.GATSBY_VERCEL_GIT_PULL_REQUEST_ID,
|
|
195
|
+
process?.env?.VITE_VERCEL_GIT_PULL_REQUEST_ID,
|
|
196
|
+
process?.env?.PUBLIC_VERCEL_GIT_PULL_REQUEST_ID,
|
|
197
|
+
process?.env?.VUE_APP_VERCEL_GIT_PULL_REQUEST_ID,
|
|
198
|
+
process?.env?.REDWOOD_ENV_VERCEL_GIT_PULL_REQUEST_ID,
|
|
199
|
+
process?.env?.SANITY_STUDIO_VERCEL_GIT_PULL_REQUEST_ID
|
|
200
|
+
);
|
|
201
|
+
} catch (_ignored) {
|
|
202
|
+
// process is not defined (or a bundler shimmed it strangely) — skip.
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
providerName: provider,
|
|
207
|
+
ownerName: owner,
|
|
208
|
+
repositoryName: repo,
|
|
209
|
+
repositoryUrlFull: buildRepositoryUrlFromVercel(provider, owner, repo),
|
|
210
|
+
refHeadName: ref,
|
|
211
|
+
refHeadRevision: revision,
|
|
212
|
+
changeId,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function detectVcsFromNetlify(): VcsAttributes {
|
|
217
|
+
// Netlify exposes read-only build env vars (REPOSITORY_URL, BRANCH,
|
|
218
|
+
// COMMIT_REF, REVIEW_ID) but does not auto-prefix them for browser
|
|
219
|
+
// exposure. Users must surface them via their framework's prefix
|
|
220
|
+
// convention themselves (e.g. `NEXT_PUBLIC_REPOSITORY_URL = $REPOSITORY_URL`
|
|
221
|
+
// for Next.js, `VITE_REPOSITORY_URL = $REPOSITORY_URL` for Vite, etc.).
|
|
222
|
+
// We enumerate the same 9 framework prefixes as the Vercel path above.
|
|
223
|
+
let repositoryUrl: string | undefined;
|
|
224
|
+
let branch: string | undefined;
|
|
225
|
+
let commit: string | undefined;
|
|
226
|
+
let reviewId: string | undefined;
|
|
227
|
+
try {
|
|
228
|
+
repositoryUrl = pickFirstString(
|
|
229
|
+
process?.env?.NEXT_PUBLIC_REPOSITORY_URL,
|
|
230
|
+
process?.env?.NUXT_ENV_REPOSITORY_URL,
|
|
231
|
+
process?.env?.REACT_APP_REPOSITORY_URL,
|
|
232
|
+
process?.env?.GATSBY_REPOSITORY_URL,
|
|
233
|
+
process?.env?.VITE_REPOSITORY_URL,
|
|
234
|
+
process?.env?.PUBLIC_REPOSITORY_URL,
|
|
235
|
+
process?.env?.VUE_APP_REPOSITORY_URL,
|
|
236
|
+
process?.env?.REDWOOD_ENV_REPOSITORY_URL,
|
|
237
|
+
process?.env?.SANITY_STUDIO_REPOSITORY_URL
|
|
238
|
+
);
|
|
239
|
+
branch = pickFirstString(
|
|
240
|
+
process?.env?.NEXT_PUBLIC_BRANCH,
|
|
241
|
+
process?.env?.NUXT_ENV_BRANCH,
|
|
242
|
+
process?.env?.REACT_APP_BRANCH,
|
|
243
|
+
process?.env?.GATSBY_BRANCH,
|
|
244
|
+
process?.env?.VITE_BRANCH,
|
|
245
|
+
process?.env?.PUBLIC_BRANCH,
|
|
246
|
+
process?.env?.VUE_APP_BRANCH,
|
|
247
|
+
process?.env?.REDWOOD_ENV_BRANCH,
|
|
248
|
+
process?.env?.SANITY_STUDIO_BRANCH
|
|
249
|
+
);
|
|
250
|
+
commit = pickFirstString(
|
|
251
|
+
process?.env?.NEXT_PUBLIC_COMMIT_REF,
|
|
252
|
+
process?.env?.NUXT_ENV_COMMIT_REF,
|
|
253
|
+
process?.env?.REACT_APP_COMMIT_REF,
|
|
254
|
+
process?.env?.GATSBY_COMMIT_REF,
|
|
255
|
+
process?.env?.VITE_COMMIT_REF,
|
|
256
|
+
process?.env?.PUBLIC_COMMIT_REF,
|
|
257
|
+
process?.env?.VUE_APP_COMMIT_REF,
|
|
258
|
+
process?.env?.REDWOOD_ENV_COMMIT_REF,
|
|
259
|
+
process?.env?.SANITY_STUDIO_COMMIT_REF
|
|
260
|
+
);
|
|
261
|
+
reviewId = pickFirstString(
|
|
262
|
+
process?.env?.NEXT_PUBLIC_REVIEW_ID,
|
|
263
|
+
process?.env?.NUXT_ENV_REVIEW_ID,
|
|
264
|
+
process?.env?.REACT_APP_REVIEW_ID,
|
|
265
|
+
process?.env?.GATSBY_REVIEW_ID,
|
|
266
|
+
process?.env?.VITE_REVIEW_ID,
|
|
267
|
+
process?.env?.PUBLIC_REVIEW_ID,
|
|
268
|
+
process?.env?.VUE_APP_REVIEW_ID,
|
|
269
|
+
process?.env?.REDWOOD_ENV_REVIEW_ID,
|
|
270
|
+
process?.env?.SANITY_STUDIO_REVIEW_ID
|
|
271
|
+
);
|
|
272
|
+
} catch (_ignored) {
|
|
273
|
+
// process is not defined — skip.
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
const parsed = parseRepositoryUrl(repositoryUrl);
|
|
277
|
+
return {
|
|
278
|
+
providerName: parsed?.providerName,
|
|
279
|
+
ownerName: parsed?.ownerName,
|
|
280
|
+
repositoryName: parsed?.repositoryName,
|
|
281
|
+
repositoryUrlFull: repositoryUrl,
|
|
282
|
+
refHeadName: branch,
|
|
283
|
+
refHeadRevision: commit,
|
|
284
|
+
changeId: reviewId,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function pickFirstString(...values: (string | undefined)[]): string | undefined {
|
|
289
|
+
for (const value of values) {
|
|
290
|
+
if (value) return value;
|
|
291
|
+
}
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function buildRepositoryUrlFromVercel(
|
|
296
|
+
provider: string | undefined,
|
|
297
|
+
owner: string | undefined,
|
|
298
|
+
repo: string | undefined
|
|
299
|
+
): string | undefined {
|
|
300
|
+
if (!provider || !owner || !repo) return undefined;
|
|
301
|
+
const host = vcsHostForProvider(provider);
|
|
302
|
+
if (!host) return undefined;
|
|
303
|
+
return `https://${host}/${owner}/${repo}`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Single source of truth for provider↔host mapping. Both `vcsHostForProvider`
|
|
307
|
+
// (Vercel path: provider name is known, derive the canonical host) and
|
|
308
|
+
// `vcsProviderForHost` (Netlify path: only the URL is known, derive the
|
|
309
|
+
// provider) consult this map so adding a provider is one edit. Each entry is
|
|
310
|
+
// a list of accepted hostnames; the FIRST entry is the canonical host used
|
|
311
|
+
// when building URLs from a provider name. Subsequent entries are exact
|
|
312
|
+
// aliases — we do not match arbitrary subdomains.
|
|
313
|
+
const VCS_PROVIDER_HOSTS: Record<string, readonly string[]> = {
|
|
314
|
+
github: ["github.com", "gist.github.com"],
|
|
315
|
+
gitlab: ["gitlab.com"],
|
|
316
|
+
bitbucket: ["bitbucket.org"],
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
function vcsHostForProvider(provider: string): string | undefined {
|
|
320
|
+
return VCS_PROVIDER_HOSTS[provider]?.[0];
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function parseRepositoryUrl(
|
|
324
|
+
url: string | undefined
|
|
325
|
+
): { providerName: string; ownerName: string; repositoryName: string } | undefined {
|
|
326
|
+
if (!url) return undefined;
|
|
327
|
+
|
|
328
|
+
let host: string;
|
|
329
|
+
let pathname: string;
|
|
330
|
+
try {
|
|
331
|
+
const parsed = new URL(url);
|
|
332
|
+
host = parsed.host;
|
|
333
|
+
pathname = parsed.pathname;
|
|
334
|
+
} catch (_ignored) {
|
|
335
|
+
return undefined;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const provider = vcsProviderForHost(host);
|
|
339
|
+
if (!provider) return undefined;
|
|
340
|
+
|
|
341
|
+
const trimmed = pathname.replace(/^\/+/, "").replace(/\.git$/, "");
|
|
342
|
+
const segments = trimmed.split("/");
|
|
343
|
+
if (segments.length < 2) return undefined;
|
|
344
|
+
|
|
345
|
+
const ownerName = segments[0];
|
|
346
|
+
// Bitbucket and GitLab can have nested groups; the repository slug is the
|
|
347
|
+
// last path segment. GitHub paths are always two segments.
|
|
348
|
+
const repositoryName = segments[segments.length - 1];
|
|
349
|
+
if (!ownerName || !repositoryName) return undefined;
|
|
350
|
+
|
|
351
|
+
return { providerName: provider, ownerName, repositoryName };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function vcsProviderForHost(host: string): string | undefined {
|
|
355
|
+
for (const [provider, hosts] of Object.entries(VCS_PROVIDER_HOSTS)) {
|
|
356
|
+
if (hosts.includes(host)) return provider;
|
|
357
|
+
}
|
|
358
|
+
return undefined;
|
|
359
|
+
}
|
|
@@ -6,6 +6,15 @@ export const DEPLOYMENT_ENVIRONMENT_NAME = "deployment.environment.name";
|
|
|
6
6
|
export const DEPLOYMENT_NAME = "deployment.name";
|
|
7
7
|
export const DEPLOYMENT_ID = "deployment.id";
|
|
8
8
|
|
|
9
|
+
// VCS Resource Attribute Keys
|
|
10
|
+
export const VCS_PROVIDER_NAME = "vcs.provider.name";
|
|
11
|
+
export const VCS_OWNER_NAME = "vcs.owner.name";
|
|
12
|
+
export const VCS_REPOSITORY_NAME = "vcs.repository.name";
|
|
13
|
+
export const VCS_REPOSITORY_URL_FULL = "vcs.repository.url.full";
|
|
14
|
+
export const VCS_REF_HEAD_NAME = "vcs.ref.head.name";
|
|
15
|
+
export const VCS_REF_HEAD_REVISION = "vcs.ref.head.revision";
|
|
16
|
+
export const VCS_CHANGE_ID = "vcs.change.id";
|
|
17
|
+
|
|
9
18
|
// Misc Signal Attribute Keys
|
|
10
19
|
export const EVENT_NAME = "event.name";
|
|
11
20
|
export const WEB_EVENT_TITLE = "dash0.web.event.title";
|