about summary refs log tree commit diff
diff options
context:
space:
mode:
authorManuel Palenzuela Merino <manuel.palenzuela@datadoghq.com>2024-12-05 11:07:35 +0100
committerManuel Palenzuela Merino <manuel.palenzuela@datadoghq.com>2024-12-05 11:08:17 +0100
commitee3c0aa3d71a857ae7fb60f1b98cf129ee6ad71d (patch)
tree8aeaa3b76be6cdcf326acf3627b3d283a8b2c372
parentUpdate README.md (diff)
downloadtest-repo-ee3c0aa3d71a857ae7fb60f1b98cf129ee6ad71d.tar.gz
test-repo-ee3c0aa3d71a857ae7fb60f1b98cf129ee6ad71d.tar.bz2
test-repo-ee3c0aa3d71a857ae7fb60f1b98cf129ee6ad71d.zip
Add tests
-rw-r--r--.github/workflows/actions-test.yaml158
-rw-r--r--.github/workflows/manual-approval-test.yaml17
-rw-r--r--.github/workflows/print-env-vars.yaml15
-rw-r--r--.github/workflows/test-matrix-strategy.yml14
-rw-r--r--__pycache__/python_test.cpython-310-pytest-8.3.3.pycbin0 -> 933 bytes
-rw-r--r--additions-test.xml10
-rw-r--r--fixtures/custom-tags-100.json102
-rw-r--r--fixtures/custom-tags-99.json101
-rw-r--r--go.mod47
-rw-r--r--go.sum195
-rw-r--r--main/additions/additions-test.xml2
-rw-r--r--main/additions/additions.go11
-rw-r--r--main/additions/additions_test.go29
-rw-r--r--main/civisibility/constants/ci.go44
-rw-r--r--main/civisibility/constants/env.go27
-rw-r--r--main/civisibility/constants/git.go52
-rw-r--r--main/civisibility/constants/os.go21
-rw-r--r--main/civisibility/constants/runtime.go16
-rw-r--r--main/civisibility/constants/span_types.go28
-rw-r--r--main/civisibility/constants/tags.go43
-rw-r--r--main/civisibility/constants/test_tags.go90
-rw-r--r--main/civisibility/integrations/civisibility.go119
-rw-r--r--main/civisibility/integrations/gotesting/reflections.go103
-rw-r--r--main/civisibility/integrations/gotesting/reflections_test.go150
-rw-r--r--main/civisibility/integrations/gotesting/testing.go354
-rw-r--r--main/civisibility/integrations/gotesting/testingB.go337
-rw-r--r--main/civisibility/integrations/gotesting/testingT.go217
-rw-r--r--main/civisibility/integrations/gotesting/testing_test.go354
-rw-r--r--main/civisibility/integrations/manual_api.go219
-rw-r--r--main/civisibility/integrations/manual_api_ddtest.go158
-rw-r--r--main/civisibility/integrations/manual_api_ddtestmodule.go141
-rw-r--r--main/civisibility/integrations/manual_api_ddtestsession.go171
-rw-r--r--main/civisibility/integrations/manual_api_ddtestsuite.go121
-rw-r--r--main/civisibility/integrations/manual_api_mocktracer_test.go278
-rw-r--r--main/civisibility/integrations/manual_api_test.go329
-rw-r--r--main/civisibility/utils/ci_providers.go569
-rw-r--r--main/civisibility/utils/ci_providers_test.go116
-rw-r--r--main/civisibility/utils/codeowners.go300
-rw-r--r--main/civisibility/utils/codeowners_test.go159
-rw-r--r--main/civisibility/utils/environmentTags.go120
-rw-r--r--main/civisibility/utils/environmentTags_test.go40
-rw-r--r--main/civisibility/utils/git.go104
-rw-r--r--main/civisibility/utils/git_test.go62
-rw-r--r--main/civisibility/utils/home.go120
-rw-r--r--main/civisibility/utils/home_test.go97
-rw-r--r--main/civisibility/utils/names.go82
-rw-r--r--main/civisibility/utils/names_test.go39
-rw-r--r--main/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITHUB54
-rw-r--r--main/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITLAB64
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/appveyor.json555
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/awscodepipeline.json62
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/azurepipelines.json935
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/bitbucket.json595
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/bitrise.json694
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/buddy.json483
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/buildkite.json1022
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/circleci.json754
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/codefresh.json162
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/github.json714
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/gitlab.json1047
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/jenkins.json904
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/teamcity.json78
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/travisci.json525
-rw-r--r--main/civisibility/utils/testdata/fixtures/providers/usersupplied.json400
-rw-r--r--main/osinfo/osinfo.go21
-rw-r--r--main/osinfo/osinfo_darwin.go24
-rw-r--r--main/osinfo/osinfo_default.go21
-rw-r--r--main/osinfo/osinfo_freebsd.go24
-rw-r--r--main/osinfo/osinfo_linux.go52
-rw-r--r--main/osinfo/osinfo_windows.go54
-rw-r--r--main/subtractions/subtractions.go5
-rw-r--r--main/subtractions/subtractions_test.go19
-rw-r--r--python_test.py5
73 files changed, 15154 insertions, 0 deletions
diff --git a/.github/workflows/actions-test.yaml b/.github/workflows/actions-test.yaml
new file mode 100644
index 0000000..bce30b2
--- /dev/null
+++ b/.github/workflows/actions-test.yaml
@@ -0,0 +1,158 @@
+name: Testing Github actions
+on: [push]
+
+jobs:
+
+  datadog-tags:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - name: Install node
+        uses: actions/setup-node@v3
+      - run: yarn global add @datadog/datadog-ci
+      - run: DD_API_KEY=${{ secrets.DD_API_KEY }} DD_APP_KEY=${{ secrets.DD_APP_KEY }} DD_SITE='datad0g.com' DD_TAGS='team:civisibility,service:my-custom-service-3' datadog-ci tag --level pipeline
+      - run: DD_API_KEY=${{ secrets.DD_API_KEY }} DD_APP_KEY=${{ secrets.DD_APP_KEY }} DD_SITE='datad0g.com' datadog-ci metric --level pipeline --metrics image_size:150
+      - run: DD_API_KEY=${{ secrets.DD_API_KEY_PROD }} DD_APP_KEY=${{ secrets.DD_APP_KEY_PROD }} datadog-ci tag --level pipeline --tags team:civisibility
+      - run: DD_API_KEY=${{ secrets.DD_API_KEY_PROD }} DD_APP_KEY=${{ secrets.DD_APP_KEY_PROD }} datadog-ci metric --level pipeline --metrics image_size:150
+
+  build:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-go@v3
+        with:
+          go-version: '>=1.17.0'
+      - run: go build ./main/...
+
+  test-additions:
+    needs: build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-go@v3
+        with:
+          go-version: '>=1.17.0'
+      - run: go install gotest.tools/gotestsum@latest
+      - run: gotestsum --junitfile additions-test.xml ./main/additions/...
+      - name: Uploading test results
+        if: always()
+        run: |
+            yarn global add @datadog/datadog-ci
+            export DATADOG_API_KEY=${{ secrets.DD_API_KEY }}
+            export DATADOG_SITE='datad0g.com'
+            export DD_ENV=ci-tests-ubuntu-latest
+            datadog-ci junit upload --service ci-visibility-test-additions --report-tags test_level:session --report-metrics=test.code_coverage.lines_pct:85 additions-test.xml
+  
+  test-subtractions:
+    needs: build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-go@v3
+        with:
+          go-version: '>=1.17.0'
+      - run: go install gotest.tools/gotestsum@latest
+      - run: gotestsum --junitfile subtractions-test.xml ./main/subtractions/...
+      - name: Uploading test results
+        if: always()
+        run: |
+            yarn global add @datadog/datadog-ci
+            export DATADOG_API_KEY=${{ secrets.DD_API_KEY }}
+            export DATADOG_SITE='datad0g.com'
+            export DD_ENV=ci-tests-ubuntu-latest
+            datadog-ci junit upload --service ci-visibility-test-subtractions --report-tags test_level:session --report-metrics=test.code_coverage.lines_pct:25 subtractions-test.xml
+  
+  test-subtractions-2:
+    needs: build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-go@v3
+        with:
+          go-version: '>=1.17.0'
+      - run: go install gotest.tools/gotestsum@latest
+      - run: gotestsum --junitfile subtractions-test.xml ./main/subtractions/...
+      - name: Uploading test results
+        if: always()
+        run: |
+            yarn global add @datadog/datadog-ci
+            export DATADOG_API_KEY=${{ secrets.DD_API_KEY }}
+            export DATADOG_SITE='datad0g.com'
+            export DD_ENV=ci-tests-ubuntu-latest
+            datadog-ci junit upload --service ci-visibility-test-subtractions --report-tags test_level:session --report-tags runtime.version:test --report-metrics=test.code_coverage.lines_pct:44 subtractions-test.xml
+
+  python-tests:
+    needs: build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-python@v4
+        with:
+          python-version: '3.x'
+      - run: pip install pytest ddtrace coverage
+      - name: Configure Datadog Test Visibility
+        uses: datadog/test-visibility-github-action@v2
+        with:
+          languages: python
+          api_key: ${{ secrets.DD_API_KEY }}
+          site: datad0g.com
+      - name: Running tests
+        if: always()
+        run: pytest
+  
+  tagged_job:
+    runs-on: ubuntu-latest
+    name: Custom tagged job
+    env:
+      DD_GITHUB_JOB_NAME: Custom tagged job
+    steps:
+      - uses: actions/checkout@v3
+      - name: Install node
+        uses: actions/setup-node@v3
+      - run: yarn global add @datadog/datadog-ci
+      - run: >
+          DATADOG_API_KEY=${{ secrets.DD_API_KEY }}
+          DATADOG_SITE='datad0g.com'
+          datadog-ci tag --level job --tags "custom_tag:1234"
+
+  datadog-gate:
+    needs: [test-additions, test-subtractions, test-subtractions-2, python-tests]
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - name: Install node
+        uses: actions/setup-node@v3
+      - run: yarn global add @datadog/datadog-ci
+      - run: >
+          DD_API_KEY=${{ secrets.DD_API_KEY }}
+          DD_APP_KEY=${{ secrets.DD_APP_KEY }}
+          DD_SITE='datad0g.com'
+          datadog-ci gate evaluate --scope=team:backend --scope=team:frontend
+  datadog-frontend-tests:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - name: Run frontend tests
+        uses: actions/setup-node@v3
+      - run: yarn global add @datadog/datadog-ci
+      - run: >
+          DATADOG_API_KEY=${{ secrets.DD_API_KEY }}
+          DATADOG_SITE='datad0g.com'
+          datadog-ci tag --level pipeline --tags team:ci-visibility-app
+
+  test-additions-with-ddtrace:
+    needs: build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v3
+      - uses: actions/setup-go@v3
+        with:
+          go-version: '>=1.17.0'
+      - name: Uploading test results
+        if: always()
+        run: |
+            export DD_API_KEY=${{ secrets.DD_API_KEY }}
+            export DD_SITE='datad0g.com'
+            export DD_SERVICE=ci-visibility-additions-ddtrace
+            export DD_CIVISIBILITY_AGENTLESS_ENABLED=true
+            go test ./main/additions/...
diff --git a/.github/workflows/manual-approval-test.yaml b/.github/workflows/manual-approval-test.yaml
new file mode 100644
index 0000000..e861181
--- /dev/null
+++ b/.github/workflows/manual-approval-test.yaml
@@ -0,0 +1,17 @@
+name: CI/CD with Manual Approval
+on: workflow_dispatch
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout code
+        uses: actions/checkout@v2
+
+  deploy:
+    runs-on: ubuntu-latest
+    needs: build
+    environment:
+      name: TEST_APPROVALS
+    steps:
+      - name: My Manual Approval step
+        run: sleep 5
diff --git a/.github/workflows/print-env-vars.yaml b/.github/workflows/print-env-vars.yaml
new file mode 100644
index 0000000..e0e5fa4
--- /dev/null
+++ b/.github/workflows/print-env-vars.yaml
@@ -0,0 +1,15 @@
+name: Printing env variables
+
+on: [push]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    name: printer
+    steps:
+      - name: Print GitHub context
+        env:
+          GITHUB_CONTEXT: ${{ toJson(github) }}
+        run: echo "$GITHUB_CONTEXT"
+      - name: Print github_sha
+        run: echo "$GITHUB_SHA"
diff --git a/.github/workflows/test-matrix-strategy.yml b/.github/workflows/test-matrix-strategy.yml
new file mode 100644
index 0000000..6233ab8
--- /dev/null
+++ b/.github/workflows/test-matrix-strategy.yml
@@ -0,0 +1,14 @@
+name: CI
+
+on: [workflow_dispatch]
+
+jobs:
+  build:
+    runs-on: ubuntu-latest
+    name: matrix ${{ matrix.version }}
+    strategy:
+      matrix:
+        version: [10, 12, 14]
+    steps:
+      - uses: actions/checkout@v3
+      - run: sleep ${{ matrix.version }}
diff --git a/__pycache__/python_test.cpython-310-pytest-8.3.3.pyc b/__pycache__/python_test.cpython-310-pytest-8.3.3.pyc
new file mode 100644
index 0000000..6731ac6
--- /dev/null
+++ b/__pycache__/python_test.cpython-310-pytest-8.3.3.pyc
Binary files differdiff --git a/additions-test.xml b/additions-test.xml
new file mode 100644
index 0000000..43a0477
--- /dev/null
+++ b/additions-test.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<testsuites tests="2" failures="0" errors="0" time="0.430965">
+	<testsuite tests="2" failures="0" time="0.000000" name="ci-visibility-test-github/main/additions" timestamp="2024-11-28T12:39:41+01:00">
+		<properties>
+			<property name="go.version" value="go1.23.2 darwin/arm64"></property>
+		</properties>
+		<testcase classname="ci-visibility-test-github/main/additions" name="Test_AddNumbers" time="0.000000"></testcase>
+		<testcase classname="ci-visibility-test-github/main/additions" name="Test_AddNumbers2" time="0.000000"></testcase>
+	</testsuite>
+</testsuites>
\ No newline at end of file
diff --git a/fixtures/custom-tags-100.json b/fixtures/custom-tags-100.json
new file mode 100644
index 0000000..3d27147
--- /dev/null
+++ b/fixtures/custom-tags-100.json
@@ -0,0 +1,102 @@
+{
+  "key1": "value1",
+  "key2": "value2",
+  "key3": "value3",
+  "key4": "value4",
+  "key5": "value5",
+  "key6": "value6",
+  "key7": "value7",
+  "key8": "value8",
+  "key9": "value9",
+  "key10": "value10",
+  "key11": "value11",
+  "key12": "value12",
+  "key13": "value13",
+  "key14": "value14",
+  "key15": "value15",
+  "key16": "value16",
+  "key17": "value17",
+  "key18": "value18",
+  "key19": "value19",
+  "key20": "value20",
+  "key21": "value21",
+  "key22": "value22",
+  "key23": "value23",
+  "key24": "value24",
+  "key25": "value25",
+  "key26": "value26",
+  "key27": "value27",
+  "key28": "value28",
+  "key29": "value29",
+  "key30": "value30",
+  "key31": "value31",
+  "key32": "value32",
+  "key33": "value33",
+  "key34": "value34",
+  "key35": "value35",
+  "key36": "value36",
+  "key37": "value37",
+  "key38": "value38",
+  "key39": "value39",
+  "key40": "value40",
+  "key41": "value41",
+  "key42": "value42",
+  "key43": "value43",
+  "key44": "value44",
+  "key45": "value45",
+  "key46": "value46",
+  "key47": "value47",
+  "key48": "value48",
+  "key49": "value49",
+  "key50": "value50",
+  "key51": "value51",
+  "key52": "value52",
+  "key53": "value53",
+  "key54": "value54",
+  "key55": "value55",
+  "key56": "value56",
+  "key57": "value57",
+  "key58": "value58",
+  "key59": "value59",
+  "key60": "value60",
+  "key61": "value61",
+  "key62": "value62",
+  "key63": "value63",
+  "key64": "value64",
+  "key65": "value65",
+  "key66": "value66",
+  "key67": "value67",
+  "key68": "value68",
+  "key69": "value69",
+  "key70": "value70",
+  "key71": "value71",
+  "key72": "value72",
+  "key73": "value73",
+  "key74": "value74",
+  "key75": "value75",
+  "key76": "value76",
+  "key77": "value77",
+  "key78": "value78",
+  "key79": "value79",
+  "key80": "value80",
+  "key81": "value81",
+  "key82": "value82",
+  "key83": "value83",
+  "key84": "value84",
+  "key85": "value85",
+  "key86": "value86",
+  "key87": "value87",
+  "key88": "value88",
+  "key89": "value89",
+  "key90": "value90",
+  "key91": "value91",
+  "key92": "value92",
+  "key93": "value93",
+  "key94": "value94",
+  "key95": "value95",
+  "key96": "value96",
+  "key97": "value97",
+  "key98": "value98",
+  "key99": "value99",
+  "key100": "value100"
+}
diff --git a/fixtures/custom-tags-99.json b/fixtures/custom-tags-99.json
new file mode 100644
index 0000000..b53b402
--- /dev/null
+++ b/fixtures/custom-tags-99.json
@@ -0,0 +1,101 @@
+{
+  "key1": "value1",
+  "key2": "value2",
+  "key3": "value3",
+  "key4": "value4",
+  "key5": "value5",
+  "key6": "value6",
+  "key7": "value7",
+  "key8": "value8",
+  "key9": "value9",
+  "key10": "value10",
+  "key11": "value11",
+  "key12": "value12",
+  "key13": "value13",
+  "key14": "value14",
+  "key15": "value15",
+  "key16": "value16",
+  "key17": "value17",
+  "key18": "value18",
+  "key19": "value19",
+  "key20": "value20",
+  "key21": "value21",
+  "key22": "value22",
+  "key23": "value23",
+  "key24": "value24",
+  "key25": "value25",
+  "key26": "value26",
+  "key27": "value27",
+  "key28": "value28",
+  "key29": "value29",
+  "key30": "value30",
+  "key31": "value31",
+  "key32": "value32",
+  "key33": "value33",
+  "key34": "value34",
+  "key35": "value35",
+  "key36": "value36",
+  "key37": "value37",
+  "key38": "value38",
+  "key39": "value39",
+  "key40": "value40",
+  "key41": "value41",
+  "key42": "value42",
+  "key43": "value43",
+  "key44": "value44",
+  "key45": "value45",
+  "key46": "value46",
+  "key47": "value47",
+  "key48": "value48",
+  "key49": "value49",
+  "key50": "value50",
+  "key51": "value51",
+  "key52": "value52",
+  "key53": "value53",
+  "key54": "value54",
+  "key55": "value55",
+  "key56": "value56",
+  "key57": "value57",
+  "key58": "value58",
+  "key59": "value59",
+  "key60": "value60",
+  "key61": "value61",
+  "key62": "value62",
+  "key63": "value63",
+  "key64": "value64",
+  "key65": "value65",
+  "key66": "value66",
+  "key67": "value67",
+  "key68": "value68",
+  "key69": "value69",
+  "key70": "value70",
+  "key71": "value71",
+  "key72": "value72",
+  "key73": "value73",
+  "key74": "value74",
+  "key75": "value75",
+  "key76": "value76",
+  "key77": "value77",
+  "key78": "value78",
+  "key79": "value79",
+  "key80": "value80",
+  "key81": "value81",
+  "key82": "value82",
+  "key83": "value83",
+  "key84": "value84",
+  "key85": "value85",
+  "key86": "value86",
+  "key87": "value87",
+  "key88": "value88",
+  "key89": "value89",
+  "key90": "value90",
+  "key91": "value91",
+  "key92": "value92",
+  "key93": "value93",
+  "key94": "value94",
+  "key95": "value95",
+  "key96": "value96",
+  "key97": "value97",
+  "key98": "value98",
+  "key99": "value99"
+}
diff --git a/go.mod b/go.mod
new file mode 100644
index 0000000..69f9f3a
--- /dev/null
+++ b/go.mod
@@ -0,0 +1,47 @@
+module ci-visibility-test-github
+
+go 1.21
+
+toolchain go1.22.4
+
+require (
+	github.com/stretchr/testify v1.9.0
+	golang.org/x/sys v0.20.0
+	gopkg.in/DataDog/dd-trace-go.v1 v1.39.0-alpha.1.0.20240717122019-0a6bcb72ef86
+)
+
+require (
+	github.com/DataDog/appsec-internal-go v1.7.0 // indirect
+	github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 // indirect
+	github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1 // indirect
+	github.com/DataDog/datadog-go/v5 v5.3.0 // indirect
+	github.com/DataDog/go-libddwaf/v3 v3.3.0 // indirect
+	github.com/DataDog/go-tuf v1.0.2-0.5.2 // indirect
+	github.com/DataDog/sketches-go v1.4.5 // indirect
+	github.com/Microsoft/go-winio v0.6.1 // indirect
+	github.com/cespare/xxhash/v2 v2.2.0 // indirect
+	github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
+	github.com/dustin/go-humanize v1.0.1 // indirect
+	github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 // indirect
+	github.com/ebitengine/purego v0.6.0-alpha.5 // indirect
+	github.com/google/uuid v1.5.0 // indirect
+	github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 // indirect
+	github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
+	github.com/hashicorp/go-sockaddr v1.0.2 // indirect
+	github.com/mitchellh/mapstructure v1.5.0 // indirect
+	github.com/outcaste-io/ristretto v0.2.3 // indirect
+	github.com/philhofer/fwd v1.1.2 // indirect
+	github.com/pkg/errors v0.9.1 // indirect
+	github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+	github.com/ryanuber/go-glob v1.0.0 // indirect
+	github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect
+	github.com/stretchr/objx v0.5.2 // indirect
+	github.com/tinylib/msgp v1.1.8 // indirect
+	go.uber.org/atomic v1.11.0 // indirect
+	golang.org/x/mod v0.14.0 // indirect
+	golang.org/x/time v0.3.0 // indirect
+	golang.org/x/tools v0.16.1 // indirect
+	golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
+	google.golang.org/protobuf v1.33.0 // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..9cd21ee
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,195 @@
+github.com/DataDog/appsec-internal-go v1.7.0 h1:iKRNLih83dJeVya3IoUfK+6HLD/hQsIbyBlfvLmAeb0=
+github.com/DataDog/appsec-internal-go v1.7.0/go.mod h1:wW0cRfWBo4C044jHGwYiyh5moQV2x0AhnwqMuiX7O/g=
+github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0 h1:bUMSNsw1iofWiju9yc1f+kBd33E3hMJtq9GuU602Iy8=
+github.com/DataDog/datadog-agent/pkg/obfuscate v0.48.0/go.mod h1:HzySONXnAgSmIQfL6gOv9hWprKJkx8CicuXuUbmgWfo=
+github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1 h1:5nE6N3JSs2IG3xzMthNFhXfOaXlrsdgqmJ73lndFf8c=
+github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.48.1/go.mod h1:Vc+snp0Bey4MrrJyiV2tVxxJb6BmLomPvN1RgAvjGaQ=
+github.com/DataDog/datadog-go/v5 v5.3.0 h1:2q2qjFOb3RwAZNU+ez27ZVDwErJv5/VpbBPprz7Z+s8=
+github.com/DataDog/datadog-go/v5 v5.3.0/go.mod h1:XRDJk1pTc00gm+ZDiBKsjh7oOOtJfYfglVCmFb8C2+Q=
+github.com/DataDog/go-libddwaf/v3 v3.3.0 h1:jS72fuQpFgJZEdEJDmHJCPAgNTEMZoz1EUvimPUOiJ4=
+github.com/DataDog/go-libddwaf/v3 v3.3.0/go.mod h1:Bz/0JkpGf689mzbUjKJeheJINqsyyhM8p9PDuHdK2Ec=
+github.com/DataDog/go-tuf v1.0.2-0.5.2 h1:EeZr937eKAWPxJ26IykAdWA4A0jQXJgkhUjqEI/w7+I=
+github.com/DataDog/go-tuf v1.0.2-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0=
+github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4=
+github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM=
+github.com/DataDog/sketches-go v1.4.5 h1:ki7VfeNz7IcNafq7yI/j5U/YCkO3LJiMDtXz9OMQbyE=
+github.com/DataDog/sketches-go v1.4.5/go.mod h1:7Y8GN8Jf66DLyDhc94zuWA3uHEt/7ttt8jHOBWWrSOg=
+github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
+github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
+github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
+github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
+github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
+github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
+github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
+github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
+github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
+github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 h1:8EXxF+tCLqaVk8AOC29zl2mnhQjwyLxxOTuhUazWRsg=
+github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4/go.mod h1:I5sHm0Y0T1u5YjlyqC5GVArM7aNZRUYtTjmJ8mPJFds=
+github.com/ebitengine/purego v0.6.0-alpha.5 h1:EYID3JOAdmQ4SNZYJHu9V6IqOeRQDBYxqKAg9PyoHFY=
+github.com/ebitengine/purego v0.6.0-alpha.5/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ=
+github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
+github.com/glebarez/go-sqlite v1.22.0 h1:uAcMJhaA6r3LHMTFgP0SifzgXg46yJkgxqyuyec+ruQ=
+github.com/glebarez/go-sqlite v1.22.0/go.mod h1:PlBIdHe0+aUEFn+r2/uthrWq4FxbzugL0L8Li6yQJbc=
+github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b h1:h9U78+dx9a4BKdQkBBos92HalKpaGKHrp+3Uo6yTodo=
+github.com/google/pprof v0.0.0-20230817174616-7a8ec2ada47b/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
+github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
+github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
+github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7 h1:UpiO20jno/eV1eVZcxqWnUohyKRe1g8FPV/xH1s/2qs=
+github.com/hashicorp/go-secure-stdlib/parseutil v0.1.7/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
+github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
+github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
+github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
+github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
+github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
+github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
+github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
+github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
+github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
+github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
+github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
+github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
+github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0=
+github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac=
+github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw=
+github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
+github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
+github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 h1:4+LEVOB87y175cLJC/mbsgKmoDOjrBldtXvioEy96WY=
+github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3/go.mod h1:vl5+MqJ1nBINuSsUI2mGgH79UweUT/B5Fy8857PqyyI=
+github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
+github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
+github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
+github.com/secure-systems-lab/go-securesystemslib v0.7.0 h1:OwvJ5jQf9LnIAS83waAjPbcMsODrTQUpJ02eNLUoxBg=
+github.com/secure-systems-lab/go-securesystemslib v0.7.0/go.mod h1:/2gYnlnHVQ6xeGtfIqFy7Do03K4cdCY0A/GlJLDKLHI=
+github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
+github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
+github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/tinylib/msgp v1.1.8 h1:FCXC1xanKO4I8plpHGH2P7koL/RzZs12l/+r7vakfm0=
+github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw=
+github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
+go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
+go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
+go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
+golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
+golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
+golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
+golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
+golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
+golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
+golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
+google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg=
+google.golang.org/grpc v1.57.1/go.mod h1:Sd+9RMTACXwmub0zcNY2c4arhtrbBYD1AUHI/dt16Mo=
+google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
+google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/DataDog/dd-trace-go.v1 v1.39.0-alpha.1.0.20240717122019-0a6bcb72ef86 h1:E+/QCDYsglgEnZJXN2MmHqeC4Hg4luEat+Ph2qfi9ds=
+gopkg.in/DataDog/dd-trace-go.v1 v1.39.0-alpha.1.0.20240717122019-0a6bcb72ef86/go.mod h1:YPIqwP9DSN2WYzCm2sQJAflQI+vSZji2bcxeUgOVbhw=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+modernc.org/libc v1.37.6 h1:orZH3c5wmhIQFTXF+Nt+eeauyd+ZIt2BX6ARe+kD+aw=
+modernc.org/libc v1.37.6/go.mod h1:YAXkAZ8ktnkCKaN9sw/UDeUVkGYJ/YquGO4FTi5nmHE=
+modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4=
+modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo=
+modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E=
+modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E=
+modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ=
+modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0=
diff --git a/main/additions/additions-test.xml b/main/additions/additions-test.xml
new file mode 100644
index 0000000..ed6f30b
--- /dev/null
+++ b/main/additions/additions-test.xml
@@ -0,0 +1,2 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<testsuites tests="0" failures="0" errors="1" time="0.546355"></testsuites>
\ No newline at end of file
diff --git a/main/additions/additions.go b/main/additions/additions.go
new file mode 100644
index 0000000..a5170de
--- /dev/null
+++ b/main/additions/additions.go
@@ -0,0 +1,11 @@
+package main
+
+import "fmt"
+
+func main() {
+	fmt.Println(addNumbers(2, 3))
+}
+
+func addNumbers(x int, y int) int {
+	return y + x
+}
diff --git a/main/additions/additions_test.go b/main/additions/additions_test.go
new file mode 100644
index 0000000..d62bc68
--- /dev/null
+++ b/main/additions/additions_test.go
@@ -0,0 +1,29 @@
+package main
+
+import (
+	"os"
+	"testing"
+
+	"ci-visibility-test-github/main/civisibility/integrations/gotesting"
+)
+
+func TestMain(m *testing.M) {
+	// NOTE: This is the only needed thing to enable test visibility instrumentation.
+	os.Exit(gotesting.RunM(m))
+}
+
+func Test_AddNumbers(t *testing.T) {
+	ans := addNumbers(2, 5)
+
+	if ans != 7 {
+		t.Fatal("Expected 7 but instead got ", ans)
+	}
+}
+
+func Test_AddNumbers2(t *testing.T) {
+	ans := addNumbers(7, 5)
+
+	if ans != 12 {
+		t.Fatal("Expected 12 but instead got ", ans)
+	}
+}
diff --git a/main/civisibility/constants/ci.go b/main/civisibility/constants/ci.go
new file mode 100644
index 0000000..d1ca2e8
--- /dev/null
+++ b/main/civisibility/constants/ci.go
@@ -0,0 +1,44 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package constants
+
+const (
+	// CIJobName indicates the name of the CI job.
+	CIJobName = "ci.job.name"
+
+	// CIJobURL indicates the URL of the CI job.
+	CIJobURL = "ci.job.url"
+
+	// CIPipelineID indicates the ID of the CI pipeline.
+	CIPipelineID = "ci.pipeline.id"
+
+	// CIPipelineName indicates the name of the CI pipeline.
+	CIPipelineName = "ci.pipeline.name"
+
+	// CIPipelineNumber indicates the number of the CI pipeline.
+	CIPipelineNumber = "ci.pipeline.number"
+
+	// CIPipelineURL indicates the URL of the CI pipeline.
+	CIPipelineURL = "ci.pipeline.url"
+
+	// CIProviderName indicates the name of the CI provider.
+	CIProviderName = "ci.provider.name"
+
+	// CIStageName indicates the name of the CI stage.
+	CIStageName = "ci.stage.name"
+
+	// CINodeName indicates the name of the node in the CI environment.
+	CINodeName = "ci.node.name"
+
+	// CINodeLabels indicates the labels associated with the node in the CI environment.
+	CINodeLabels = "ci.node.labels"
+
+	// CIWorkspacePath records an absolute path to the directory where the project has been checked out.
+	CIWorkspacePath = "ci.workspace_path"
+
+	// CIEnvVars contains environment variables used to get the pipeline correlation ID.
+	CIEnvVars = "_dd.ci.env_vars"
+)
diff --git a/main/civisibility/constants/env.go b/main/civisibility/constants/env.go
new file mode 100644
index 0000000..ebc00fc
--- /dev/null
+++ b/main/civisibility/constants/env.go
@@ -0,0 +1,27 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package constants
+
+const (
+	// CIVisibilityEnabledEnvironmentVariable indicates if CI Visibility mode is enabled.
+	// This environment variable should be set to "1" or "true" to enable CI Visibility mode, which activates tracing and other
+	// features related to CI Visibility in the Datadog platform.
+	CIVisibilityEnabledEnvironmentVariable = "DD_CIVISIBILITY_ENABLED"
+
+	// CIVisibilityAgentlessEnabledEnvironmentVariable indicates if CI Visibility agentless mode is enabled.
+	// This environment variable should be set to "1" or "true" to enable agentless mode for CI Visibility, where traces
+	// are sent directly to Datadog without using a local agent.
+	CIVisibilityAgentlessEnabledEnvironmentVariable = "DD_CIVISIBILITY_AGENTLESS_ENABLED"
+
+	// CIVisibilityAgentlessURLEnvironmentVariable forces the agentless URL to a custom one.
+	// This environment variable allows you to specify a custom URL for the agentless intake in CI Visibility mode.
+	CIVisibilityAgentlessURLEnvironmentVariable = "DD_CIVISIBILITY_AGENTLESS_URL"
+
+	// APIKeyEnvironmentVariable indicates the API key to be used for agentless intake.
+	// This environment variable should be set to your Datadog API key, allowing the agentless mode to authenticate and
+	// send data directly to the Datadog platform.
+	APIKeyEnvironmentVariable = "DD_API_KEY"
+)
diff --git a/main/civisibility/constants/git.go b/main/civisibility/constants/git.go
new file mode 100644
index 0000000..6265be3
--- /dev/null
+++ b/main/civisibility/constants/git.go
@@ -0,0 +1,52 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package constants
+
+const (
+	// GitBranch indicates the current git branch.
+	// This constant is used to tag traces with the branch name being used in the CI/CD process.
+	GitBranch = "git.branch"
+
+	// GitCommitAuthorDate indicates the git commit author date related to the build.
+	// This constant is used to tag traces with the date when the author created the commit.
+	GitCommitAuthorDate = "git.commit.author.date"
+
+	// GitCommitAuthorEmail indicates the git commit author email related to the build.
+	// This constant is used to tag traces with the email of the author who created the commit.
+	GitCommitAuthorEmail = "git.commit.author.email"
+
+	// GitCommitAuthorName indicates the git commit author name related to the build.
+	// This constant is used to tag traces with the name of the author who created the commit.
+	GitCommitAuthorName = "git.commit.author.name"
+
+	// GitCommitCommitterDate indicates the git commit committer date related to the build.
+	// This constant is used to tag traces with the date when the committer applied the commit.
+	GitCommitCommitterDate = "git.commit.committer.date"
+
+	// GitCommitCommitterEmail indicates the git commit committer email related to the build.
+	// This constant is used to tag traces with the email of the committer who applied the commit.
+	GitCommitCommitterEmail = "git.commit.committer.email"
+
+	// GitCommitCommitterName indicates the git commit committer name related to the build.
+	// This constant is used to tag traces with the name of the committer who applied the commit.
+	GitCommitCommitterName = "git.commit.committer.name"
+
+	// GitCommitMessage indicates the git commit message related to the build.
+	// This constant is used to tag traces with the message associated with the commit.
+	GitCommitMessage = "git.commit.message"
+
+	// GitCommitSHA indicates the git commit SHA1 hash related to the build.
+	// This constant is used to tag traces with the SHA1 hash of the commit.
+	GitCommitSHA = "git.commit.sha"
+
+	// GitRepositoryURL indicates the git repository URL related to the build.
+	// This constant is used to tag traces with the URL of the repository where the commit is stored.
+	GitRepositoryURL = "git.repository_url"
+
+	// GitTag indicates the current git tag.
+	// This constant is used to tag traces with the tag name associated with the current commit.
+	GitTag = "git.tag"
+)
diff --git a/main/civisibility/constants/os.go b/main/civisibility/constants/os.go
new file mode 100644
index 0000000..e065f17
--- /dev/null
+++ b/main/civisibility/constants/os.go
@@ -0,0 +1,21 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package constants
+
+const (
+	// OSPlatform indicates the operating system family (e.g., linux, windows, darwin).
+	// This constant is used to tag traces with the operating system family on which the tests are running.
+	OSPlatform = "os.platform"
+
+	// OSVersion indicates the version of the operating system.
+	// This constant is used to tag traces with the specific version of the operating system on which the tests are running.
+	OSVersion = "os.version"
+
+	// OSArchitecture indicates the architecture this SDK is built for (e.g., amd64, 386, arm).
+	// This constant is used to tag traces with the architecture of the operating system for which the tests are built.
+	// Note: This could be 32-bit on a 64-bit system.
+	OSArchitecture = "os.architecture"
+)
diff --git a/main/civisibility/constants/runtime.go b/main/civisibility/constants/runtime.go
new file mode 100644
index 0000000..15257ef
--- /dev/null
+++ b/main/civisibility/constants/runtime.go
@@ -0,0 +1,16 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package constants
+
+const (
+	// RuntimeName indicates the name of the runtime compiler.
+	// This constant is used to tag traces with the name of the runtime compiler being used (e.g., Go, JVM).
+	RuntimeName = "runtime.name"
+
+	// RuntimeVersion indicates the version of the runtime compiler.
+	// This constant is used to tag traces with the specific version of the runtime compiler being used.
+	RuntimeVersion = "runtime.version"
+)
diff --git a/main/civisibility/constants/span_types.go b/main/civisibility/constants/span_types.go
new file mode 100644
index 0000000..55f3fe5
--- /dev/null
+++ b/main/civisibility/constants/span_types.go
@@ -0,0 +1,28 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package constants
+
+const (
+	// SpanTypeTest marks a span as a test execution.
+	// This constant is used to indicate that the span represents a test execution.
+	SpanTypeTest = "test"
+
+	// SpanTypeTestSuite marks a span as a test suite.
+	// This constant is used to indicate that the span represents the end of a test suite.
+	SpanTypeTestSuite = "test_suite_end"
+
+	// SpanTypeTestModule marks a span as a test module.
+	// This constant is used to indicate that the span represents the end of a test module.
+	SpanTypeTestModule = "test_module_end"
+
+	// SpanTypeTestSession marks a span as a test session.
+	// This constant is used to indicate that the span represents the end of a test session.
+	SpanTypeTestSession = "test_session_end"
+
+	// SpanTypeSpan marks a span as a span event.
+	// This constant is used to indicate that the span represents a general span event.
+	SpanTypeSpan = "span"
+)
diff --git a/main/civisibility/constants/tags.go b/main/civisibility/constants/tags.go
new file mode 100644
index 0000000..4563cb8
--- /dev/null
+++ b/main/civisibility/constants/tags.go
@@ -0,0 +1,43 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package constants
+
+const (
+	// Origin is a tag used to indicate the origin of the data.
+	// This tag helps in identifying the source of the trace data.
+	Origin = "_dd.origin"
+
+	// CIAppTestOrigin defines the CIApp test origin value.
+	// This constant is used to tag traces that originate from CIApp test executions.
+	CIAppTestOrigin = "ciapp-test"
+
+	// TestSessionIDTag defines the test session ID tag for the CI Visibility Protocol.
+	// This constant is used to tag traces with the ID of the test session.
+	TestSessionIDTag string = "test_session_id"
+
+	// TestModuleIDTag defines the test module ID tag for the CI Visibility Protocol.
+	// This constant is used to tag traces with the ID of the test module.
+	TestModuleIDTag string = "test_module_id"
+
+	// TestSuiteIDTag defines the test suite ID tag for the CI Visibility Protocol.
+	// This constant is used to tag traces with the ID of the test suite.
+	TestSuiteIDTag string = "test_suite_id"
+
+	// ItrCorrelationIDTag defines the correlation ID for the intelligent test runner tag for the CI Visibility Protocol.
+	// This constant is used to tag traces with the correlation ID for intelligent test runs.
+	ItrCorrelationIDTag string = "itr_correlation_id"
+)
+
+// Coverage tags
+const (
+	// CodeCoverageEnabledTag defines if code coverage has been enabled.
+	// This constant is used to tag traces to indicate whether code coverage measurement is enabled.
+	CodeCoverageEnabledTag string = "test.code_coverage.enabled"
+
+	// CodeCoveragePercentageOfTotalLines defines the percentage of total code coverage by a session.
+	// This constant is used to tag traces with the percentage of code lines covered during the test session.
+	CodeCoveragePercentageOfTotalLines string = "test.code_coverage.lines_pct"
+)
diff --git a/main/civisibility/constants/test_tags.go b/main/civisibility/constants/test_tags.go
new file mode 100644
index 0000000..3a621a2
--- /dev/null
+++ b/main/civisibility/constants/test_tags.go
@@ -0,0 +1,90 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package constants
+
+const (
+	// TestModule indicates the test module name.
+	// This constant is used to tag traces with the name of the test module.
+	TestModule = "test.module"
+
+	// TestSuite indicates the test suite name.
+	// This constant is used to tag traces with the name of the test suite.
+	TestSuite = "test.suite"
+
+	// TestName indicates the test name.
+	// This constant is used to tag traces with the name of the test.
+	TestName = "test.name"
+
+	// TestType indicates the type of the test (e.g., test, benchmark).
+	// This constant is used to tag traces with the type of the test.
+	TestType = "test.type"
+
+	// TestFramework indicates the test framework name.
+	// This constant is used to tag traces with the name of the test framework.
+	TestFramework = "test.framework"
+
+	// TestFrameworkVersion indicates the test framework version.
+	// This constant is used to tag traces with the version of the test framework.
+	TestFrameworkVersion = "test.framework_version"
+
+	// TestStatus indicates the test execution status.
+	// This constant is used to tag traces with the execution status of the test.
+	TestStatus = "test.status"
+
+	// TestSkipReason indicates the skip reason of the test.
+	// This constant is used to tag traces with the reason why the test was skipped.
+	TestSkipReason = "test.skip_reason"
+
+	// TestSourceFile indicates the source file where the test is located.
+	// This constant is used to tag traces with the file path of the test source code.
+	TestSourceFile = "test.source.file"
+
+	// TestSourceStartLine indicates the line of the source file where the test starts.
+	// This constant is used to tag traces with the line number in the source file where the test starts.
+	TestSourceStartLine = "test.source.start"
+
+	// TestCodeOwners indicates the test code owners.
+	// This constant is used to tag traces with the code owners responsible for the test.
+	TestCodeOwners = "test.codeowners"
+
+	// TestCommand indicates the test command.
+	// This constant is used to tag traces with the command used to execute the test.
+	TestCommand = "test.command"
+
+	// TestCommandExitCode indicates the test command exit code.
+	// This constant is used to tag traces with the exit code of the test command.
+	TestCommandExitCode = "test.exit_code"
+
+	// TestCommandWorkingDirectory indicates the test command working directory relative to the source root.
+	// This constant is used to tag traces with the working directory path relative to the source root.
+	TestCommandWorkingDirectory = "test.working_directory"
+)
+
+// Define valid test status types.
+const (
+	// TestStatusPass marks test execution as passed.
+	// This constant is used to tag traces with a status indicating that the test passed.
+	TestStatusPass = "pass"
+
+	// TestStatusFail marks test execution as failed.
+	// This constant is used to tag traces with a status indicating that the test failed.
+	TestStatusFail = "fail"
+
+	// TestStatusSkip marks test execution as skipped.
+	// This constant is used to tag traces with a status indicating that the test was skipped.
+	TestStatusSkip = "skip"
+)
+
+// Define valid test types.
+const (
+	// TestTypeTest defines test type as test.
+	// This constant is used to tag traces indicating that the type of test is a standard test.
+	TestTypeTest = "test"
+
+	// TestTypeBenchmark defines test type as benchmark.
+	// This constant is used to tag traces indicating that the type of test is a benchmark.
+	TestTypeBenchmark = "benchmark"
+)
diff --git a/main/civisibility/integrations/civisibility.go b/main/civisibility/integrations/civisibility.go
new file mode 100644
index 0000000..dfbc6f3
--- /dev/null
+++ b/main/civisibility/integrations/civisibility.go
@@ -0,0 +1,119 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package integrations
+
+import (
+	"os"
+	"os/signal"
+	"regexp"
+	"strings"
+	"sync"
+	"syscall"
+
+	"ci-visibility-test-github/main/civisibility/constants"
+	"ci-visibility-test-github/main/civisibility/utils"
+
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer"
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
+)
+
+// ciVisibilityCloseAction defines an action to be executed when CI visibility is closing.
+type ciVisibilityCloseAction func()
+
+var (
+	// ciVisibilityInitializationOnce ensures we initialize the CI visibility tracer only once.
+	ciVisibilityInitializationOnce sync.Once
+
+	// closeActions holds CI visibility close actions.
+	closeActions []ciVisibilityCloseAction
+
+	// closeActionsMutex synchronizes access to closeActions.
+	closeActionsMutex sync.Mutex
+
+	// mTracer contains the mock tracer instance for testing purposes
+	mTracer mocktracer.Tracer
+)
+
+// EnsureCiVisibilityInitialization initializes the CI visibility tracer if it hasn't been initialized already.
+func EnsureCiVisibilityInitialization() {
+	internalCiVisibilityInitialization(func(opts []tracer.StartOption) {
+		// Initialize the tracer.
+		tracer.Start(opts...)
+	})
+}
+
+// InitializeCIVisibilityMock initialize the mocktracer for CI Visibility usage
+func InitializeCIVisibilityMock() mocktracer.Tracer {
+	internalCiVisibilityInitialization(func([]tracer.StartOption) {
+		// Initialize the mocktracer
+		mTracer = mocktracer.Start()
+	})
+	return mTracer
+}
+
+func internalCiVisibilityInitialization(tracerInitializer func([]tracer.StartOption)) {
+	ciVisibilityInitializationOnce.Do(func() {
+		// Since calling this method indicates we are in CI Visibility mode, set the environment variable.
+		_ = os.Setenv(constants.CIVisibilityEnabledEnvironmentVariable, "1")
+
+		// Avoid sampling rate warning (in CI Visibility mode we send all data)
+		_ = os.Setenv("DD_TRACE_SAMPLE_RATE", "1")
+
+		// Preload the CodeOwner file
+		_ = utils.GetCodeOwners()
+
+		// Preload all CI, Git, and CodeOwners tags.
+		ciTags := utils.GetCITags()
+
+		// Check if DD_SERVICE has been set; otherwise default to the repo name (from the spec).
+		var opts []tracer.StartOption
+		if v := os.Getenv("DD_SERVICE"); v == "" {
+			if repoURL, ok := ciTags[constants.GitRepositoryURL]; ok {
+				// regex to sanitize the repository url to be used as a service name
+				repoRegex := regexp.MustCompile(`(?m)/([a-zA-Z0-9\\\-_.]*)$`)
+				matches := repoRegex.FindStringSubmatch(repoURL)
+				if len(matches) > 1 {
+					repoURL = strings.TrimSuffix(matches[1], ".git")
+				}
+				opts = append(opts, tracer.WithService(repoURL))
+			}
+		}
+
+		// Initialize the tracer
+		tracerInitializer(opts)
+
+		// Handle SIGINT and SIGTERM signals to ensure we close all open spans and flush the tracer before exiting
+		signals := make(chan os.Signal, 1)
+		signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
+		go func() {
+			<-signals
+			ExitCiVisibility()
+			os.Exit(1)
+		}()
+	})
+}
+
+// PushCiVisibilityCloseAction adds a close action to be executed when CI visibility exits.
+func PushCiVisibilityCloseAction(action ciVisibilityCloseAction) {
+	closeActionsMutex.Lock()
+	defer closeActionsMutex.Unlock()
+	closeActions = append([]ciVisibilityCloseAction{action}, closeActions...)
+}
+
+// ExitCiVisibility executes all registered close actions and stops the tracer.
+func ExitCiVisibility() {
+	closeActionsMutex.Lock()
+	defer closeActionsMutex.Unlock()
+	defer func() {
+		closeActions = []ciVisibilityCloseAction{}
+
+		tracer.Flush()
+		tracer.Stop()
+	}()
+	for _, v := range closeActions {
+		v()
+	}
+}
diff --git a/main/civisibility/integrations/gotesting/reflections.go b/main/civisibility/integrations/gotesting/reflections.go
new file mode 100644
index 0000000..6aaac3e
--- /dev/null
+++ b/main/civisibility/integrations/gotesting/reflections.go
@@ -0,0 +1,103 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package gotesting
+
+import (
+	"errors"
+	"reflect"
+	"sync"
+	"testing"
+	"unsafe"
+)
+
+// getFieldPointerFrom gets an unsafe.Pointer (gc-safe type of pointer) to a struct field
+// useful to get or set values to private field
+func getFieldPointerFrom(value any, fieldName string) (unsafe.Pointer, error) {
+	indirectValue := reflect.Indirect(reflect.ValueOf(value))
+	member := indirectValue.FieldByName(fieldName)
+	if member.IsValid() {
+		return unsafe.Pointer(member.UnsafeAddr()), nil
+	}
+
+	return unsafe.Pointer(nil), errors.New("member is invalid")
+}
+
+// TESTING
+
+// getInternalTestArray gets the pointer to the testing.InternalTest array inside a
+// testing.M instance containing all the "root" tests
+func getInternalTestArray(m *testing.M) *[]testing.InternalTest {
+	if ptr, err := getFieldPointerFrom(m, "tests"); err == nil {
+		return (*[]testing.InternalTest)(ptr)
+	}
+	return nil
+}
+
+// BENCHMARKS
+
+// get the pointer to the internal benchmark array
+// getInternalBenchmarkArray gets the pointer to the testing.InternalBenchmark array inside
+// a testing.M instance containing all the "root" benchmarks
+func getInternalBenchmarkArray(m *testing.M) *[]testing.InternalBenchmark {
+	if ptr, err := getFieldPointerFrom(m, "benchmarks"); err == nil {
+		return (*[]testing.InternalBenchmark)(ptr)
+	}
+	return nil
+}
+
+// commonPrivateFields is collection of required private fields from testing.common
+type commonPrivateFields struct {
+	mu    *sync.RWMutex
+	level *int
+	name  *string // Name of test or benchmark.
+}
+
+// AddLevel increase or decrease the testing.common.level field value, used by
+// testing.B to create the name of the benchmark test
+func (c *commonPrivateFields) AddLevel(delta int) int {
+	c.mu.Lock()
+	defer c.mu.Unlock()
+	*c.level = *c.level + delta
+	return *c.level
+}
+
+// benchmarkPrivateFields is a collection of required private fields from testing.B
+// also contains a pointer to the original testing.B for easy access
+type benchmarkPrivateFields struct {
+	commonPrivateFields
+	B         *testing.B
+	benchFunc *func(b *testing.B)
+	result    *testing.BenchmarkResult
+}
+
+// getBenchmarkPrivateFields is a method to retrieve all required privates field from
+// testing.B, returning a benchmarkPrivateFields instance
+func getBenchmarkPrivateFields(b *testing.B) *benchmarkPrivateFields {
+	benchFields := &benchmarkPrivateFields{
+		B: b,
+	}
+
+	// common
+	if ptr, err := getFieldPointerFrom(b, "mu"); err == nil {
+		benchFields.mu = (*sync.RWMutex)(ptr)
+	}
+	if ptr, err := getFieldPointerFrom(b, "level"); err == nil {
+		benchFields.level = (*int)(ptr)
+	}
+	if ptr, err := getFieldPointerFrom(b, "name"); err == nil {
+		benchFields.name = (*string)(ptr)
+	}
+
+	// benchmark
+	if ptr, err := getFieldPointerFrom(b, "benchFunc"); err == nil {
+		benchFields.benchFunc = (*func(b *testing.B))(ptr)
+	}
+	if ptr, err := getFieldPointerFrom(b, "result"); err == nil {
+		benchFields.result = (*testing.BenchmarkResult)(ptr)
+	}
+
+	return benchFields
+}
diff --git a/main/civisibility/integrations/gotesting/reflections_test.go b/main/civisibility/integrations/gotesting/reflections_test.go
new file mode 100644
index 0000000..453cd7a
--- /dev/null
+++ b/main/civisibility/integrations/gotesting/reflections_test.go
@@ -0,0 +1,150 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package gotesting
+
+import (
+	"sync"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+// TestGetFieldPointerFrom tests the getFieldPointerFrom function.
+func TestGetFieldPointerFrom(t *testing.T) {
+	// Create a mock struct with a private field
+	mockStruct := struct {
+		privateField string
+	}{
+		privateField: "testValue",
+	}
+
+	// Attempt to get a pointer to the private field
+	ptr, err := getFieldPointerFrom(&mockStruct, "privateField")
+	if err != nil {
+		t.Fatalf("Expected no error, got %v", err)
+	}
+
+	if ptr == nil {
+		t.Fatal("Expected a valid pointer, got nil")
+	}
+
+	// Dereference the pointer to get the actual value
+	actualValue := (*string)(ptr)
+	if *actualValue != mockStruct.privateField {
+		t.Fatalf("Expected 'testValue', got %s", *actualValue)
+	}
+
+	// Modify the value through the pointer
+	*actualValue = "modified value"
+	if *actualValue != mockStruct.privateField {
+		t.Fatalf("Expected 'modified value', got %s", mockStruct.privateField)
+	}
+
+	// Attempt to get a pointer to a non-existent field
+	_, err = getFieldPointerFrom(&mockStruct, "nonExistentField")
+	if err == nil {
+		t.Fatal("Expected an error for non-existent field, got nil")
+	}
+}
+
+// TestGetInternalTestArray tests the getInternalTestArray function.
+func TestGetInternalTestArray(t *testing.T) {
+	assert := assert.New(t)
+
+	// Get the internal test array from the mock testing.M
+	tests := getInternalTestArray(currentM)
+	assert.NotNil(tests)
+
+	// Check that the test array contains the expected test
+	var testNames []string
+	for _, v := range *tests {
+		testNames = append(testNames, v.Name)
+		assert.NotNil(v.F)
+	}
+
+	assert.Contains(testNames, "TestGetFieldPointerFrom")
+	assert.Contains(testNames, "TestGetInternalTestArray")
+	assert.Contains(testNames, "TestGetInternalBenchmarkArray")
+	assert.Contains(testNames, "TestCommonPrivateFields_AddLevel")
+	assert.Contains(testNames, "TestGetBenchmarkPrivateFields")
+}
+
+// TestGetInternalBenchmarkArray tests the getInternalBenchmarkArray function.
+func TestGetInternalBenchmarkArray(t *testing.T) {
+	assert := assert.New(t)
+
+	// Get the internal benchmark array from the mock testing.M
+	benchmarks := getInternalBenchmarkArray(currentM)
+	assert.NotNil(benchmarks)
+
+	// Check that the benchmark array contains the expected benchmark
+	var testNames []string
+	for _, v := range *benchmarks {
+		testNames = append(testNames, v.Name)
+		assert.NotNil(v.F)
+	}
+
+	assert.Contains(testNames, "BenchmarkDummy")
+}
+
+// TestCommonPrivateFields_AddLevel tests the AddLevel method of commonPrivateFields.
+func TestCommonPrivateFields_AddLevel(t *testing.T) {
+	// Create a commonPrivateFields struct with a mutex and a level
+	level := 1
+	commonFields := &commonPrivateFields{
+		mu:    &sync.RWMutex{},
+		level: &level,
+	}
+
+	// Add a level and check the new level
+	newLevel := commonFields.AddLevel(1)
+	if newLevel != 2 || newLevel != *commonFields.level {
+		t.Fatalf("Expected level to be 2, got %d", newLevel)
+	}
+
+	// Subtract a level and check the new level
+	newLevel = commonFields.AddLevel(-1)
+	if newLevel != 1 || newLevel != *commonFields.level {
+		t.Fatalf("Expected level to be 1, got %d", newLevel)
+	}
+}
+
+// TestGetBenchmarkPrivateFields tests the getBenchmarkPrivateFields function.
+func TestGetBenchmarkPrivateFields(t *testing.T) {
+	// Create a new testing.B instance
+	b := &testing.B{}
+
+	// Get the private fields of the benchmark
+	benchFields := getBenchmarkPrivateFields(b)
+	if benchFields == nil {
+		t.Fatal("Expected a valid benchmarkPrivateFields, got nil")
+	}
+
+	// Set values to the private fields
+	*benchFields.name = "BenchmarkTest"
+	*benchFields.level = 1
+	*benchFields.benchFunc = func(b *testing.B) {}
+	*benchFields.result = testing.BenchmarkResult{}
+
+	// Check that the private fields have the expected values
+	if benchFields.level == nil || *benchFields.level != 1 {
+		t.Fatalf("Expected level to be 1, got %v", *benchFields.level)
+	}
+
+	if benchFields.name == nil || *benchFields.name != b.Name() {
+		t.Fatalf("Expected name to be 'BenchmarkTest', got %v", *benchFields.name)
+	}
+
+	if benchFields.benchFunc == nil {
+		t.Fatal("Expected benchFunc to be set, got nil")
+	}
+
+	if benchFields.result == nil {
+		t.Fatal("Expected result to be set, got nil")
+	}
+}
+
+func BenchmarkDummy(*testing.B) {}
diff --git a/main/civisibility/integrations/gotesting/testing.go b/main/civisibility/integrations/gotesting/testing.go
new file mode 100644
index 0000000..593892c
--- /dev/null
+++ b/main/civisibility/integrations/gotesting/testing.go
@@ -0,0 +1,354 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package gotesting
+
+import (
+	"fmt"
+	"os"
+	"reflect"
+	"runtime"
+	"strings"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"ci-visibility-test-github/main/civisibility/integrations"
+	"ci-visibility-test-github/main/civisibility/utils"
+
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
+)
+
+const (
+	// testFramework represents the name of the testing framework.
+	testFramework = "golang.org/pkg/testing"
+)
+
+var (
+	// session represents the CI visibility test session.
+	session integrations.DdTestSession
+
+	// testInfos holds information about the instrumented tests.
+	testInfos []*testingTInfo
+
+	// benchmarkInfos holds information about the instrumented benchmarks.
+	benchmarkInfos []*testingBInfo
+
+	// modulesCounters keeps track of the number of tests per module.
+	modulesCounters = map[string]*int32{}
+
+	// suitesCounters keeps track of the number of tests per suite.
+	suitesCounters = map[string]*int32{}
+)
+
+type (
+	// commonInfo holds common information about tests and benchmarks.
+	commonInfo struct {
+		moduleName string
+		suiteName  string
+		testName   string
+	}
+
+	// testingTInfo holds information specific to tests.
+	testingTInfo struct {
+		commonInfo
+		originalFunc func(*testing.T)
+	}
+
+	// testingBInfo holds information specific to benchmarks.
+	testingBInfo struct {
+		commonInfo
+		originalFunc func(b *testing.B)
+	}
+
+	// M is a wrapper around testing.M to provide instrumentation.
+	M testing.M
+)
+
+// Run initializes CI Visibility, instruments tests and benchmarks, and runs them.
+func (ddm *M) Run() int {
+	integrations.EnsureCiVisibilityInitialization()
+	defer integrations.ExitCiVisibility()
+
+	// Create a new test session for CI visibility.
+	session = integrations.CreateTestSession()
+
+	m := (*testing.M)(ddm)
+
+	// Instrument the internal tests for CI visibility.
+	ddm.instrumentInternalTests(getInternalTestArray(m))
+
+	// Instrument the internal benchmarks for CI visibility.
+	for _, v := range os.Args {
+		// check if benchmarking is enabled to instrument
+		if strings.Contains(v, "-bench") || strings.Contains(v, "test.bench") {
+			ddm.instrumentInternalBenchmarks(getInternalBenchmarkArray(m))
+			break
+		}
+	}
+
+	// Run the tests and benchmarks.
+	var exitCode = m.Run()
+
+	// Close the session and return the exit code.
+	session.Close(exitCode)
+	return exitCode
+}
+
+// instrumentInternalTests instruments the internal tests for CI visibility.
+func (ddm *M) instrumentInternalTests(internalTests *[]testing.InternalTest) {
+	if internalTests != nil {
+		// Extract info from internal tests
+		testInfos = make([]*testingTInfo, len(*internalTests))
+		for idx, test := range *internalTests {
+			moduleName, suiteName := utils.GetModuleAndSuiteName(reflect.Indirect(reflect.ValueOf(test.F)).Pointer())
+			testInfo := &testingTInfo{
+				originalFunc: test.F,
+				commonInfo: commonInfo{
+					moduleName: moduleName,
+					suiteName:  suiteName,
+					testName:   test.Name,
+				},
+			}
+
+			// Initialize module and suite counters if not already present.
+			if _, ok := modulesCounters[moduleName]; !ok {
+				var v int32
+				modulesCounters[moduleName] = &v
+			}
+			// Increment the test count in the module.
+			atomic.AddInt32(modulesCounters[moduleName], 1)
+
+			if _, ok := suitesCounters[suiteName]; !ok {
+				var v int32
+				suitesCounters[suiteName] = &v
+			}
+			// Increment the test count in the suite.
+			atomic.AddInt32(suitesCounters[suiteName], 1)
+
+			testInfos[idx] = testInfo
+		}
+
+		// Create new instrumented internal tests
+		newTestArray := make([]testing.InternalTest, len(*internalTests))
+		for idx, testInfo := range testInfos {
+			newTestArray[idx] = testing.InternalTest{
+				Name: testInfo.testName,
+				F:    ddm.executeInternalTest(testInfo),
+			}
+		}
+		*internalTests = newTestArray
+	}
+}
+
+// executeInternalTest wraps the original test function to include CI visibility instrumentation.
+func (ddm *M) executeInternalTest(testInfo *testingTInfo) func(*testing.T) {
+	originalFunc := runtime.FuncForPC(reflect.Indirect(reflect.ValueOf(testInfo.originalFunc)).Pointer())
+	return func(t *testing.T) {
+		// Create or retrieve the module, suite, and test for CI visibility.
+		module := session.GetOrCreateModuleWithFramework(testInfo.moduleName, testFramework, runtime.Version())
+		suite := module.GetOrCreateSuite(testInfo.suiteName)
+		test := suite.CreateTest(testInfo.testName)
+		test.SetTestFunc(originalFunc)
+		setCiVisibilityTest(t, test)
+		defer func() {
+			if r := recover(); r != nil {
+				// Handle panic and set error information.
+				test.SetErrorInfo("panic", fmt.Sprint(r), utils.GetStacktrace(1))
+				suite.SetTag(ext.Error, true)
+				module.SetTag(ext.Error, true)
+				test.Close(integrations.ResultStatusFail)
+				checkModuleAndSuite(module, suite)
+				integrations.ExitCiVisibility()
+				panic(r)
+			} else {
+				// Normal finalization: determine the test result based on its state.
+				if t.Failed() {
+					test.SetTag(ext.Error, true)
+					suite.SetTag(ext.Error, true)
+					module.SetTag(ext.Error, true)
+					test.Close(integrations.ResultStatusFail)
+				} else if t.Skipped() {
+					test.Close(integrations.ResultStatusSkip)
+				} else {
+					test.Close(integrations.ResultStatusPass)
+				}
+
+				checkModuleAndSuite(module, suite)
+			}
+		}()
+
+		// Execute the original test function.
+		testInfo.originalFunc(t)
+	}
+}
+
+// instrumentInternalBenchmarks instruments the internal benchmarks for CI visibility.
+func (ddm *M) instrumentInternalBenchmarks(internalBenchmarks *[]testing.InternalBenchmark) {
+	if internalBenchmarks != nil {
+		// Extract info from internal benchmarks
+		benchmarkInfos = make([]*testingBInfo, len(*internalBenchmarks))
+		for idx, benchmark := range *internalBenchmarks {
+			moduleName, suiteName := utils.GetModuleAndSuiteName(reflect.Indirect(reflect.ValueOf(benchmark.F)).Pointer())
+			benchmarkInfo := &testingBInfo{
+				originalFunc: benchmark.F,
+				commonInfo: commonInfo{
+					moduleName: moduleName,
+					suiteName:  suiteName,
+					testName:   benchmark.Name,
+				},
+			}
+
+			// Initialize module and suite counters if not already present.
+			if _, ok := modulesCounters[moduleName]; !ok {
+				var v int32
+				modulesCounters[moduleName] = &v
+			}
+			// Increment the test count in the module.
+			atomic.AddInt32(modulesCounters[moduleName], 1)
+
+			if _, ok := suitesCounters[suiteName]; !ok {
+				var v int32
+				suitesCounters[suiteName] = &v
+			}
+			// Increment the test count in the suite.
+			atomic.AddInt32(suitesCounters[suiteName], 1)
+
+			benchmarkInfos[idx] = benchmarkInfo
+		}
+
+		// Create a new instrumented internal benchmarks
+		newBenchmarkArray := make([]testing.InternalBenchmark, len(*internalBenchmarks))
+		for idx, benchmarkInfo := range benchmarkInfos {
+			newBenchmarkArray[idx] = testing.InternalBenchmark{
+				Name: benchmarkInfo.testName,
+				F:    ddm.executeInternalBenchmark(benchmarkInfo),
+			}
+		}
+
+		*internalBenchmarks = newBenchmarkArray
+	}
+}
+
+// executeInternalBenchmark wraps the original benchmark function to include CI visibility instrumentation.
+func (ddm *M) executeInternalBenchmark(benchmarkInfo *testingBInfo) func(*testing.B) {
+	return func(b *testing.B) {
+
+		// decrement level
+		getBenchmarkPrivateFields(b).AddLevel(-1)
+
+		startTime := time.Now()
+		originalFunc := runtime.FuncForPC(reflect.Indirect(reflect.ValueOf(benchmarkInfo.originalFunc)).Pointer())
+		module := session.GetOrCreateModuleWithFrameworkAndStartTime(benchmarkInfo.moduleName, testFramework, runtime.Version(), startTime)
+		suite := module.GetOrCreateSuiteWithStartTime(benchmarkInfo.suiteName, startTime)
+		test := suite.CreateTestWithStartTime(benchmarkInfo.testName, startTime)
+		test.SetTestFunc(originalFunc)
+
+		// Run the original benchmark function.
+		var iPfOfB *benchmarkPrivateFields
+		var recoverFunc *func(r any)
+		b.Run(b.Name(), func(b *testing.B) {
+			// Stop the timer to perform initialization and replacements.
+			b.StopTimer()
+
+			defer func() {
+				if r := recover(); r != nil {
+					// Handle panic if it occurs during benchmark execution.
+					if recoverFunc != nil {
+						fn := *recoverFunc
+						fn(r)
+					}
+					panic(r)
+				}
+			}()
+
+			// Enable allocation reporting.
+			b.ReportAllocs()
+			// Retrieve the private fields of the inner testing.B.
+			iPfOfB = getBenchmarkPrivateFields(b)
+			// Replace the benchmark function with the original one (this must be executed only once - the first iteration[b.run1]).
+			*iPfOfB.benchFunc = benchmarkInfo.originalFunc
+			// Set the CI visibility benchmark.
+			setCiVisibilityBenchmark(b, test)
+
+			// Restart the timer and execute the original benchmark function.
+			b.ResetTimer()
+			b.StartTimer()
+			benchmarkInfo.originalFunc(b)
+		})
+
+		endTime := time.Now()
+		results := iPfOfB.result
+
+		// Set benchmark data for CI visibility.
+		test.SetBenchmarkData("duration", map[string]any{
+			"run":  results.N,
+			"mean": results.NsPerOp(),
+		})
+		test.SetBenchmarkData("memory_total_operations", map[string]any{
+			"run":            results.N,
+			"mean":           results.AllocsPerOp(),
+			"statistics.max": results.MemAllocs,
+		})
+		test.SetBenchmarkData("mean_heap_allocations", map[string]any{
+			"run":  results.N,
+			"mean": results.AllocedBytesPerOp(),
+		})
+		test.SetBenchmarkData("total_heap_allocations", map[string]any{
+			"run":  results.N,
+			"mean": iPfOfB.result.MemBytes,
+		})
+		if len(results.Extra) > 0 {
+			mapConverted := map[string]any{}
+			for k, v := range results.Extra {
+				mapConverted[k] = v
+			}
+			test.SetBenchmarkData("extra", mapConverted)
+		}
+
+		// Define a function to handle panic during benchmark finalization.
+		panicFunc := func(r any) {
+			test.SetErrorInfo("panic", fmt.Sprint(r), utils.GetStacktrace(1))
+			suite.SetTag(ext.Error, true)
+			module.SetTag(ext.Error, true)
+			test.Close(integrations.ResultStatusFail)
+			checkModuleAndSuite(module, suite)
+			integrations.ExitCiVisibility()
+		}
+		recoverFunc = &panicFunc
+
+		// Normal finalization: determine the benchmark result based on its state.
+		if iPfOfB.B.Failed() {
+			test.SetTag(ext.Error, true)
+			suite.SetTag(ext.Error, true)
+			module.SetTag(ext.Error, true)
+			test.CloseWithFinishTime(integrations.ResultStatusFail, endTime)
+		} else if iPfOfB.B.Skipped() {
+			test.CloseWithFinishTime(integrations.ResultStatusSkip, endTime)
+		} else {
+			test.CloseWithFinishTime(integrations.ResultStatusPass, endTime)
+		}
+
+		checkModuleAndSuite(module, suite)
+	}
+}
+
+// RunM runs the tests and benchmarks using CI visibility.
+func RunM(m *testing.M) int {
+	return (*M)(m).Run()
+}
+
+// checkModuleAndSuite checks and closes the modules and suites if all tests are executed.
+func checkModuleAndSuite(module integrations.DdTestModule, suite integrations.DdTestSuite) {
+	// If all tests in a suite has been executed we can close the suite
+	if atomic.AddInt32(suitesCounters[suite.Name()], -1) <= 0 {
+		suite.Close()
+	}
+
+	// If all tests in a module has been executed we can close the module
+	if atomic.AddInt32(modulesCounters[module.Name()], -1) <= 0 {
+		module.Close()
+	}
+}
diff --git a/main/civisibility/integrations/gotesting/testingB.go b/main/civisibility/integrations/gotesting/testingB.go
new file mode 100644
index 0000000..058f536
--- /dev/null
+++ b/main/civisibility/integrations/gotesting/testingB.go
@@ -0,0 +1,337 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package gotesting
+
+import (
+	"context"
+	"fmt"
+	"reflect"
+	"regexp"
+	"runtime"
+	"sync"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"ci-visibility-test-github/main/civisibility/integrations"
+	"ci-visibility-test-github/main/civisibility/utils"
+
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
+)
+
+var (
+	// ciVisibilityBenchmarks holds a map of *testing.B to civisibility.DdTest for tracking benchmarks.
+	ciVisibilityBenchmarks = map[*testing.B]integrations.DdTest{}
+
+	// ciVisibilityBenchmarksMutex is a read-write mutex for synchronizing access to ciVisibilityBenchmarks.
+	ciVisibilityBenchmarksMutex sync.RWMutex
+
+	// subBenchmarkAutoName is a placeholder name for CI Visibility sub-benchmarks.
+	subBenchmarkAutoName = "[DD:TestVisibility]"
+
+	// subBenchmarkAutoNameRegex is a regex pattern to match the sub-benchmark auto name.
+	subBenchmarkAutoNameRegex = regexp.MustCompile(`(?si)\/\[DD:TestVisibility\].*`)
+)
+
+// B is a type alias for testing.B to provide additional methods for CI visibility.
+type B testing.B
+
+// GetBenchmark is a helper to return *gotesting.B from *testing.B.
+// Internally, it is just a (*gotesting.B)(b) cast.
+func GetBenchmark(t *testing.B) *B { return (*B)(t) }
+
+// Run benchmarks f as a subbenchmark with the given name. It reports
+// whether there were any failures.
+//
+// A subbenchmark is like any other benchmark. A benchmark that calls Run at
+// least once will not be measured itself and will be called once with N=1.
+func (ddb *B) Run(name string, f func(*testing.B)) bool {
+	// Reflect the function to obtain its pointer.
+	fReflect := reflect.Indirect(reflect.ValueOf(f))
+	moduleName, suiteName := utils.GetModuleAndSuiteName(fReflect.Pointer())
+	originalFunc := runtime.FuncForPC(fReflect.Pointer())
+
+	// Increment the test count in the module.
+	atomic.AddInt32(modulesCounters[moduleName], 1)
+
+	// Increment the test count in the suite.
+	atomic.AddInt32(suitesCounters[suiteName], 1)
+
+	pb := (*testing.B)(ddb)
+	return pb.Run(subBenchmarkAutoName, func(b *testing.B) {
+		// The sub-benchmark implementation relies on creating a dummy sub benchmark (called [DD:TestVisibility]) with
+		// a Run over the original sub benchmark function to get the child results without interfering measurements
+		// By doing this the name of the sub-benchmark are changed
+		// from:
+		// 		benchmark/child
+		// to:
+		//		benchmark/[DD:TestVisibility]/child
+		// We use regex and decrement the depth level of the benchmark to restore the original name
+
+		// Decrement level.
+		bpf := getBenchmarkPrivateFields(b)
+		bpf.AddLevel(-1)
+
+		startTime := time.Now()
+		module := session.GetOrCreateModuleWithFrameworkAndStartTime(moduleName, testFramework, runtime.Version(), startTime)
+		suite := module.GetOrCreateSuiteWithStartTime(suiteName, startTime)
+		test := suite.CreateTestWithStartTime(fmt.Sprintf("%s/%s", pb.Name(), name), startTime)
+		test.SetTestFunc(originalFunc)
+
+		// Restore the original name without the sub-benchmark auto name.
+		*bpf.name = subBenchmarkAutoNameRegex.ReplaceAllString(*bpf.name, "")
+
+		// Run original benchmark.
+		var iPfOfB *benchmarkPrivateFields
+		var recoverFunc *func(r any)
+		b.Run(name, func(b *testing.B) {
+			// Stop the timer to do the initialization and replacements.
+			b.StopTimer()
+
+			defer func() {
+				if r := recover(); r != nil {
+					if recoverFunc != nil {
+						fn := *recoverFunc
+						fn(r)
+					}
+					panic(r)
+				}
+			}()
+
+			// First time we get the private fields of the inner testing.B.
+			iPfOfB = getBenchmarkPrivateFields(b)
+			// Replace this function with the original one (executed only once - the first iteration[b.run1]).
+			*iPfOfB.benchFunc = f
+			// Set b to the CI visibility test.
+			setCiVisibilityBenchmark(b, test)
+
+			// Enable the timer again.
+			b.ResetTimer()
+			b.StartTimer()
+
+			// Execute original func
+			f(b)
+		})
+
+		endTime := time.Now()
+		results := iPfOfB.result
+
+		// Set benchmark data for CI visibility.
+		test.SetBenchmarkData("duration", map[string]any{
+			"run":  results.N,
+			"mean": results.NsPerOp(),
+		})
+		test.SetBenchmarkData("memory_total_operations", map[string]any{
+			"run":            results.N,
+			"mean":           results.AllocsPerOp(),
+			"statistics.max": results.MemAllocs,
+		})
+		test.SetBenchmarkData("mean_heap_allocations", map[string]any{
+			"run":  results.N,
+			"mean": results.AllocedBytesPerOp(),
+		})
+		test.SetBenchmarkData("total_heap_allocations", map[string]any{
+			"run":  results.N,
+			"mean": iPfOfB.result.MemBytes,
+		})
+		if len(results.Extra) > 0 {
+			mapConverted := map[string]any{}
+			for k, v := range results.Extra {
+				mapConverted[k] = v
+			}
+			test.SetBenchmarkData("extra", mapConverted)
+		}
+
+		// Define a function to handle panic during benchmark finalization.
+		panicFunc := func(r any) {
+			test.SetErrorInfo("panic", fmt.Sprint(r), utils.GetStacktrace(1))
+			suite.SetTag(ext.Error, true)
+			module.SetTag(ext.Error, true)
+			test.Close(integrations.ResultStatusFail)
+			checkModuleAndSuite(module, suite)
+			integrations.ExitCiVisibility()
+		}
+		recoverFunc = &panicFunc
+
+		// Normal finalization: determine the benchmark result based on its state.
+		if iPfOfB.B.Failed() {
+			test.SetTag(ext.Error, true)
+			suite.SetTag(ext.Error, true)
+			module.SetTag(ext.Error, true)
+			test.CloseWithFinishTime(integrations.ResultStatusFail, endTime)
+		} else if iPfOfB.B.Skipped() {
+			test.CloseWithFinishTime(integrations.ResultStatusSkip, endTime)
+		} else {
+			test.CloseWithFinishTime(integrations.ResultStatusPass, endTime)
+		}
+
+		checkModuleAndSuite(module, suite)
+	})
+}
+
+// Context returns the CI Visibility context of the Test span.
+// This may be used to create test's children spans useful for
+// integration tests.
+func (ddb *B) Context() context.Context {
+	b := (*testing.B)(ddb)
+	ciTest := getCiVisibilityBenchmark(b)
+	if ciTest != nil {
+		return ciTest.Context()
+	}
+
+	return context.Background()
+}
+
+// Fail marks the function as having failed but continues execution.
+func (ddb *B) Fail() { ddb.getBWithError("Fail", "failed test").Fail() }
+
+// FailNow marks the function as having failed and stops its execution
+// by calling runtime.Goexit (which then runs all deferred calls in the
+// current goroutine). Execution will continue at the next test or benchmark.
+// FailNow must be called from the goroutine running the test or benchmark function,
+// not from other goroutines created during the test. Calling FailNow does not stop
+// those other goroutines.
+func (ddb *B) FailNow() {
+	b := ddb.getBWithError("FailNow", "failed test")
+	integrations.ExitCiVisibility()
+	b.FailNow()
+}
+
+// Error is equivalent to Log followed by Fail.
+func (ddb *B) Error(args ...any) { ddb.getBWithError("Error", fmt.Sprint(args...)).Error(args...) }
+
+// Errorf is equivalent to Logf followed by Fail.
+func (ddb *B) Errorf(format string, args ...any) {
+	ddb.getBWithError("Errorf", fmt.Sprintf(format, args...)).Errorf(format, args...)
+}
+
+// Fatal is equivalent to Log followed by FailNow.
+func (ddb *B) Fatal(args ...any) { ddb.getBWithError("Fatal", fmt.Sprint(args...)).Fatal(args...) }
+
+// Fatalf is equivalent to Logf followed by FailNow.
+func (ddb *B) Fatalf(format string, args ...any) {
+	ddb.getBWithError("Fatalf", fmt.Sprintf(format, args...)).Fatalf(format, args...)
+}
+
+// Skip is equivalent to Log followed by SkipNow.
+func (ddb *B) Skip(args ...any) { ddb.getBWithSkip(fmt.Sprint(args...)).Skip(args...) }
+
+// Skipf is equivalent to Logf followed by SkipNow.
+func (ddb *B) Skipf(format string, args ...any) {
+	ddb.getBWithSkip(fmt.Sprintf(format, args...)).Skipf(format, args...)
+}
+
+// SkipNow marks the test as having been skipped and stops its execution
+// by calling runtime.Goexit. If a test fails (see Error, Errorf, Fail) and is then skipped,
+// it is still considered to have failed. Execution will continue at the next test or benchmark.
+// SkipNow must be called from the goroutine running the test, not from other goroutines created
+// during the test. Calling SkipNow does not stop those other goroutines.
+func (ddb *B) SkipNow() {
+	b := (*testing.B)(ddb)
+	ciTest := getCiVisibilityBenchmark(b)
+	if ciTest != nil {
+		ciTest.Close(integrations.ResultStatusSkip)
+	}
+
+	b.SkipNow()
+}
+
+// StartTimer starts timing a test. This function is called automatically
+// before a benchmark starts, but it can also be used to resume timing after
+// a call to StopTimer.
+func (ddb *B) StartTimer() { (*testing.B)(ddb).StartTimer() }
+
+// StopTimer stops timing a test. This can be used to pause the timer
+// while performing complex initialization that you don't want to measure.
+func (ddb *B) StopTimer() { (*testing.B)(ddb).StopTimer() }
+
+// ReportAllocs enables malloc statistics for this benchmark.
+// It is equivalent to setting -test.benchmem, but it only affects the
+// benchmark function that calls ReportAllocs.
+func (ddb *B) ReportAllocs() { (*testing.B)(ddb).ReportAllocs() }
+
+// ResetTimer zeroes the elapsed benchmark time and memory allocation counters
+// and deletes user-reported metrics. It does not affect whether the timer is running.
+func (ddb *B) ResetTimer() { (*testing.B)(ddb).ResetTimer() }
+
+// Elapsed returns the measured elapsed time of the benchmark.
+// The duration reported by Elapsed matches the one measured by
+// StartTimer, StopTimer, and ResetTimer.
+func (ddb *B) Elapsed() time.Duration {
+	return (*testing.B)(ddb).Elapsed()
+}
+
+// ReportMetric adds "n unit" to the reported benchmark results.
+// If the metric is per-iteration, the caller should divide by b.N,
+// and by convention units should end in "/op".
+// ReportMetric overrides any previously reported value for the same unit.
+// ReportMetric panics if unit is the empty string or if unit contains
+// any whitespace.
+// If unit is a unit normally reported by the benchmark framework itself
+// (such as "allocs/op"), ReportMetric will override that metric.
+// Setting "ns/op" to 0 will suppress that built-in metric.
+func (ddb *B) ReportMetric(n float64, unit string) { (*testing.B)(ddb).ReportMetric(n, unit) }
+
+// RunParallel runs a benchmark in parallel.
+// It creates multiple goroutines and distributes b.N iterations among them.
+// The number of goroutines defaults to GOMAXPROCS. To increase parallelism for
+// non-CPU-bound benchmarks, call SetParallelism before RunParallel.
+// RunParallel is usually used with the go test -cpu flag.
+//
+// The body function will be run in each goroutine. It should set up any
+// goroutine-local state and then iterate until pb.Next returns false.
+// It should not use the StartTimer, StopTimer, or ResetTimer functions,
+// because they have global effect. It should also not call Run.
+//
+// RunParallel reports ns/op values as wall time for the benchmark as a whole,
+// not the sum of wall time or CPU time over each parallel goroutine.
+func (ddb *B) RunParallel(body func(*testing.PB)) { (*testing.B)(ddb).RunParallel(body) }
+
+// SetBytes records the number of bytes processed in a single operation.
+// If this is called, the benchmark will report ns/op and MB/s.
+func (ddb *B) SetBytes(n int64) { (*testing.B)(ddb).SetBytes(n) }
+
+// SetParallelism sets the number of goroutines used by RunParallel to p*GOMAXPROCS.
+// There is usually no need to call SetParallelism for CPU-bound benchmarks.
+// If p is less than 1, this call will have no effect.
+func (ddb *B) SetParallelism(p int) { (*testing.B)(ddb).SetParallelism(p) }
+
+func (ddb *B) getBWithError(errType string, errMessage string) *testing.B {
+	b := (*testing.B)(ddb)
+	ciTest := getCiVisibilityBenchmark(b)
+	if ciTest != nil {
+		ciTest.SetErrorInfo(errType, errMessage, utils.GetStacktrace(2))
+	}
+	return b
+}
+
+func (ddb *B) getBWithSkip(skipReason string) *testing.B {
+	b := (*testing.B)(ddb)
+	ciTest := getCiVisibilityBenchmark(b)
+	if ciTest != nil {
+		ciTest.CloseWithFinishTimeAndSkipReason(integrations.ResultStatusSkip, time.Now(), skipReason)
+	}
+	return b
+}
+
+// getCiVisibilityBenchmark retrieves the CI visibility benchmark associated with a given *testing.B.
+func getCiVisibilityBenchmark(b *testing.B) integrations.DdTest {
+	ciVisibilityBenchmarksMutex.RLock()
+	defer ciVisibilityBenchmarksMutex.RUnlock()
+
+	if v, ok := ciVisibilityBenchmarks[b]; ok {
+		return v
+	}
+
+	return nil
+}
+
+// setCiVisibilityBenchmark associates a CI visibility benchmark with a given *testing.B.
+func setCiVisibilityBenchmark(b *testing.B, ciTest integrations.DdTest) {
+	ciVisibilityBenchmarksMutex.Lock()
+	defer ciVisibilityBenchmarksMutex.Unlock()
+	ciVisibilityBenchmarks[b] = ciTest
+}
diff --git a/main/civisibility/integrations/gotesting/testingT.go b/main/civisibility/integrations/gotesting/testingT.go
new file mode 100644
index 0000000..5b7f117
--- /dev/null
+++ b/main/civisibility/integrations/gotesting/testingT.go
@@ -0,0 +1,217 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package gotesting
+
+import (
+	"context"
+	"fmt"
+	"reflect"
+	"runtime"
+	"sync"
+	"sync/atomic"
+	"testing"
+	"time"
+
+	"ci-visibility-test-github/main/civisibility/integrations"
+	"ci-visibility-test-github/main/civisibility/utils"
+
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
+)
+
+var (
+	// ciVisibilityTests holds a map of *testing.T to civisibility.DdTest for tracking tests.
+	ciVisibilityTests = map[*testing.T]integrations.DdTest{}
+
+	// ciVisibilityTestsMutex is a read-write mutex for synchronizing access to ciVisibilityTests.
+	ciVisibilityTestsMutex sync.RWMutex
+)
+
+// T is a type alias for testing.T to provide additional methods for CI visibility.
+type T testing.T
+
+// GetTest is a helper to return *gotesting.T from *testing.T.
+// Internally, it is just a (*gotesting.T)(t) cast.
+func GetTest(t *testing.T) *T {
+	return (*T)(t)
+}
+
+// Run runs f as a subtest of t called name. It runs f in a separate goroutine
+// and blocks until f returns or calls t.Parallel to become a parallel test.
+// Run reports whether f succeeded (or at least did not fail before calling t.Parallel).
+//
+// Run may be called simultaneously from multiple goroutines, but all such calls
+// must return before the outer test function for t returns.
+func (ddt *T) Run(name string, f func(*testing.T)) bool {
+	// Reflect the function to obtain its pointer.
+	fReflect := reflect.Indirect(reflect.ValueOf(f))
+	moduleName, suiteName := utils.GetModuleAndSuiteName(fReflect.Pointer())
+	originalFunc := runtime.FuncForPC(fReflect.Pointer())
+
+	// Increment the test count in the module.
+	atomic.AddInt32(modulesCounters[moduleName], 1)
+
+	// Increment the test count in the suite.
+	atomic.AddInt32(suitesCounters[suiteName], 1)
+
+	t := (*testing.T)(ddt)
+	return t.Run(name, func(t *testing.T) {
+		// Create or retrieve the module, suite, and test for CI visibility.
+		module := session.GetOrCreateModuleWithFramework(moduleName, testFramework, runtime.Version())
+		suite := module.GetOrCreateSuite(suiteName)
+		test := suite.CreateTest(t.Name())
+		test.SetTestFunc(originalFunc)
+		setCiVisibilityTest(t, test)
+		defer func() {
+			if r := recover(); r != nil {
+				// Handle panic and set error information.
+				test.SetErrorInfo("panic", fmt.Sprint(r), utils.GetStacktrace(1))
+				test.Close(integrations.ResultStatusFail)
+				checkModuleAndSuite(module, suite)
+				integrations.ExitCiVisibility()
+				panic(r)
+			} else {
+				// Normal finalization: determine the test result based on its state.
+				if t.Failed() {
+					test.SetTag(ext.Error, true)
+					suite.SetTag(ext.Error, true)
+					module.SetTag(ext.Error, true)
+					test.Close(integrations.ResultStatusFail)
+				} else if t.Skipped() {
+					test.Close(integrations.ResultStatusSkip)
+				} else {
+					test.Close(integrations.ResultStatusPass)
+				}
+				checkModuleAndSuite(module, suite)
+			}
+		}()
+
+		// Execute the original test function.
+		f(t)
+	})
+}
+
+// Context returns the CI Visibility context of the Test span.
+// This may be used to create test's children spans useful for
+// integration tests.
+func (ddt *T) Context() context.Context {
+	t := (*testing.T)(ddt)
+	ciTest := getCiVisibilityTest(t)
+	if ciTest != nil {
+		return ciTest.Context()
+	}
+
+	return context.Background()
+}
+
+// Fail marks the function as having failed but continues execution.
+func (ddt *T) Fail() { ddt.getTWithError("Fail", "failed test").Fail() }
+
+// FailNow marks the function as having failed and stops its execution
+// by calling runtime.Goexit (which then runs all deferred calls in the
+// current goroutine). Execution will continue at the next test or benchmark.
+// FailNow must be called from the goroutine running the test or benchmark function,
+// not from other goroutines created during the test. Calling FailNow does not stop
+// those other goroutines.
+func (ddt *T) FailNow() {
+	t := ddt.getTWithError("FailNow", "failed test")
+	integrations.ExitCiVisibility()
+	t.FailNow()
+}
+
+// Error is equivalent to Log followed by Fail.
+func (ddt *T) Error(args ...any) { ddt.getTWithError("Error", fmt.Sprint(args...)).Error(args...) }
+
+// Errorf is equivalent to Logf followed by Fail.
+func (ddt *T) Errorf(format string, args ...any) {
+	ddt.getTWithError("Errorf", fmt.Sprintf(format, args...)).Errorf(format, args...)
+}
+
+// Fatal is equivalent to Log followed by FailNow.
+func (ddt *T) Fatal(args ...any) { ddt.getTWithError("Fatal", fmt.Sprint(args...)).Fatal(args...) }
+
+// Fatalf is equivalent to Logf followed by FailNow.
+func (ddt *T) Fatalf(format string, args ...any) {
+	ddt.getTWithError("Fatalf", fmt.Sprintf(format, args...)).Fatalf(format, args...)
+}
+
+// Skip is equivalent to Log followed by SkipNow.
+func (ddt *T) Skip(args ...any) { ddt.getTWithSkip(fmt.Sprint(args...)).Skip(args...) }
+
+// Skipf is equivalent to Logf followed by SkipNow.
+func (ddt *T) Skipf(format string, args ...any) {
+	ddt.getTWithSkip(fmt.Sprintf(format, args...)).Skipf(format, args...)
+}
+
+// SkipNow marks the test as having been skipped and stops its execution
+// by calling runtime.Goexit. If a test fails (see Error, Errorf, Fail) and is then skipped,
+// it is still considered to have failed. Execution will continue at the next test or benchmark.
+// SkipNow must be called from the goroutine running the test, not from other goroutines created
+// during the test. Calling SkipNow does not stop those other goroutines.
+func (ddt *T) SkipNow() {
+	t := (*testing.T)(ddt)
+	ciTest := getCiVisibilityTest(t)
+	if ciTest != nil {
+		ciTest.Close(integrations.ResultStatusSkip)
+	}
+
+	t.SkipNow()
+}
+
+// Parallel signals that this test is to be run in parallel with (and only with)
+// other parallel tests. When a test is run multiple times due to use of
+// -test.count or -test.cpu, multiple instances of a single test never run in
+// parallel with each other.
+func (ddt *T) Parallel() { (*testing.T)(ddt).Parallel() }
+
+// Deadline reports the time at which the test binary will have
+// exceeded the timeout specified by the -timeout flag.
+// The ok result is false if the -timeout flag indicates “no timeout” (0).
+func (ddt *T) Deadline() (deadline time.Time, ok bool) {
+	return (*testing.T)(ddt).Deadline()
+}
+
+// Setenv calls os.Setenv(key, value) and uses Cleanup to
+// restore the environment variable to its original value
+// after the test. Because Setenv affects the whole process,
+// it cannot be used in parallel tests or tests with parallel ancestors.
+func (ddt *T) Setenv(key, value string) { (*testing.T)(ddt).Setenv(key, value) }
+
+func (ddt *T) getTWithError(errType string, errMessage string) *testing.T {
+	t := (*testing.T)(ddt)
+	ciTest := getCiVisibilityTest(t)
+	if ciTest != nil {
+		ciTest.SetErrorInfo(errType, errMessage, utils.GetStacktrace(2))
+	}
+	return t
+}
+
+func (ddt *T) getTWithSkip(skipReason string) *testing.T {
+	t := (*testing.T)(ddt)
+	ciTest := getCiVisibilityTest(t)
+	if ciTest != nil {
+		ciTest.CloseWithFinishTimeAndSkipReason(integrations.ResultStatusSkip, time.Now(), skipReason)
+	}
+	return t
+}
+
+// getCiVisibilityTest retrieves the CI visibility test associated with a given *testing.T.
+func getCiVisibilityTest(t *testing.T) integrations.DdTest {
+	ciVisibilityTestsMutex.RLock()
+	defer ciVisibilityTestsMutex.RUnlock()
+
+	if v, ok := ciVisibilityTests[t]; ok {
+		return v
+	}
+
+	return nil
+}
+
+// setCiVisibilityTest associates a CI visibility test with a given *testing.T.
+func setCiVisibilityTest(t *testing.T, ciTest integrations.DdTest) {
+	ciVisibilityTestsMutex.Lock()
+	defer ciVisibilityTestsMutex.Unlock()
+	ciVisibilityTests[t] = ciTest
+}
diff --git a/main/civisibility/integrations/gotesting/testing_test.go b/main/civisibility/integrations/gotesting/testing_test.go
new file mode 100644
index 0000000..bf3b48e
--- /dev/null
+++ b/main/civisibility/integrations/gotesting/testing_test.go
@@ -0,0 +1,354 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package gotesting
+
+import (
+	"fmt"
+	"net/http"
+	"net/http/httptest"
+	"os"
+	"strconv"
+	"testing"
+
+	"ci-visibility-test-github/main/civisibility/constants"
+	"ci-visibility-test-github/main/civisibility/integrations"
+
+	ddhttp "gopkg.in/DataDog/dd-trace-go.v1/contrib/net/http"
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer"
+	ddtracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
+
+	"github.com/stretchr/testify/assert"
+)
+
+var currentM *testing.M
+var mTracer mocktracer.Tracer
+
+// TestMain is the entry point for testing and runs before any test.
+func TestMain(m *testing.M) {
+	currentM = m
+	mTracer = integrations.InitializeCIVisibilityMock()
+
+	// (*M)(m).Run() cast m to gotesting.M and just run
+	// or use a helper method gotesting.RunM(m)
+
+	// os.Exit((*M)(m).Run())
+	_ = RunM(m)
+
+	finishedSpans := mTracer.FinishedSpans()
+	// 1 session span
+	// 1 module span
+	// 1 suite span (optional 1 from reflections_test.go)
+	// 6 tests spans
+	// 7 sub stest spans
+	// 2 normal spans (from integration tests)
+	// 1 benchmark span (optional - require the -bench option)
+	if len(finishedSpans) < 17 {
+		panic("expected at least 17 finished spans, got " + strconv.Itoa(len(finishedSpans)))
+	}
+
+	sessionSpans := getSpansWithType(finishedSpans, constants.SpanTypeTestSession)
+	if len(sessionSpans) != 1 {
+		panic("expected exactly 1 session span, got " + strconv.Itoa(len(sessionSpans)))
+	}
+
+	moduleSpans := getSpansWithType(finishedSpans, constants.SpanTypeTestModule)
+	if len(moduleSpans) != 1 {
+		panic("expected exactly 1 module span, got " + strconv.Itoa(len(moduleSpans)))
+	}
+
+	suiteSpans := getSpansWithType(finishedSpans, constants.SpanTypeTestSuite)
+	if len(suiteSpans) < 1 {
+		panic("expected at least 1 suite span, got " + strconv.Itoa(len(suiteSpans)))
+	}
+
+	testSpans := getSpansWithType(finishedSpans, constants.SpanTypeTest)
+	if len(testSpans) < 12 {
+		panic("expected at least 12 suite span, got " + strconv.Itoa(len(testSpans)))
+	}
+
+	httpSpans := getSpansWithType(finishedSpans, ext.SpanTypeHTTP)
+	if len(httpSpans) != 2 {
+		panic("expected exactly 2 normal spans, got " + strconv.Itoa(len(httpSpans)))
+	}
+
+	os.Exit(0)
+}
+
+// TestMyTest01 demonstrates instrumentation of InternalTests
+func TestMyTest01(t *testing.T) {
+	assertTest(t)
+}
+
+// TestMyTest02 demonstrates instrumentation of subtests.
+func TestMyTest02(gt *testing.T) {
+	assertTest(gt)
+
+	// To instrument subTests we just need to cast
+	// testing.T to our gotesting.T
+	// using: newT := (*gotesting.T)(t)
+	// Then all testing.T will be available but instrumented
+	t := (*T)(gt)
+	// or
+	t = GetTest(gt)
+
+	t.Run("sub01", func(oT2 *testing.T) {
+		t2 := (*T)(oT2) // Cast the sub-test to gotesting.T
+		t2.Log("From sub01")
+		t2.Run("sub03", func(t3 *testing.T) {
+			t3.Log("From sub03")
+		})
+	})
+}
+
+func Test_Foo(gt *testing.T) {
+	assertTest(gt)
+	t := (*T)(gt)
+	var tests = []struct {
+		name  string
+		input string
+		want  string
+	}{
+		{"yellow should return color", "yellow", "color"},
+		{"banana should return fruit", "banana", "fruit"},
+		{"duck should return animal", "duck", "animal"},
+	}
+	for _, test := range tests {
+		t.Run(test.name, func(t *testing.T) {
+			t.Log(test.name)
+		})
+	}
+}
+
+// TestWithExternalCalls demonstrates testing with external HTTP calls.
+func TestWithExternalCalls(gt *testing.T) {
+	assertTest(gt)
+	t := (*T)(gt)
+
+	// Create a new HTTP test server
+	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+		_, _ = w.Write([]byte("Hello World"))
+	}))
+	defer s.Close()
+
+	t.Run("default", func(t *testing.T) {
+
+		// if we want to use the test span as a parent of a child span
+		// we can extract the SpanContext and use it in other integrations
+		ctx := (*T)(t).Context()
+
+		// Wrap the default HTTP transport for tracing
+		rt := ddhttp.WrapRoundTripper(http.DefaultTransport)
+		client := &http.Client{
+			Transport: rt,
+		}
+
+		// Create a new HTTP request
+		req, err := http.NewRequest("GET", s.URL+"/hello/world", nil)
+		if err != nil {
+			t.FailNow()
+		}
+
+		// Use the span context here so the http span will appear as a child of the test
+		req = req.WithContext(ctx)
+
+		res, err := client.Do(req)
+		if err != nil {
+			t.FailNow()
+		}
+		_ = res.Body.Close()
+	})
+
+	t.Run("custom-name", func(t *testing.T) {
+
+		// we can also add custom tags to the test span by retrieving the
+		// context and call the `ddtracer.SpanFromContext` api
+		ctx := (*T)(t).Context()
+		span, _ := ddtracer.SpanFromContext(ctx)
+
+		// Custom namer function for the HTTP request
+		customNamer := func(req *http.Request) string {
+			value := fmt.Sprintf("%s %s", req.Method, req.URL.Path)
+
+			// Then we can set custom tags to that test span
+			span.SetTag("customNamer.Value", value)
+			return value
+		}
+
+		rt := ddhttp.WrapRoundTripper(http.DefaultTransport, ddhttp.RTWithResourceNamer(customNamer))
+		client := &http.Client{
+			Transport: rt,
+		}
+
+		req, err := http.NewRequest("GET", s.URL+"/hello/world", nil)
+		if err != nil {
+			t.FailNow()
+		}
+
+		// Use the span context here so the http span will appear as a child of the test
+		req = req.WithContext(ctx)
+
+		res, err := client.Do(req)
+		if err != nil {
+			t.FailNow()
+		}
+		_ = res.Body.Close()
+	})
+}
+
+// TestSkip demonstrates skipping a test with a message.
+func TestSkip(gt *testing.T) {
+	assertTest(gt)
+
+	t := (*T)(gt)
+
+	// because we use the instrumented Skip
+	// the message will be reported as the skip reason.
+	t.Skip("Nothing to do here, skipping!")
+}
+
+// BenchmarkFirst demonstrates benchmark instrumentation with sub-benchmarks.
+func BenchmarkFirst(gb *testing.B) {
+
+	// Same happens with sub benchmarks
+	// we just need to cast testing.B to gotesting.B
+	// using: newB := (*gotesting.B)(b)
+	b := (*B)(gb)
+	// or
+	b = GetBenchmark(gb)
+
+	var mapArray []map[string]string
+	b.Run("child01", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+			mapArray = append(mapArray, map[string]string{})
+		}
+	})
+
+	b.Run("child02", func(b *testing.B) {
+		for i := 0; i < b.N; i++ {
+			mapArray = append(mapArray, map[string]string{})
+		}
+	})
+
+	b.Run("child03", func(b *testing.B) {
+		GetBenchmark(b).Skip("The reason...")
+	})
+
+	_ = gb.Elapsed()
+}
+
+func assertTest(t *testing.T) {
+	assert := assert.New(t)
+	spans := mTracer.OpenSpans()
+	hasSession := false
+	hasModule := false
+	hasSuite := false
+	hasTest := false
+	for _, span := range spans {
+		spanTags := span.Tags()
+
+		// Assert Session
+		if span.Tag(ext.SpanType) == constants.SpanTypeTestSession {
+			assert.Contains(spanTags, constants.TestSessionIDTag)
+			assertCommon(assert, span)
+			hasSession = true
+		}
+
+		// Assert Module
+		if span.Tag(ext.SpanType) == constants.SpanTypeTestModule {
+			assert.Subset(spanTags, map[string]interface{}{
+				constants.TestModule:    "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting",
+				constants.TestFramework: "golang.org/pkg/testing",
+			})
+			assert.Contains(spanTags, constants.TestSessionIDTag)
+			assert.Contains(spanTags, constants.TestModuleIDTag)
+			assert.Contains(spanTags, constants.TestFrameworkVersion)
+			assertCommon(assert, span)
+			hasModule = true
+		}
+
+		// Assert Suite
+		if span.Tag(ext.SpanType) == constants.SpanTypeTestSuite {
+			assert.Subset(spanTags, map[string]interface{}{
+				constants.TestModule:    "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting",
+				constants.TestFramework: "golang.org/pkg/testing",
+			})
+			assert.Contains(spanTags, constants.TestSessionIDTag)
+			assert.Contains(spanTags, constants.TestModuleIDTag)
+			assert.Contains(spanTags, constants.TestSuiteIDTag)
+			assert.Contains(spanTags, constants.TestFrameworkVersion)
+			assert.Contains(spanTags, constants.TestSuite)
+			assertCommon(assert, span)
+			hasSuite = true
+		}
+
+		// Assert Test
+		if span.Tag(ext.SpanType) == constants.SpanTypeTest {
+			assert.Subset(spanTags, map[string]interface{}{
+				constants.TestModule:    "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations/gotesting",
+				constants.TestFramework: "golang.org/pkg/testing",
+				constants.TestSuite:     "testing_test.go",
+				constants.TestName:      t.Name(),
+				constants.TestType:      constants.TestTypeTest,
+			})
+			assert.Contains(spanTags, constants.TestSessionIDTag)
+			assert.Contains(spanTags, constants.TestModuleIDTag)
+			assert.Contains(spanTags, constants.TestSuiteIDTag)
+			assert.Contains(spanTags, constants.TestFrameworkVersion)
+			assert.Contains(spanTags, constants.TestCodeOwners)
+			assert.Contains(spanTags, constants.TestSourceFile)
+			assert.Contains(spanTags, constants.TestSourceStartLine)
+			assertCommon(assert, span)
+			hasTest = true
+		}
+	}
+
+	assert.True(hasSession)
+	assert.True(hasModule)
+	assert.True(hasSuite)
+	assert.True(hasTest)
+}
+
+func assertCommon(assert *assert.Assertions, span mocktracer.Span) {
+	spanTags := span.Tags()
+
+	assert.Subset(spanTags, map[string]interface{}{
+		constants.Origin:   constants.CIAppTestOrigin,
+		constants.TestType: constants.TestTypeTest,
+	})
+
+	assert.Contains(spanTags, ext.ResourceName)
+	assert.Contains(spanTags, constants.TestCommand)
+	assert.Contains(spanTags, constants.TestCommandWorkingDirectory)
+	assert.Contains(spanTags, constants.OSPlatform)
+	assert.Contains(spanTags, constants.OSArchitecture)
+	assert.Contains(spanTags, constants.OSVersion)
+	assert.Contains(spanTags, constants.RuntimeVersion)
+	assert.Contains(spanTags, constants.RuntimeName)
+	assert.Contains(spanTags, constants.GitRepositoryURL)
+	assert.Contains(spanTags, constants.GitCommitSHA)
+	// GitHub CI does not provide commit details
+	if spanTags[constants.CIProviderName] != "github" {
+		assert.Contains(spanTags, constants.GitCommitMessage)
+		assert.Contains(spanTags, constants.GitCommitAuthorEmail)
+		assert.Contains(spanTags, constants.GitCommitAuthorDate)
+		assert.Contains(spanTags, constants.GitCommitCommitterEmail)
+		assert.Contains(spanTags, constants.GitCommitCommitterDate)
+		assert.Contains(spanTags, constants.GitCommitCommitterName)
+	}
+	assert.Contains(spanTags, constants.CIWorkspacePath)
+}
+
+func getSpansWithType(spans []mocktracer.Span, spanType string) []mocktracer.Span {
+	var result []mocktracer.Span
+	for _, span := range spans {
+		if span.Tag(ext.SpanType) == spanType {
+			result = append(result, span)
+		}
+	}
+
+	return result
+}
diff --git a/main/civisibility/integrations/manual_api.go b/main/civisibility/integrations/manual_api.go
new file mode 100644
index 0000000..168ec86
--- /dev/null
+++ b/main/civisibility/integrations/manual_api.go
@@ -0,0 +1,219 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package integrations
+
+import (
+	"context"
+	"runtime"
+	"sync"
+	"time"
+
+	"ci-visibility-test-github/main/civisibility/constants"
+	"ci-visibility-test-github/main/civisibility/utils"
+
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
+)
+
+// TestResultStatus represents the result status of a test.
+type TestResultStatus int
+
+const (
+	// ResultStatusPass indicates that the test has passed.
+	ResultStatusPass TestResultStatus = 0
+
+	// ResultStatusFail indicates that the test has failed.
+	ResultStatusFail TestResultStatus = 1
+
+	// ResultStatusSkip indicates that the test has been skipped.
+	ResultStatusSkip TestResultStatus = 2
+)
+
+// ddTslvEvent is an interface that provides common methods for CI visibility events.
+type ddTslvEvent interface {
+	// Context returns the context of the event.
+	Context() context.Context
+
+	// StartTime returns the start time of the event.
+	StartTime() time.Time
+
+	// SetError sets an error on the event.
+	SetError(err error)
+
+	// SetErrorInfo sets detailed error information on the event.
+	SetErrorInfo(errType string, message string, callstack string)
+
+	// SetTag sets a tag on the event.
+	SetTag(key string, value interface{})
+}
+
+// DdTestSession represents a session for a set of tests.
+type DdTestSession interface {
+	ddTslvEvent
+
+	// Command returns the command used to run the session.
+	Command() string
+
+	// Framework returns the testing framework used.
+	Framework() string
+
+	// WorkingDirectory returns the working directory of the session.
+	WorkingDirectory() string
+
+	// Close closes the test session with the given exit code.
+	Close(exitCode int)
+
+	// CloseWithFinishTime closes the test session with the given exit code and finish time.
+	CloseWithFinishTime(exitCode int, finishTime time.Time)
+
+	// GetOrCreateModule returns an existing module or creates a new one with the given name.
+	GetOrCreateModule(name string) DdTestModule
+
+	// GetOrCreateModuleWithFramework returns an existing module or creates a new one with the given name, framework, and framework version.
+	GetOrCreateModuleWithFramework(name string, framework string, frameworkVersion string) DdTestModule
+
+	// GetOrCreateModuleWithFrameworkAndStartTime returns an existing module or creates a new one with the given name, framework, framework version, and start time.
+	GetOrCreateModuleWithFrameworkAndStartTime(name string, framework string, frameworkVersion string, startTime time.Time) DdTestModule
+}
+
+// DdTestModule represents a module within a test session.
+type DdTestModule interface {
+	ddTslvEvent
+
+	// Session returns the test session to which the module belongs.
+	Session() DdTestSession
+
+	// Framework returns the testing framework used by the module.
+	Framework() string
+
+	// Name returns the name of the module.
+	Name() string
+
+	// Close closes the test module.
+	Close()
+
+	// CloseWithFinishTime closes the test module with the given finish time.
+	CloseWithFinishTime(finishTime time.Time)
+
+	// GetOrCreateSuite returns an existing suite or creates a new one with the given name.
+	GetOrCreateSuite(name string) DdTestSuite
+
+	// GetOrCreateSuiteWithStartTime returns an existing suite or creates a new one with the given name and start time.
+	GetOrCreateSuiteWithStartTime(name string, startTime time.Time) DdTestSuite
+}
+
+// DdTestSuite represents a suite of tests within a module.
+type DdTestSuite interface {
+	ddTslvEvent
+
+	// Module returns the module to which the suite belongs.
+	Module() DdTestModule
+
+	// Name returns the name of the suite.
+	Name() string
+
+	// Close closes the test suite.
+	Close()
+
+	// CloseWithFinishTime closes the test suite with the given finish time.
+	CloseWithFinishTime(finishTime time.Time)
+
+	// CreateTest creates a new test with the given name.
+	CreateTest(name string) DdTest
+
+	// CreateTestWithStartTime creates a new test with the given name and start time.
+	CreateTestWithStartTime(name string, startTime time.Time) DdTest
+}
+
+// DdTest represents an individual test within a suite.
+type DdTest interface {
+	ddTslvEvent
+
+	// Name returns the name of the test.
+	Name() string
+
+	// Suite returns the suite to which the test belongs.
+	Suite() DdTestSuite
+
+	// Close closes the test with the given status.
+	Close(status TestResultStatus)
+
+	// CloseWithFinishTime closes the test with the given status and finish time.
+	CloseWithFinishTime(status TestResultStatus, finishTime time.Time)
+
+	// CloseWithFinishTimeAndSkipReason closes the test with the given status, finish time, and skip reason.
+	CloseWithFinishTimeAndSkipReason(status TestResultStatus, finishTime time.Time, skipReason string)
+
+	// SetTestFunc sets the function to be tested. (Sets the test.source tags and test.codeowners)
+	SetTestFunc(fn *runtime.Func)
+
+	// SetBenchmarkData sets benchmark data for the test.
+	SetBenchmarkData(measureType string, data map[string]any)
+}
+
+// common
+var _ ddTslvEvent = (*ciVisibilityCommon)(nil)
+
+// ciVisibilityCommon is a struct that implements the ddTslvEvent interface and provides common functionality for CI visibility.
+type ciVisibilityCommon struct {
+	startTime time.Time
+
+	tags   []tracer.StartSpanOption
+	span   tracer.Span
+	ctx    context.Context
+	mutex  sync.Mutex
+	closed bool
+}
+
+// Context returns the context of the event.
+func (c *ciVisibilityCommon) Context() context.Context { return c.ctx }
+
+// StartTime returns the start time of the event.
+func (c *ciVisibilityCommon) StartTime() time.Time { return c.startTime }
+
+// SetError sets an error on the event.
+func (c *ciVisibilityCommon) SetError(err error) {
+	c.span.SetTag(ext.Error, err)
+}
+
+// SetErrorInfo sets detailed error information on the event.
+func (c *ciVisibilityCommon) SetErrorInfo(errType string, message string, callstack string) {
+	// set the span with error:1
+	c.span.SetTag(ext.Error, true)
+
+	// set the error type
+	if errType != "" {
+		c.span.SetTag(ext.ErrorType, errType)
+	}
+
+	// set the error message
+	if message != "" {
+		c.span.SetTag(ext.ErrorMsg, message)
+	}
+
+	// set the error stacktrace
+	if callstack != "" {
+		c.span.SetTag(ext.ErrorStack, callstack)
+	}
+}
+
+// SetTag sets a tag on the event.
+func (c *ciVisibilityCommon) SetTag(key string, value interface{}) { c.span.SetTag(key, value) }
+
+// fillCommonTags adds common tags to the span options for CI visibility.
+func fillCommonTags(opts []tracer.StartSpanOption) []tracer.StartSpanOption {
+	opts = append(opts, []tracer.StartSpanOption{
+		tracer.Tag(constants.Origin, constants.CIAppTestOrigin),
+		tracer.Tag(ext.ManualKeep, true),
+	}...)
+
+	// Apply CI tags
+	for k, v := range utils.GetCITags() {
+		opts = append(opts, tracer.Tag(k, v))
+	}
+
+	return opts
+}
diff --git a/main/civisibility/integrations/manual_api_ddtest.go b/main/civisibility/integrations/manual_api_ddtest.go
new file mode 100644
index 0000000..7104e70
--- /dev/null
+++ b/main/civisibility/integrations/manual_api_ddtest.go
@@ -0,0 +1,158 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package integrations
+
+import (
+	"context"
+	"fmt"
+	"runtime"
+	"strings"
+	"time"
+
+	"ci-visibility-test-github/main/civisibility/constants"
+	"ci-visibility-test-github/main/civisibility/utils"
+
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
+)
+
+// Test
+
+// Ensures that tslvTest implements the DdTest interface.
+var _ DdTest = (*tslvTest)(nil)
+
+// tslvTest implements the DdTest interface and represents an individual test within a suite.
+type tslvTest struct {
+	ciVisibilityCommon
+	suite *tslvTestSuite
+	name  string
+}
+
+// createTest initializes a new test within a given suite.
+func createTest(suite *tslvTestSuite, name string, startTime time.Time) DdTest {
+	if suite == nil {
+		return nil
+	}
+
+	operationName := "test"
+	if suite.module.framework != "" {
+		operationName = fmt.Sprintf("%s.%s", strings.ToLower(suite.module.framework), operationName)
+	}
+
+	resourceName := fmt.Sprintf("%s.%s", suite.name, name)
+
+	// Test tags should include suite, module, and session tags so the backend can calculate the suite, module, and session fingerprint from the test.
+	testTags := append(suite.tags, tracer.Tag(constants.TestName, name))
+	testOpts := append(fillCommonTags([]tracer.StartSpanOption{
+		tracer.ResourceName(resourceName),
+		tracer.SpanType(constants.SpanTypeTest),
+		tracer.StartTime(startTime),
+	}), testTags...)
+
+	span, ctx := tracer.StartSpanFromContext(context.Background(), operationName, testOpts...)
+	if suite.module.session != nil {
+		span.SetTag(constants.TestSessionIDTag, fmt.Sprint(suite.module.session.sessionID))
+	}
+	span.SetTag(constants.TestModuleIDTag, fmt.Sprint(suite.module.moduleID))
+	span.SetTag(constants.TestSuiteIDTag, fmt.Sprint(suite.suiteID))
+
+	t := &tslvTest{
+		suite: suite,
+		name:  name,
+		ciVisibilityCommon: ciVisibilityCommon{
+			startTime: startTime,
+			tags:      testTags,
+			span:      span,
+			ctx:       ctx,
+		},
+	}
+
+	// Ensure to close everything before CI visibility exits. In CI visibility mode, we try to never lose data.
+	PushCiVisibilityCloseAction(func() { t.Close(ResultStatusFail) })
+
+	return t
+}
+
+// Name returns the name of the test.
+func (t *tslvTest) Name() string { return t.name }
+
+// Suite returns the suite to which the test belongs.
+func (t *tslvTest) Suite() DdTestSuite { return t.suite }
+
+// Close closes the test with the given status and sets the finish time to the current time.
+func (t *tslvTest) Close(status TestResultStatus) { t.CloseWithFinishTime(status, time.Now()) }
+
+// CloseWithFinishTime closes the test with the given status and finish time.
+func (t *tslvTest) CloseWithFinishTime(status TestResultStatus, finishTime time.Time) {
+	t.CloseWithFinishTimeAndSkipReason(status, finishTime, "")
+}
+
+// CloseWithFinishTimeAndSkipReason closes the test with the given status, finish time, and skip reason.
+func (t *tslvTest) CloseWithFinishTimeAndSkipReason(status TestResultStatus, finishTime time.Time, skipReason string) {
+	t.mutex.Lock()
+	defer t.mutex.Unlock()
+	if t.closed {
+		return
+	}
+
+	switch status {
+	case ResultStatusPass:
+		t.span.SetTag(constants.TestStatus, constants.TestStatusPass)
+	case ResultStatusFail:
+		t.span.SetTag(constants.TestStatus, constants.TestStatusFail)
+	case ResultStatusSkip:
+		t.span.SetTag(constants.TestStatus, constants.TestStatusSkip)
+	}
+
+	if skipReason != "" {
+		t.span.SetTag(constants.TestSkipReason, skipReason)
+	}
+
+	t.span.Finish(tracer.FinishTime(finishTime))
+	t.closed = true
+}
+
+// SetError sets an error on the test and marks the suite and module as having an error.
+func (t *tslvTest) SetError(err error) {
+	t.ciVisibilityCommon.SetError(err)
+	t.Suite().SetTag(ext.Error, true)
+	t.Suite().Module().SetTag(ext.Error, true)
+}
+
+// SetErrorInfo sets detailed error information on the test and marks the suite and module as having an error.
+func (t *tslvTest) SetErrorInfo(errType string, message string, callstack string) {
+	t.ciVisibilityCommon.SetErrorInfo(errType, message, callstack)
+	t.Suite().SetTag(ext.Error, true)
+	t.Suite().Module().SetTag(ext.Error, true)
+}
+
+// SetTestFunc sets the function to be tested and records its source location.
+func (t *tslvTest) SetTestFunc(fn *runtime.Func) {
+	if fn == nil {
+		return
+	}
+
+	file, line := fn.FileLine(fn.Entry())
+	file = utils.GetRelativePathFromCITagsSourceRoot(file)
+	t.SetTag(constants.TestSourceFile, file)
+	t.SetTag(constants.TestSourceStartLine, line)
+
+	codeOwners := utils.GetCodeOwners()
+	if codeOwners != nil {
+		match, found := codeOwners.Match("/" + file)
+		if found {
+			t.SetTag(constants.TestCodeOwners, match.GetOwnersString())
+		}
+	}
+}
+
+// SetBenchmarkData sets benchmark data for the test.
+func (t *tslvTest) SetBenchmarkData(measureType string, data map[string]any) {
+	t.span.SetTag(constants.TestType, constants.TestTypeBenchmark)
+	for k, v := range data {
+		t.span.SetTag(fmt.Sprintf("benchmark.%s.%s", measureType, k), v)
+	}
+}
diff --git a/main/civisibility/integrations/manual_api_ddtestmodule.go b/main/civisibility/integrations/manual_api_ddtestmodule.go
new file mode 100644
index 0000000..c432358
--- /dev/null
+++ b/main/civisibility/integrations/manual_api_ddtestmodule.go
@@ -0,0 +1,141 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package integrations
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"ci-visibility-test-github/main/civisibility/constants"
+
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
+)
+
+// Test Module
+
+// Ensures that tslvTestModule implements the DdTestModule interface.
+var _ DdTestModule = (*tslvTestModule)(nil)
+
+// tslvTestModule implements the DdTestModule interface and represents a module within a test session.
+type tslvTestModule struct {
+	ciVisibilityCommon
+	session   *tslvTestSession
+	moduleID  uint64
+	name      string
+	framework string
+
+	suites map[string]DdTestSuite
+}
+
+// createTestModule initializes a new test module within a given session.
+func createTestModule(session *tslvTestSession, name string, framework string, frameworkVersion string, startTime time.Time) DdTestModule {
+	// Ensure CI visibility is properly configured.
+	EnsureCiVisibilityInitialization()
+
+	operationName := "test_module"
+	if framework != "" {
+		operationName = fmt.Sprintf("%s.%s", strings.ToLower(framework), operationName)
+	}
+
+	resourceName := name
+
+	var sessionTags []tracer.StartSpanOption
+	if session != nil {
+		sessionTags = session.tags
+	}
+
+	// Module tags should include session tags so the backend can calculate the session fingerprint from the module.
+	moduleTags := append(sessionTags, []tracer.StartSpanOption{
+		tracer.Tag(constants.TestType, constants.TestTypeTest),
+		tracer.Tag(constants.TestModule, name),
+		tracer.Tag(constants.TestFramework, framework),
+		tracer.Tag(constants.TestFrameworkVersion, frameworkVersion),
+	}...)
+
+	testOpts := append(fillCommonTags([]tracer.StartSpanOption{
+		tracer.ResourceName(resourceName),
+		tracer.SpanType(constants.SpanTypeTestModule),
+		tracer.StartTime(startTime),
+	}), moduleTags...)
+
+	span, ctx := tracer.StartSpanFromContext(context.Background(), operationName, testOpts...)
+	moduleID := span.Context().SpanID()
+	if session != nil {
+		span.SetTag(constants.TestSessionIDTag, fmt.Sprint(session.sessionID))
+	}
+	span.SetTag(constants.TestModuleIDTag, fmt.Sprint(moduleID))
+
+	module := &tslvTestModule{
+		session:   session,
+		moduleID:  moduleID,
+		name:      name,
+		framework: framework,
+		suites:    map[string]DdTestSuite{},
+		ciVisibilityCommon: ciVisibilityCommon{
+			startTime: startTime,
+			tags:      moduleTags,
+			span:      span,
+			ctx:       ctx,
+		},
+	}
+
+	// Ensure to close everything before CI visibility exits. In CI visibility mode, we try to never lose data.
+	PushCiVisibilityCloseAction(func() { module.Close() })
+
+	return module
+}
+
+// Name returns the name of the test module.
+func (t *tslvTestModule) Name() string { return t.name }
+
+// Framework returns the testing framework used by the test module.
+func (t *tslvTestModule) Framework() string { return t.framework }
+
+// Session returns the test session to which the test module belongs.
+func (t *tslvTestModule) Session() DdTestSession { return t.session }
+
+// Close closes the test module and sets the finish time to the current time.
+func (t *tslvTestModule) Close() { t.CloseWithFinishTime(time.Now()) }
+
+// CloseWithFinishTime closes the test module with the given finish time.
+func (t *tslvTestModule) CloseWithFinishTime(finishTime time.Time) {
+	t.mutex.Lock()
+	defer t.mutex.Unlock()
+	if t.closed {
+		return
+	}
+
+	for _, suite := range t.suites {
+		suite.Close()
+	}
+	t.suites = map[string]DdTestSuite{}
+
+	t.span.Finish(tracer.FinishTime(finishTime))
+	t.closed = true
+}
+
+// GetOrCreateSuite returns an existing suite or creates a new one with the given name.
+func (t *tslvTestModule) GetOrCreateSuite(name string) DdTestSuite {
+	return t.GetOrCreateSuiteWithStartTime(name, time.Now())
+}
+
+// GetOrCreateSuiteWithStartTime returns an existing suite or creates a new one with the given name and start time.
+func (t *tslvTestModule) GetOrCreateSuiteWithStartTime(name string, startTime time.Time) DdTestSuite {
+	t.mutex.Lock()
+	defer t.mutex.Unlock()
+
+	var suite DdTestSuite
+	if v, ok := t.suites[name]; ok {
+		suite = v
+	} else {
+		suite = createTestSuite(t, name, startTime)
+		t.suites[name] = suite
+	}
+
+	return suite
+}
diff --git a/main/civisibility/integrations/manual_api_ddtestsession.go b/main/civisibility/integrations/manual_api_ddtestsession.go
new file mode 100644
index 0000000..93cf8a8
--- /dev/null
+++ b/main/civisibility/integrations/manual_api_ddtestsession.go
@@ -0,0 +1,171 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package integrations
+
+import (
+	"context"
+	"fmt"
+	"os"
+	"path/filepath"
+	"regexp"
+	"strings"
+	"time"
+
+	"ci-visibility-test-github/main/civisibility/constants"
+	"ci-visibility-test-github/main/civisibility/utils"
+
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
+)
+
+// Test Session
+
+// Ensures that tslvTestSession implements the DdTestSession interface.
+var _ DdTestSession = (*tslvTestSession)(nil)
+
+// tslvTestSession implements the DdTestSession interface and represents a session for a set of tests.
+type tslvTestSession struct {
+	ciVisibilityCommon
+	sessionID        uint64
+	command          string
+	workingDirectory string
+	framework        string
+
+	modules map[string]DdTestModule
+}
+
+// CreateTestSession initializes a new test session. It automatically determines the command and working directory.
+func CreateTestSession() DdTestSession {
+	var cmd string
+	if len(os.Args) == 1 {
+		cmd = filepath.Base(os.Args[0])
+	} else {
+		cmd = fmt.Sprintf("%s %s ", filepath.Base(os.Args[0]), strings.Join(os.Args[1:], " "))
+	}
+
+	// Filter out some parameters to make the command more stable.
+	cmd = regexp.MustCompile(`(?si)-test.gocoverdir=(.*)\s`).ReplaceAllString(cmd, "")
+	cmd = regexp.MustCompile(`(?si)-test.v=(.*)\s`).ReplaceAllString(cmd, "")
+	cmd = regexp.MustCompile(`(?si)-test.testlogfile=(.*)\s`).ReplaceAllString(cmd, "")
+	cmd = strings.TrimSpace(cmd)
+	wd, err := os.Getwd()
+	if err == nil {
+		wd = utils.GetRelativePathFromCITagsSourceRoot(wd)
+	}
+	return CreateTestSessionWith(cmd, wd, "", time.Now())
+}
+
+// CreateTestSessionWith initializes a new test session with specified command, working directory, framework, and start time.
+func CreateTestSessionWith(command string, workingDirectory string, framework string, startTime time.Time) DdTestSession {
+	// Ensure CI visibility is properly configured.
+	EnsureCiVisibilityInitialization()
+
+	operationName := "test_session"
+	if framework != "" {
+		operationName = fmt.Sprintf("%s.%s", strings.ToLower(framework), operationName)
+	}
+
+	resourceName := fmt.Sprintf("%s.%s", operationName, command)
+
+	sessionTags := []tracer.StartSpanOption{
+		tracer.Tag(constants.TestType, constants.TestTypeTest),
+		tracer.Tag(constants.TestCommand, command),
+		tracer.Tag(constants.TestCommandWorkingDirectory, workingDirectory),
+	}
+
+	testOpts := append(fillCommonTags([]tracer.StartSpanOption{
+		tracer.ResourceName(resourceName),
+		tracer.SpanType(constants.SpanTypeTestSession),
+		tracer.StartTime(startTime),
+	}), sessionTags...)
+
+	span, ctx := tracer.StartSpanFromContext(context.Background(), operationName, testOpts...)
+	sessionID := span.Context().SpanID()
+	span.SetTag(constants.TestSessionIDTag, fmt.Sprint(sessionID))
+
+	s := &tslvTestSession{
+		sessionID:        sessionID,
+		command:          command,
+		workingDirectory: workingDirectory,
+		framework:        framework,
+		modules:          map[string]DdTestModule{},
+		ciVisibilityCommon: ciVisibilityCommon{
+			startTime: startTime,
+			tags:      sessionTags,
+			span:      span,
+			ctx:       ctx,
+		},
+	}
+
+	// Ensure to close everything before CI visibility exits. In CI visibility mode, we try to never lose data.
+	PushCiVisibilityCloseAction(func() { s.Close(1) })
+
+	return s
+}
+
+// Command returns the command used to run the test session.
+func (t *tslvTestSession) Command() string { return t.command }
+
+// Framework returns the testing framework used in the test session.
+func (t *tslvTestSession) Framework() string { return t.framework }
+
+// WorkingDirectory returns the working directory of the test session.
+func (t *tslvTestSession) WorkingDirectory() string { return t.workingDirectory }
+
+// Close closes the test session with the given exit code and sets the finish time to the current time.
+func (t *tslvTestSession) Close(exitCode int) { t.CloseWithFinishTime(exitCode, time.Now()) }
+
+// CloseWithFinishTime closes the test session with the given exit code and finish time.
+func (t *tslvTestSession) CloseWithFinishTime(exitCode int, finishTime time.Time) {
+	t.mutex.Lock()
+	defer t.mutex.Unlock()
+	if t.closed {
+		return
+	}
+
+	for _, m := range t.modules {
+		m.Close()
+	}
+	t.modules = map[string]DdTestModule{}
+
+	t.span.SetTag(constants.TestCommandExitCode, exitCode)
+	if exitCode == 0 {
+		t.span.SetTag(constants.TestStatus, constants.TestStatusPass)
+	} else {
+		t.SetErrorInfo("ExitCode", "exit code is not zero.", "")
+		t.span.SetTag(constants.TestStatus, constants.TestStatusFail)
+	}
+
+	t.span.Finish(tracer.FinishTime(finishTime))
+	t.closed = true
+
+	tracer.Flush()
+}
+
+// GetOrCreateModule returns an existing module or creates a new one with the given name.
+func (t *tslvTestSession) GetOrCreateModule(name string) DdTestModule {
+	return t.GetOrCreateModuleWithFramework(name, "", "")
+}
+
+// GetOrCreateModuleWithFramework returns an existing module or creates a new one with the given name, framework, and framework version.
+func (t *tslvTestSession) GetOrCreateModuleWithFramework(name string, framework string, frameworkVersion string) DdTestModule {
+	return t.GetOrCreateModuleWithFrameworkAndStartTime(name, framework, frameworkVersion, time.Now())
+}
+
+// GetOrCreateModuleWithFrameworkAndStartTime returns an existing module or creates a new one with the given name, framework, framework version, and start time.
+func (t *tslvTestSession) GetOrCreateModuleWithFrameworkAndStartTime(name string, framework string, frameworkVersion string, startTime time.Time) DdTestModule {
+	t.mutex.Lock()
+	defer t.mutex.Unlock()
+
+	var mod DdTestModule
+	if v, ok := t.modules[name]; ok {
+		mod = v
+	} else {
+		mod = createTestModule(t, name, framework, frameworkVersion, startTime)
+		t.modules[name] = mod
+	}
+
+	return mod
+}
diff --git a/main/civisibility/integrations/manual_api_ddtestsuite.go b/main/civisibility/integrations/manual_api_ddtestsuite.go
new file mode 100644
index 0000000..68d936d
--- /dev/null
+++ b/main/civisibility/integrations/manual_api_ddtestsuite.go
@@ -0,0 +1,121 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package integrations
+
+import (
+	"context"
+	"fmt"
+	"strings"
+	"time"
+
+	"ci-visibility-test-github/main/civisibility/constants"
+
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
+)
+
+// Test Suite
+
+// Ensures that tslvTestSuite implements the DdTestSuite interface.
+var _ DdTestSuite = (*tslvTestSuite)(nil)
+
+// tslvTestSuite implements the DdTestSuite interface and represents a suite of tests within a module.
+type tslvTestSuite struct {
+	ciVisibilityCommon
+	module  *tslvTestModule
+	suiteID uint64
+	name    string
+}
+
+// createTestSuite initializes a new test suite within a given module.
+func createTestSuite(module *tslvTestModule, name string, startTime time.Time) DdTestSuite {
+	if module == nil {
+		return nil
+	}
+
+	operationName := "test_suite"
+	if module.framework != "" {
+		operationName = fmt.Sprintf("%s.%s", strings.ToLower(module.framework), operationName)
+	}
+
+	resourceName := name
+
+	// Suite tags should include module and session tags so the backend can calculate the module and session fingerprint from the suite.
+	suiteTags := append(module.tags, tracer.Tag(constants.TestSuite, name))
+	testOpts := append(fillCommonTags([]tracer.StartSpanOption{
+		tracer.ResourceName(resourceName),
+		tracer.SpanType(constants.SpanTypeTestSuite),
+		tracer.StartTime(startTime),
+	}), suiteTags...)
+
+	span, ctx := tracer.StartSpanFromContext(context.Background(), operationName, testOpts...)
+	suiteID := span.Context().SpanID()
+	if module.session != nil {
+		span.SetTag(constants.TestSessionIDTag, fmt.Sprint(module.session.sessionID))
+	}
+	span.SetTag(constants.TestModuleIDTag, fmt.Sprint(module.moduleID))
+	span.SetTag(constants.TestSuiteIDTag, fmt.Sprint(suiteID))
+
+	suite := &tslvTestSuite{
+		module:  module,
+		suiteID: suiteID,
+		name:    name,
+		ciVisibilityCommon: ciVisibilityCommon{
+			startTime: startTime,
+			tags:      suiteTags,
+			span:      span,
+			ctx:       ctx,
+		},
+	}
+
+	// Ensure to close everything before CI visibility exits. In CI visibility mode, we try to never lose data.
+	PushCiVisibilityCloseAction(func() { suite.Close() })
+
+	return suite
+}
+
+// Name returns the name of the test suite.
+func (t *tslvTestSuite) Name() string { return t.name }
+
+// Module returns the module to which the test suite belongs.
+func (t *tslvTestSuite) Module() DdTestModule { return t.module }
+
+// Close closes the test suite and sets the finish time to the current time.
+func (t *tslvTestSuite) Close() { t.CloseWithFinishTime(time.Now()) }
+
+// CloseWithFinishTime closes the test suite with the given finish time.
+func (t *tslvTestSuite) CloseWithFinishTime(finishTime time.Time) {
+	t.mutex.Lock()
+	defer t.mutex.Unlock()
+	if t.closed {
+		return
+	}
+
+	t.span.Finish(tracer.FinishTime(finishTime))
+	t.closed = true
+}
+
+// SetError sets an error on the test suite and marks the module as having an error.
+func (t *tslvTestSuite) SetError(err error) {
+	t.ciVisibilityCommon.SetError(err)
+	t.Module().SetTag(ext.Error, true)
+}
+
+// SetErrorInfo sets detailed error information on the test suite and marks the module as having an error.
+func (t *tslvTestSuite) SetErrorInfo(errType string, message string, callstack string) {
+	t.ciVisibilityCommon.SetErrorInfo(errType, message, callstack)
+	t.Module().SetTag(ext.Error, true)
+}
+
+// CreateTest creates a new test with the given name and sets the start time to the current time.
+func (t *tslvTestSuite) CreateTest(name string) DdTest {
+	return t.CreateTestWithStartTime(name, time.Now())
+}
+
+// CreateTestWithStartTime creates a new test with the given name and start time.
+func (t *tslvTestSuite) CreateTestWithStartTime(name string, startTime time.Time) DdTest {
+	return createTest(t, name, startTime)
+}
diff --git a/main/civisibility/integrations/manual_api_mocktracer_test.go b/main/civisibility/integrations/manual_api_mocktracer_test.go
new file mode 100644
index 0000000..dce2216
--- /dev/null
+++ b/main/civisibility/integrations/manual_api_mocktracer_test.go
@@ -0,0 +1,278 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package integrations
+
+import (
+	"errors"
+	"os"
+	"runtime"
+	"testing"
+	"time"
+
+	"ci-visibility-test-github/main/civisibility/constants"
+
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext"
+
+	"github.com/stretchr/testify/assert"
+	"gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer"
+)
+
+var mockTracer mocktracer.Tracer
+
+func TestMain(m *testing.M) {
+	// Initialize civisibility using the mocktracer for testing
+	mockTracer = InitializeCIVisibilityMock()
+
+	// Run tests
+	os.Exit(m.Run())
+}
+
+func createDDTestSession(now time.Time) DdTestSession {
+	session := CreateTestSessionWith("my-command", "/tmp/wd", "my-testing-framework", now)
+	session.SetTag("my-tag", "my-value")
+	return session
+}
+
+func createDDTestModule(now time.Time) (DdTestSession, DdTestModule) {
+	session := createDDTestSession(now)
+	module := session.GetOrCreateModuleWithFrameworkAndStartTime("my-module", "my-module-framework", "framework-version", now)
+	module.SetTag("my-tag", "my-value")
+	return session, module
+}
+
+func createDDTestSuite(now time.Time) (DdTestSession, DdTestModule, DdTestSuite) {
+	session, module := createDDTestModule(now)
+	suite := module.GetOrCreateSuiteWithStartTime("my-suite", now)
+	suite.SetTag("my-tag", "my-value")
+	return session, module, suite
+}
+
+func createDDTest(now time.Time) (DdTestSession, DdTestModule, DdTestSuite, DdTest) {
+	session, module, suite := createDDTestSuite(now)
+	test := suite.CreateTestWithStartTime("my-test", now)
+	test.SetTag("my-tag", "my-value")
+	return session, module, suite, test
+}
+
+func commonAssertions(assert *assert.Assertions, sessionSpan mocktracer.Span) {
+	tags := map[string]interface{}{
+		"my-tag":              "my-value",
+		constants.Origin:      constants.CIAppTestOrigin,
+		constants.TestType:    constants.TestTypeTest,
+		constants.TestCommand: "my-command",
+	}
+
+	spanTags := sessionSpan.Tags()
+
+	assert.Subset(spanTags, tags)
+	assert.Contains(spanTags, constants.OSPlatform)
+	assert.Contains(spanTags, constants.OSArchitecture)
+	assert.Contains(spanTags, constants.OSVersion)
+	assert.Contains(spanTags, constants.RuntimeVersion)
+	assert.Contains(spanTags, constants.RuntimeName)
+	assert.Contains(spanTags, constants.GitRepositoryURL)
+	assert.Contains(spanTags, constants.GitCommitSHA)
+}
+
+func TestSession(t *testing.T) {
+	mockTracer.Reset()
+	assert := assert.New(t)
+
+	now := time.Now()
+	session := createDDTestSession(now)
+	assert.NotNil(session.Context())
+	assert.Equal("my-command", session.Command())
+	assert.Equal("/tmp/wd", session.WorkingDirectory())
+	assert.Equal("my-testing-framework", session.Framework())
+	assert.Equal(now, session.StartTime())
+
+	session.Close(42)
+
+	finishedSpans := mockTracer.FinishedSpans()
+	assert.Equal(1, len(finishedSpans))
+	sessionAssertions(assert, now, finishedSpans[0])
+
+	// session already closed, this is a no-op
+	session.Close(0)
+}
+
+func sessionAssertions(assert *assert.Assertions, now time.Time, sessionSpan mocktracer.Span) {
+	assert.Equal(now, sessionSpan.StartTime())
+	assert.Equal("my-testing-framework.test_session", sessionSpan.OperationName())
+
+	tags := map[string]interface{}{
+		ext.ResourceName:              "my-testing-framework.test_session.my-command",
+		ext.Error:                     true,
+		ext.ErrorType:                 "ExitCode",
+		ext.ErrorMsg:                  "exit code is not zero.",
+		ext.SpanType:                  constants.SpanTypeTestSession,
+		constants.TestStatus:          constants.TestStatusFail,
+		constants.TestCommandExitCode: 42,
+	}
+
+	spanTags := sessionSpan.Tags()
+
+	assert.Subset(spanTags, tags)
+	assert.Contains(spanTags, constants.TestSessionIDTag)
+	commonAssertions(assert, sessionSpan)
+}
+
+func TestModule(t *testing.T) {
+	mockTracer.Reset()
+	assert := assert.New(t)
+
+	now := time.Now()
+	session, module := createDDTestModule(now)
+	defer func() { session.Close(0) }()
+	module.SetErrorInfo("my-type", "my-message", "my-stack")
+
+	assert.NotNil(module.Context())
+	assert.Equal("my-module", module.Name())
+	assert.Equal("my-module-framework", module.Framework())
+	assert.Equal(now, module.StartTime())
+	assert.Equal(session, module.Session())
+
+	module.Close()
+
+	finishedSpans := mockTracer.FinishedSpans()
+	assert.Equal(1, len(finishedSpans))
+	moduleAssertions(assert, now, finishedSpans[0])
+
+	//no-op call
+	module.Close()
+}
+
+func moduleAssertions(assert *assert.Assertions, now time.Time, moduleSpan mocktracer.Span) {
+	assert.Equal(now, moduleSpan.StartTime())
+	assert.Equal("my-module-framework.test_module", moduleSpan.OperationName())
+
+	tags := map[string]interface{}{
+		ext.ResourceName:     "my-module",
+		ext.Error:            true,
+		ext.ErrorType:        "my-type",
+		ext.ErrorMsg:         "my-message",
+		ext.ErrorStack:       "my-stack",
+		ext.SpanType:         constants.SpanTypeTestModule,
+		constants.TestModule: "my-module",
+	}
+
+	spanTags := moduleSpan.Tags()
+
+	assert.Subset(spanTags, tags)
+	assert.Contains(spanTags, constants.TestSessionIDTag)
+	assert.Contains(spanTags, constants.TestModuleIDTag)
+	commonAssertions(assert, moduleSpan)
+}
+
+func TestSuite(t *testing.T) {
+	mockTracer.Reset()
+	assert := assert.New(t)
+
+	now := time.Now()
+	session, module, suite := createDDTestSuite(now)
+	defer func() {
+		session.Close(0)
+		module.Close()
+	}()
+	suite.SetErrorInfo("my-type", "my-message", "my-stack")
+
+	assert.NotNil(suite.Context())
+	assert.Equal("my-suite", suite.Name())
+	assert.Equal(now, suite.StartTime())
+	assert.Equal(module, suite.Module())
+
+	suite.Close()
+
+	finishedSpans := mockTracer.FinishedSpans()
+	assert.Equal(1, len(finishedSpans))
+	suiteAssertions(assert, now, finishedSpans[0])
+
+	//no-op call
+	suite.Close()
+}
+
+func suiteAssertions(assert *assert.Assertions, now time.Time, suiteSpan mocktracer.Span) {
+	assert.Equal(now, suiteSpan.StartTime())
+	assert.Equal("my-module-framework.test_suite", suiteSpan.OperationName())
+
+	tags := map[string]interface{}{
+		ext.ResourceName:     "my-suite",
+		ext.Error:            true,
+		ext.ErrorType:        "my-type",
+		ext.ErrorMsg:         "my-message",
+		ext.ErrorStack:       "my-stack",
+		ext.SpanType:         constants.SpanTypeTestSuite,
+		constants.TestModule: "my-module",
+		constants.TestSuite:  "my-suite",
+	}
+
+	spanTags := suiteSpan.Tags()
+
+	assert.Subset(spanTags, tags)
+	assert.Contains(spanTags, constants.TestSessionIDTag)
+	assert.Contains(spanTags, constants.TestModuleIDTag)
+	assert.Contains(spanTags, constants.TestSuiteIDTag)
+	commonAssertions(assert, suiteSpan)
+}
+
+func Test(t *testing.T) {
+	mockTracer.Reset()
+	assert := assert.New(t)
+
+	now := time.Now()
+	session, module, suite, test := createDDTest(now)
+	defer func() {
+		session.Close(0)
+		module.Close()
+		suite.Close()
+	}()
+	test.SetError(errors.New("we keep the last error"))
+	test.SetErrorInfo("my-type", "my-message", "my-stack")
+	pc, _, _, _ := runtime.Caller(0)
+	test.SetTestFunc(runtime.FuncForPC(pc))
+
+	assert.NotNil(test.Context())
+	assert.Equal("my-test", test.Name())
+	assert.Equal(now, test.StartTime())
+	assert.Equal(suite, test.Suite())
+
+	test.Close(ResultStatusPass)
+
+	finishedSpans := mockTracer.FinishedSpans()
+	assert.Equal(1, len(finishedSpans))
+	testAssertions(assert, now, finishedSpans[0])
+
+	//no-op call
+	test.Close(ResultStatusSkip)
+}
+
+func testAssertions(assert *assert.Assertions, now time.Time, testSpan mocktracer.Span) {
+	assert.Equal(now, testSpan.StartTime())
+	assert.Equal("my-module-framework.test", testSpan.OperationName())
+
+	tags := map[string]interface{}{
+		ext.ResourceName:     "my-suite.my-test",
+		ext.Error:            true,
+		ext.ErrorType:        "my-type",
+		ext.ErrorMsg:         "my-message",
+		ext.ErrorStack:       "my-stack",
+		ext.SpanType:         constants.SpanTypeTest,
+		constants.TestModule: "my-module",
+		constants.TestSuite:  "my-suite",
+		constants.TestName:   "my-test",
+		constants.TestStatus: constants.TestStatusPass,
+	}
+
+	spanTags := testSpan.Tags()
+
+	assert.Subset(spanTags, tags)
+	assert.Contains(spanTags, constants.TestSessionIDTag)
+	assert.Contains(spanTags, constants.TestModuleIDTag)
+	assert.Contains(spanTags, constants.TestSuiteIDTag)
+	assert.Contains(spanTags, constants.TestSourceFile)
+	assert.Contains(spanTags, constants.TestSourceStartLine)
+	commonAssertions(assert, testSpan)
+}
diff --git a/main/civisibility/integrations/manual_api_test.go b/main/civisibility/integrations/manual_api_test.go
new file mode 100644
index 0000000..2bb0a4e
--- /dev/null
+++ b/main/civisibility/integrations/manual_api_test.go
@@ -0,0 +1,329 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package integrations
+
+import (
+	"context"
+	"runtime"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
+)
+
+// Mocking the ddTslvEvent interface
+type MockDdTslvEvent struct {
+	mock.Mock
+}
+
+func (m *MockDdTslvEvent) Context() context.Context {
+	args := m.Called()
+	return args.Get(0).(context.Context)
+}
+
+func (m *MockDdTslvEvent) StartTime() time.Time {
+	args := m.Called()
+	return args.Get(0).(time.Time)
+}
+
+func (m *MockDdTslvEvent) SetError(err error) {
+	m.Called(err)
+}
+
+func (m *MockDdTslvEvent) SetErrorInfo(errType string, message string, callstack string) {
+	m.Called(errType, message, callstack)
+}
+
+func (m *MockDdTslvEvent) SetTag(key string, value interface{}) {
+	m.Called(key, value)
+}
+
+// Mocking the DdTest interface
+type MockDdTest struct {
+	MockDdTslvEvent
+	mock.Mock
+}
+
+func (m *MockDdTest) Name() string {
+	args := m.Called()
+	return args.String(0)
+}
+
+func (m *MockDdTest) Suite() DdTestSuite {
+	args := m.Called()
+	return args.Get(0).(DdTestSuite)
+}
+
+func (m *MockDdTest) Close(status TestResultStatus) {
+	m.Called(status)
+}
+
+func (m *MockDdTest) CloseWithFinishTime(status TestResultStatus, finishTime time.Time) {
+	m.Called(status, finishTime)
+}
+
+func (m *MockDdTest) CloseWithFinishTimeAndSkipReason(status TestResultStatus, finishTime time.Time, skipReason string) {
+	m.Called(status, finishTime, skipReason)
+}
+
+func (m *MockDdTest) SetTestFunc(fn *runtime.Func) {
+	m.Called(fn)
+}
+
+func (m *MockDdTest) SetBenchmarkData(measureType string, data map[string]any) {
+	m.Called(measureType, data)
+}
+
+// Mocking the DdTestSession interface
+type MockDdTestSession struct {
+	MockDdTslvEvent
+	mock.Mock
+}
+
+func (m *MockDdTestSession) Command() string {
+	args := m.Called()
+	return args.String(0)
+}
+
+func (m *MockDdTestSession) Framework() string {
+	args := m.Called()
+	return args.String(0)
+}
+
+func (m *MockDdTestSession) WorkingDirectory() string {
+	args := m.Called()
+	return args.String(0)
+}
+
+func (m *MockDdTestSession) Close(exitCode int) {
+	m.Called(exitCode)
+}
+
+func (m *MockDdTestSession) CloseWithFinishTime(exitCode int, finishTime time.Time) {
+	m.Called(exitCode, finishTime)
+}
+
+func (m *MockDdTestSession) GetOrCreateModule(name string) DdTestModule {
+	args := m.Called(name)
+	return args.Get(0).(DdTestModule)
+}
+
+func (m *MockDdTestSession) GetOrCreateModuleWithFramework(name string, framework string, frameworkVersion string) DdTestModule {
+	args := m.Called(name, framework, frameworkVersion)
+	return args.Get(0).(DdTestModule)
+}
+
+func (m *MockDdTestSession) GetOrCreateModuleWithFrameworkAndStartTime(name string, framework string, frameworkVersion string, startTime time.Time) DdTestModule {
+	args := m.Called(name, framework, frameworkVersion, startTime)
+	return args.Get(0).(DdTestModule)
+}
+
+// Mocking the DdTestModule interface
+type MockDdTestModule struct {
+	MockDdTslvEvent
+	mock.Mock
+}
+
+func (m *MockDdTestModule) Session() DdTestSession {
+	args := m.Called()
+	return args.Get(0).(DdTestSession)
+}
+
+func (m *MockDdTestModule) Framework() string {
+	args := m.Called()
+	return args.String(0)
+}
+
+func (m *MockDdTestModule) Name() string {
+	args := m.Called()
+	return args.String(0)
+}
+
+func (m *MockDdTestModule) Close() {
+	m.Called()
+}
+
+func (m *MockDdTestModule) CloseWithFinishTime(finishTime time.Time) {
+	m.Called(finishTime)
+}
+
+func (m *MockDdTestModule) GetOrCreateSuite(name string) DdTestSuite {
+	args := m.Called(name)
+	return args.Get(0).(DdTestSuite)
+}
+
+func (m *MockDdTestModule) GetOrCreateSuiteWithStartTime(name string, startTime time.Time) DdTestSuite {
+	args := m.Called(name, startTime)
+	return args.Get(0).(DdTestSuite)
+}
+
+// Mocking the DdTestSuite interface
+type MockDdTestSuite struct {
+	MockDdTslvEvent
+	mock.Mock
+}
+
+func (m *MockDdTestSuite) Module() DdTestModule {
+	args := m.Called()
+	return args.Get(0).(DdTestModule)
+}
+
+func (m *MockDdTestSuite) Name() string {
+	args := m.Called()
+	return args.String(0)
+}
+
+func (m *MockDdTestSuite) Close() {
+	m.Called()
+}
+
+func (m *MockDdTestSuite) CloseWithFinishTime(finishTime time.Time) {
+	m.Called(finishTime)
+}
+
+func (m *MockDdTestSuite) CreateTest(name string) DdTest {
+	args := m.Called(name)
+	return args.Get(0).(DdTest)
+}
+
+func (m *MockDdTestSuite) CreateTestWithStartTime(name string, startTime time.Time) DdTest {
+	args := m.Called(name, startTime)
+	return args.Get(0).(DdTest)
+}
+
+// Unit tests
+func TestDdTestSession(t *testing.T) {
+	mockSession := new(MockDdTestSession)
+	mockSession.On("Command").Return("test-command")
+	mockSession.On("Framework").Return("test-framework")
+	mockSession.On("WorkingDirectory").Return("/path/to/working/dir")
+	mockSession.On("Close", 0).Return()
+	mockSession.On("CloseWithFinishTime", 0, mock.Anything).Return()
+	mockSession.On("GetOrCreateModule", "test-module").Return(new(MockDdTestModule))
+	mockSession.On("GetOrCreateModuleWithFramework", "test-module", "test-framework", "1.0").Return(new(MockDdTestModule))
+	mockSession.On("GetOrCreateModuleWithFrameworkAndStartTime", "test-module", "test-framework", "1.0", mock.Anything).Return(new(MockDdTestModule))
+
+	session := (DdTestSession)(mockSession)
+	assert.Equal(t, "test-command", session.Command())
+	assert.Equal(t, "test-framework", session.Framework())
+	assert.Equal(t, "/path/to/working/dir", session.WorkingDirectory())
+
+	session.Close(0)
+	mockSession.AssertCalled(t, "Close", 0)
+
+	now := time.Now()
+	session.CloseWithFinishTime(0, now)
+	mockSession.AssertCalled(t, "CloseWithFinishTime", 0, now)
+
+	module := session.GetOrCreateModule("test-module")
+	assert.NotNil(t, module)
+	mockSession.AssertCalled(t, "GetOrCreateModule", "test-module")
+
+	module = session.GetOrCreateModuleWithFramework("test-module", "test-framework", "1.0")
+	assert.NotNil(t, module)
+	mockSession.AssertCalled(t, "GetOrCreateModuleWithFramework", "test-module", "test-framework", "1.0")
+
+	module = session.GetOrCreateModuleWithFrameworkAndStartTime("test-module", "test-framework", "1.0", now)
+	assert.NotNil(t, module)
+	mockSession.AssertCalled(t, "GetOrCreateModuleWithFrameworkAndStartTime", "test-module", "test-framework", "1.0", now)
+}
+
+func TestDdTestModule(t *testing.T) {
+	mockModule := new(MockDdTestModule)
+	mockModule.On("Session").Return(new(MockDdTestSession))
+	mockModule.On("Framework").Return("test-framework")
+	mockModule.On("Name").Return("test-module")
+	mockModule.On("Close").Return()
+	mockModule.On("CloseWithFinishTime", mock.Anything).Return()
+	mockModule.On("GetOrCreateSuite", "test-suite").Return(new(MockDdTestSuite))
+	mockModule.On("GetOrCreateSuiteWithStartTime", "test-suite", mock.Anything).Return(new(MockDdTestSuite))
+
+	module := (DdTestModule)(mockModule)
+
+	assert.Equal(t, "test-framework", module.Framework())
+	assert.Equal(t, "test-module", module.Name())
+
+	module.Close()
+	mockModule.AssertCalled(t, "Close")
+
+	now := time.Now()
+	module.CloseWithFinishTime(now)
+	mockModule.AssertCalled(t, "CloseWithFinishTime", now)
+
+	suite := module.GetOrCreateSuite("test-suite")
+	assert.NotNil(t, suite)
+	mockModule.AssertCalled(t, "GetOrCreateSuite", "test-suite")
+
+	suite = module.GetOrCreateSuiteWithStartTime("test-suite", now)
+	assert.NotNil(t, suite)
+	mockModule.AssertCalled(t, "GetOrCreateSuiteWithStartTime", "test-suite", now)
+}
+
+func TestDdTestSuite(t *testing.T) {
+	mockSuite := new(MockDdTestSuite)
+	mockSuite.On("Module").Return(new(MockDdTestModule))
+	mockSuite.On("Name").Return("test-suite")
+	mockSuite.On("Close").Return()
+	mockSuite.On("CloseWithFinishTime", mock.Anything).Return()
+	mockSuite.On("CreateTest", "test-name").Return(new(MockDdTest))
+	mockSuite.On("CreateTestWithStartTime", "test-name", mock.Anything).Return(new(MockDdTest))
+
+	suite := (DdTestSuite)(mockSuite)
+
+	assert.Equal(t, "test-suite", suite.Name())
+
+	suite.Close()
+	mockSuite.AssertCalled(t, "Close")
+
+	now := time.Now()
+	suite.CloseWithFinishTime(now)
+	mockSuite.AssertCalled(t, "CloseWithFinishTime", now)
+
+	test := suite.CreateTest("test-name")
+	assert.NotNil(t, test)
+	mockSuite.AssertCalled(t, "CreateTest", "test-name")
+
+	test = suite.CreateTestWithStartTime("test-name", now)
+	assert.NotNil(t, test)
+	mockSuite.AssertCalled(t, "CreateTestWithStartTime", "test-name", now)
+}
+
+func TestDdTest(t *testing.T) {
+	mockTest := new(MockDdTest)
+	mockTest.On("Name").Return("test-name")
+	mockTest.On("Suite").Return(new(MockDdTestSuite))
+	mockTest.On("Close", ResultStatusPass).Return()
+	mockTest.On("CloseWithFinishTime", ResultStatusPass, mock.Anything).Return()
+	mockTest.On("CloseWithFinishTimeAndSkipReason", ResultStatusSkip, mock.Anything, "SkipReason").Return()
+	mockTest.On("SetTestFunc", mock.Anything).Return()
+	mockTest.On("SetBenchmarkData", "measure-type", mock.Anything).Return()
+
+	test := (DdTest)(mockTest)
+
+	assert.Equal(t, "test-name", test.Name())
+
+	suite := test.Suite()
+	assert.NotNil(t, suite)
+
+	test.Close(ResultStatusPass)
+	mockTest.AssertCalled(t, "Close", ResultStatusPass)
+
+	now := time.Now()
+	test.CloseWithFinishTime(ResultStatusPass, now)
+	mockTest.AssertCalled(t, "CloseWithFinishTime", ResultStatusPass, now)
+
+	skipReason := "SkipReason"
+	test.CloseWithFinishTimeAndSkipReason(ResultStatusSkip, now, skipReason)
+	mockTest.AssertCalled(t, "CloseWithFinishTimeAndSkipReason", ResultStatusSkip, now, skipReason)
+
+	test.SetTestFunc(nil)
+	mockTest.AssertCalled(t, "SetTestFunc", (*runtime.Func)(nil))
+
+	benchmarkData := map[string]any{"key": "value"}
+	test.SetBenchmarkData("measure-type", benchmarkData)
+	mockTest.AssertCalled(t, "SetBenchmarkData", "measure-type", benchmarkData)
+}
diff --git a/main/civisibility/utils/ci_providers.go b/main/civisibility/utils/ci_providers.go
new file mode 100644
index 0000000..bb8010c
--- /dev/null
+++ b/main/civisibility/utils/ci_providers.go
@@ -0,0 +1,569 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package utils
+
+import (
+	"encoding/json"
+	"fmt"
+	"os"
+	"regexp"
+	"sort"
+	"strings"
+
+	"ci-visibility-test-github/main/civisibility/constants"
+)
+
+// providerType defines a function type that returns a map of string key-value pairs.
+type providerType = func() map[string]string
+
+// providers maps environment variable names to their corresponding CI provider extraction functions.
+var providers = map[string]providerType{
+	"APPVEYOR":            extractAppveyor,
+	"TF_BUILD":            extractAzurePipelines,
+	"BITBUCKET_COMMIT":    extractBitbucket,
+	"BUDDY":               extractBuddy,
+	"BUILDKITE":           extractBuildkite,
+	"CIRCLECI":            extractCircleCI,
+	"GITHUB_SHA":          extractGithubActions,
+	"GITLAB_CI":           extractGitlab,
+	"JENKINS_URL":         extractJenkins,
+	"TEAMCITY_VERSION":    extractTeamcity,
+	"TRAVIS":              extractTravis,
+	"BITRISE_BUILD_SLUG":  extractBitrise,
+	"CF_BUILD_ID":         extractCodefresh,
+	"CODEBUILD_INITIATOR": extractAwsCodePipeline,
+}
+
+// getEnvVarsJSON returns a JSON representation of the specified environment variables.
+func getEnvVarsJSON(envVars ...string) ([]byte, error) {
+	envVarsMap := make(map[string]string)
+	for _, envVar := range envVars {
+		value := os.Getenv(envVar)
+		if value != "" {
+			envVarsMap[envVar] = value
+		}
+	}
+	return json.Marshal(envVarsMap)
+}
+
+// getProviderTags extracts CI information from environment variables.
+func getProviderTags() map[string]string {
+	tags := map[string]string{}
+	for key, provider := range providers {
+		if _, ok := os.LookupEnv(key); !ok {
+			continue
+		}
+		tags = provider()
+	}
+
+	// replace with user specific tags
+	replaceWithUserSpecificTags(tags)
+
+	// Normalize tags
+	normalizeTags(tags)
+
+	// Expand ~
+	if tag, ok := tags[constants.CIWorkspacePath]; ok && tag != "" {
+		tags[constants.CIWorkspacePath] = ExpandPath(tag)
+	}
+
+	// remove empty values
+	for tag, value := range tags {
+		if value == "" {
+			delete(tags, tag)
+		}
+	}
+
+	return tags
+}
+
+// normalizeTags normalizes specific tags to remove prefixes and sensitive information.
+func normalizeTags(tags map[string]string) {
+	if tag, ok := tags[constants.GitBranch]; ok && tag != "" {
+		if strings.Contains(tag, "refs/tags") || strings.Contains(tag, "origin/tags") || strings.Contains(tag, "refs/heads/tags") {
+			tags[constants.GitTag] = normalizeRef(tag)
+		}
+		tags[constants.GitBranch] = normalizeRef(tag)
+	}
+	if tag, ok := tags[constants.GitTag]; ok && tag != "" {
+		tags[constants.GitTag] = normalizeRef(tag)
+	}
+	if tag, ok := tags[constants.GitRepositoryURL]; ok && tag != "" {
+		tags[constants.GitRepositoryURL] = filterSensitiveInfo(tag)
+	}
+	if tag, ok := tags[constants.CIPipelineURL]; ok && tag != "" {
+		tags[constants.CIPipelineURL] = filterSensitiveInfo(tag)
+	}
+	if tag, ok := tags[constants.CIJobURL]; ok && tag != "" {
+		tags[constants.CIJobURL] = filterSensitiveInfo(tag)
+	}
+	if tag, ok := tags[constants.CIEnvVars]; ok && tag != "" {
+		tags[constants.CIEnvVars] = filterSensitiveInfo(tag)
+	}
+}
+
+// replaceWithUserSpecificTags replaces certain tags with user-specific environment variable values.
+func replaceWithUserSpecificTags(tags map[string]string) {
+	replace := func(tagName, envName string) {
+		tags[tagName] = getEnvironmentVariableIfIsNotEmpty(envName, tags[tagName])
+	}
+
+	replace(constants.GitBranch, "DD_GIT_BRANCH")
+	replace(constants.GitTag, "DD_GIT_TAG")
+	replace(constants.GitRepositoryURL, "DD_GIT_REPOSITORY_URL")
+	replace(constants.GitCommitSHA, "DD_GIT_COMMIT_SHA")
+	replace(constants.GitCommitMessage, "DD_GIT_COMMIT_MESSAGE")
+	replace(constants.GitCommitAuthorName, "DD_GIT_COMMIT_AUTHOR_NAME")
+	replace(constants.GitCommitAuthorEmail, "DD_GIT_COMMIT_AUTHOR_EMAIL")
+	replace(constants.GitCommitAuthorDate, "DD_GIT_COMMIT_AUTHOR_DATE")
+	replace(constants.GitCommitCommitterName, "DD_GIT_COMMIT_COMMITTER_NAME")
+	replace(constants.GitCommitCommitterEmail, "DD_GIT_COMMIT_COMMITTER_EMAIL")
+	replace(constants.GitCommitCommitterDate, "DD_GIT_COMMIT_COMMITTER_DATE")
+}
+
+// getEnvironmentVariableIfIsNotEmpty returns the environment variable value if it is not empty, otherwise returns the default value.
+func getEnvironmentVariableIfIsNotEmpty(key string, defaultValue string) string {
+	if value, ok := os.LookupEnv(key); ok && value != "" {
+		return value
+	}
+	return defaultValue
+}
+
+// normalizeRef normalizes a Git reference name by removing common prefixes.
+func normalizeRef(name string) string {
+	// Define the prefixes to remove
+	prefixes := []string{"refs/heads/", "refs/", "origin/", "tags/"}
+
+	// Iterate over prefixes and remove them if present
+	for _, prefix := range prefixes {
+		if strings.HasPrefix(name, prefix) {
+			name = strings.TrimPrefix(name, prefix)
+		}
+	}
+	return name
+}
+
+// firstEnv returns the value of the first non-empty environment variable from the provided list.
+func firstEnv(keys ...string) string {
+	for _, key := range keys {
+		if value, ok := os.LookupEnv(key); ok {
+			if value != "" {
+				return value
+			}
+		}
+	}
+	return ""
+}
+
+// extractAppveyor extracts CI information specific to Appveyor.
+func extractAppveyor() map[string]string {
+	tags := map[string]string{}
+	url := fmt.Sprintf("https://ci.appveyor.com/project/%s/builds/%s", os.Getenv("APPVEYOR_REPO_NAME"), os.Getenv("APPVEYOR_BUILD_ID"))
+	tags[constants.CIProviderName] = "appveyor"
+	if os.Getenv("APPVEYOR_REPO_PROVIDER") == "github" {
+		tags[constants.GitRepositoryURL] = fmt.Sprintf("https://github.com/%s.git", os.Getenv("APPVEYOR_REPO_NAME"))
+	} else {
+		tags[constants.GitRepositoryURL] = os.Getenv("APPVEYOR_REPO_NAME")
+	}
+
+	tags[constants.GitCommitSHA] = os.Getenv("APPVEYOR_REPO_COMMIT")
+	tags[constants.GitBranch] = firstEnv("APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH", "APPVEYOR_REPO_BRANCH")
+	tags[constants.GitTag] = os.Getenv("APPVEYOR_REPO_TAG_NAME")
+
+	tags[constants.CIWorkspacePath] = os.Getenv("APPVEYOR_BUILD_FOLDER")
+	tags[constants.CIPipelineID] = os.Getenv("APPVEYOR_BUILD_ID")
+	tags[constants.CIPipelineName] = os.Getenv("APPVEYOR_REPO_NAME")
+	tags[constants.CIPipelineNumber] = os.Getenv("APPVEYOR_BUILD_NUMBER")
+	tags[constants.CIPipelineURL] = url
+	tags[constants.CIJobURL] = url
+	tags[constants.GitCommitMessage] = fmt.Sprintf("%s\n%s", os.Getenv("APPVEYOR_REPO_COMMIT_MESSAGE"), os.Getenv("APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED"))
+	tags[constants.GitCommitAuthorName] = os.Getenv("APPVEYOR_REPO_COMMIT_AUTHOR")
+	tags[constants.GitCommitAuthorEmail] = os.Getenv("APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL")
+	return tags
+}
+
+// extractAzurePipelines extracts CI information specific to Azure Pipelines.
+func extractAzurePipelines() map[string]string {
+	tags := map[string]string{}
+	baseURL := fmt.Sprintf("%s%s/_build/results?buildId=%s", os.Getenv("SYSTEM_TEAMFOUNDATIONSERVERURI"), os.Getenv("SYSTEM_TEAMPROJECTID"), os.Getenv("BUILD_BUILDID"))
+	pipelineURL := baseURL
+	jobURL := fmt.Sprintf("%s&view=logs&j=%s&t=%s", baseURL, os.Getenv("SYSTEM_JOBID"), os.Getenv("SYSTEM_TASKINSTANCEID"))
+	branchOrTag := firstEnv("SYSTEM_PULLREQUEST_SOURCEBRANCH", "BUILD_SOURCEBRANCH", "BUILD_SOURCEBRANCHNAME")
+	branch := ""
+	tag := ""
+	if strings.Contains(branchOrTag, "tags/") {
+		tag = branchOrTag
+	} else {
+		branch = branchOrTag
+	}
+	tags[constants.CIProviderName] = "azurepipelines"
+	tags[constants.CIWorkspacePath] = os.Getenv("BUILD_SOURCESDIRECTORY")
+
+	tags[constants.CIPipelineID] = os.Getenv("BUILD_BUILDID")
+	tags[constants.CIPipelineName] = os.Getenv("BUILD_DEFINITIONNAME")
+	tags[constants.CIPipelineNumber] = os.Getenv("BUILD_BUILDID")
+	tags[constants.CIPipelineURL] = pipelineURL
+
+	tags[constants.CIStageName] = os.Getenv("SYSTEM_STAGEDISPLAYNAME")
+
+	tags[constants.CIJobName] = os.Getenv("SYSTEM_JOBDISPLAYNAME")
+	tags[constants.CIJobURL] = jobURL
+
+	tags[constants.GitRepositoryURL] = firstEnv("SYSTEM_PULLREQUEST_SOURCEREPOSITORYURI", "BUILD_REPOSITORY_URI")
+	tags[constants.GitCommitSHA] = firstEnv("SYSTEM_PULLREQUEST_SOURCECOMMITID", "BUILD_SOURCEVERSION")
+	tags[constants.GitBranch] = branch
+	tags[constants.GitTag] = tag
+	tags[constants.GitCommitMessage] = os.Getenv("BUILD_SOURCEVERSIONMESSAGE")
+	tags[constants.GitCommitAuthorName] = os.Getenv("BUILD_REQUESTEDFORID")
+	tags[constants.GitCommitAuthorEmail] = os.Getenv("BUILD_REQUESTEDFOREMAIL")
+
+	jsonString, err := getEnvVarsJSON("SYSTEM_TEAMPROJECTID", "BUILD_BUILDID", "SYSTEM_JOBID")
+	if err == nil {
+		tags[constants.CIEnvVars] = string(jsonString)
+	}
+
+	return tags
+}
+
+// extractBitrise extracts CI information specific to Bitrise.
+func extractBitrise() map[string]string {
+	tags := map[string]string{}
+	tags[constants.CIProviderName] = "bitrise"
+	tags[constants.GitRepositoryURL] = os.Getenv("GIT_REPOSITORY_URL")
+	tags[constants.GitCommitSHA] = firstEnv("BITRISE_GIT_COMMIT", "GIT_CLONE_COMMIT_HASH")
+	tags[constants.GitBranch] = firstEnv("BITRISEIO_GIT_BRANCH_DEST", "BITRISE_GIT_BRANCH")
+	tags[constants.GitTag] = os.Getenv("BITRISE_GIT_TAG")
+	tags[constants.CIWorkspacePath] = os.Getenv("BITRISE_SOURCE_DIR")
+	tags[constants.CIPipelineID] = os.Getenv("BITRISE_BUILD_SLUG")
+	tags[constants.CIPipelineName] = os.Getenv("BITRISE_TRIGGERED_WORKFLOW_ID")
+	tags[constants.CIPipelineNumber] = os.Getenv("BITRISE_BUILD_NUMBER")
+	tags[constants.CIPipelineURL] = os.Getenv("BITRISE_BUILD_URL")
+	tags[constants.GitCommitMessage] = os.Getenv("BITRISE_GIT_MESSAGE")
+	return tags
+}
+
+// extractBitbucket extracts CI information specific to Bitbucket.
+func extractBitbucket() map[string]string {
+	tags := map[string]string{}
+	url := fmt.Sprintf("https://bitbucket.org/%s/addon/pipelines/home#!/results/%s", os.Getenv("BITBUCKET_REPO_FULL_NAME"), os.Getenv("BITBUCKET_BUILD_NUMBER"))
+	tags[constants.CIProviderName] = "bitbucket"
+	tags[constants.GitRepositoryURL] = firstEnv("BITBUCKET_GIT_SSH_ORIGIN", "BITBUCKET_GIT_HTTP_ORIGIN")
+	tags[constants.GitCommitSHA] = os.Getenv("BITBUCKET_COMMIT")
+	tags[constants.GitBranch] = os.Getenv("BITBUCKET_BRANCH")
+	tags[constants.GitTag] = os.Getenv("BITBUCKET_TAG")
+	tags[constants.CIWorkspacePath] = os.Getenv("BITBUCKET_CLONE_DIR")
+	tags[constants.CIPipelineID] = strings.Trim(os.Getenv("BITBUCKET_PIPELINE_UUID"), "{}")
+	tags[constants.CIPipelineNumber] = os.Getenv("BITBUCKET_BUILD_NUMBER")
+	tags[constants.CIPipelineName] = os.Getenv("BITBUCKET_REPO_FULL_NAME")
+	tags[constants.CIPipelineURL] = url
+	tags[constants.CIJobURL] = url
+	return tags
+}
+
+// extractBuddy extracts CI information specific to Buddy.
+func extractBuddy() map[string]string {
+	tags := map[string]string{}
+	tags[constants.CIProviderName] = "buddy"
+	tags[constants.CIPipelineID] = fmt.Sprintf("%s/%s", os.Getenv("BUDDY_PIPELINE_ID"), os.Getenv("BUDDY_EXECUTION_ID"))
+	tags[constants.CIPipelineName] = os.Getenv("BUDDY_PIPELINE_NAME")
+	tags[constants.CIPipelineNumber] = os.Getenv("BUDDY_EXECUTION_ID")
+	tags[constants.CIPipelineURL] = os.Getenv("BUDDY_EXECUTION_URL")
+	tags[constants.GitCommitSHA] = os.Getenv("BUDDY_EXECUTION_REVISION")
+	tags[constants.GitRepositoryURL] = os.Getenv("BUDDY_SCM_URL")
+	tags[constants.GitBranch] = os.Getenv("BUDDY_EXECUTION_BRANCH")
+	tags[constants.GitTag] = os.Getenv("BUDDY_EXECUTION_TAG")
+	tags[constants.GitCommitMessage] = os.Getenv("BUDDY_EXECUTION_REVISION_MESSAGE")
+	tags[constants.GitCommitCommitterName] = os.Getenv("BUDDY_EXECUTION_REVISION_COMMITTER_NAME")
+	tags[constants.GitCommitCommitterEmail] = os.Getenv("BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL")
+	return tags
+}
+
+// extractBuildkite extracts CI information specific to Buildkite.
+func extractBuildkite() map[string]string {
+	tags := map[string]string{}
+	tags[constants.GitBranch] = os.Getenv("BUILDKITE_BRANCH")
+	tags[constants.GitCommitSHA] = os.Getenv("BUILDKITE_COMMIT")
+	tags[constants.GitRepositoryURL] = os.Getenv("BUILDKITE_REPO")
+	tags[constants.GitTag] = os.Getenv("BUILDKITE_TAG")
+	tags[constants.CIPipelineID] = os.Getenv("BUILDKITE_BUILD_ID")
+	tags[constants.CIPipelineName] = os.Getenv("BUILDKITE_PIPELINE_SLUG")
+	tags[constants.CIPipelineNumber] = os.Getenv("BUILDKITE_BUILD_NUMBER")
+	tags[constants.CIPipelineURL] = os.Getenv("BUILDKITE_BUILD_URL")
+	tags[constants.CIJobURL] = fmt.Sprintf("%s#%s", os.Getenv("BUILDKITE_BUILD_URL"), os.Getenv("BUILDKITE_JOB_ID"))
+	tags[constants.CIProviderName] = "buildkite"
+	tags[constants.CIWorkspacePath] = os.Getenv("BUILDKITE_BUILD_CHECKOUT_PATH")
+	tags[constants.GitCommitMessage] = os.Getenv("BUILDKITE_MESSAGE")
+	tags[constants.GitCommitAuthorName] = os.Getenv("BUILDKITE_BUILD_AUTHOR")
+	tags[constants.GitCommitAuthorEmail] = os.Getenv("BUILDKITE_BUILD_AUTHOR_EMAIL")
+	tags[constants.CINodeName] = os.Getenv("BUILDKITE_AGENT_ID")
+
+	jsonString, err := getEnvVarsJSON("BUILDKITE_BUILD_ID", "BUILDKITE_JOB_ID")
+	if err == nil {
+		tags[constants.CIEnvVars] = string(jsonString)
+	}
+
+	var extraTags []string
+	envVars := os.Environ()
+	for _, envVar := range envVars {
+		if strings.HasPrefix(envVar, "BUILDKITE_AGENT_META_DATA_") {
+			envVarAsTag := envVar
+			envVarAsTag = strings.TrimPrefix(envVarAsTag, "BUILDKITE_AGENT_META_DATA_")
+			envVarAsTag = strings.ToLower(envVarAsTag)
+			envVarAsTag = strings.Replace(envVarAsTag, "=", ":", 1)
+			extraTags = append(extraTags, envVarAsTag)
+		}
+	}
+
+	if len(extraTags) != 0 {
+		// HACK: Sorting isn't actually needed, but it simplifies testing if the order is consistent
+		sort.Sort(sort.Reverse(sort.StringSlice(extraTags)))
+		jsonString, err = json.Marshal(extraTags)
+		if err == nil {
+			tags[constants.CINodeLabels] = string(jsonString)
+		}
+	}
+
+	return tags
+}
+
+// extractCircleCI extracts CI information specific to CircleCI.
+func extractCircleCI() map[string]string {
+	tags := map[string]string{}
+	tags[constants.CIProviderName] = "circleci"
+	tags[constants.GitRepositoryURL] = os.Getenv("CIRCLE_REPOSITORY_URL")
+	tags[constants.GitCommitSHA] = os.Getenv("CIRCLE_SHA1")
+	tags[constants.GitTag] = os.Getenv("CIRCLE_TAG")
+	tags[constants.GitBranch] = os.Getenv("CIRCLE_BRANCH")
+	tags[constants.CIWorkspacePath] = os.Getenv("CIRCLE_WORKING_DIRECTORY")
+	tags[constants.CIPipelineID] = os.Getenv("CIRCLE_WORKFLOW_ID")
+	tags[constants.CIPipelineName] = os.Getenv("CIRCLE_PROJECT_REPONAME")
+	tags[constants.CIPipelineNumber] = os.Getenv("CIRCLE_BUILD_NUM")
+	tags[constants.CIPipelineURL] = fmt.Sprintf("https://app.circleci.com/pipelines/workflows/%s", os.Getenv("CIRCLE_WORKFLOW_ID"))
+	tags[constants.CIJobName] = os.Getenv("CIRCLE_JOB")
+	tags[constants.CIJobURL] = os.Getenv("CIRCLE_BUILD_URL")
+
+	jsonString, err := getEnvVarsJSON("CIRCLE_BUILD_NUM", "CIRCLE_WORKFLOW_ID")
+	if err == nil {
+		tags[constants.CIEnvVars] = string(jsonString)
+	}
+
+	return tags
+}
+
+// extractGithubActions extracts CI information specific to GitHub Actions.
+func extractGithubActions() map[string]string {
+	tags := map[string]string{}
+	branchOrTag := firstEnv("GITHUB_HEAD_REF", "GITHUB_REF")
+	tag := ""
+	branch := ""
+	if strings.Contains(branchOrTag, "tags/") {
+		tag = branchOrTag
+	} else {
+		branch = branchOrTag
+	}
+
+	serverURL := os.Getenv("GITHUB_SERVER_URL")
+	if serverURL == "" {
+		serverURL = "https://github.com"
+	}
+	serverURL = strings.TrimSuffix(serverURL, "/")
+
+	rawRepository := fmt.Sprintf("%s/%s", serverURL, os.Getenv("GITHUB_REPOSITORY"))
+	pipelineID := os.Getenv("GITHUB_RUN_ID")
+	commitSha := os.Getenv("GITHUB_SHA")
+
+	tags[constants.CIProviderName] = "github"
+	tags[constants.GitRepositoryURL] = rawRepository + ".git"
+	tags[constants.GitCommitSHA] = commitSha
+	tags[constants.GitBranch] = branch
+	tags[constants.GitTag] = tag
+	tags[constants.CIWorkspacePath] = os.Getenv("GITHUB_WORKSPACE")
+	tags[constants.CIPipelineID] = pipelineID
+	tags[constants.CIPipelineNumber] = os.Getenv("GITHUB_RUN_NUMBER")
+	tags[constants.CIPipelineName] = os.Getenv("GITHUB_WORKFLOW")
+	tags[constants.CIJobURL] = fmt.Sprintf("%s/commit/%s/checks", rawRepository, commitSha)
+	tags[constants.CIJobName] = os.Getenv("GITHUB_JOB")
+
+	attempts := os.Getenv("GITHUB_RUN_ATTEMPT")
+	if attempts == "" {
+		tags[constants.CIPipelineURL] = fmt.Sprintf("%s/actions/runs/%s", rawRepository, pipelineID)
+	} else {
+		tags[constants.CIPipelineURL] = fmt.Sprintf("%s/actions/runs/%s/attempts/%s", rawRepository, pipelineID, attempts)
+	}
+
+	jsonString, err := getEnvVarsJSON("GITHUB_SERVER_URL", "GITHUB_REPOSITORY", "GITHUB_RUN_ID", "GITHUB_RUN_ATTEMPT")
+	if err == nil {
+		tags[constants.CIEnvVars] = string(jsonString)
+	}
+
+	return tags
+}
+
+// extractGitlab extracts CI information specific to GitLab.
+func extractGitlab() map[string]string {
+	tags := map[string]string{}
+	url := os.Getenv("CI_PIPELINE_URL")
+
+	tags[constants.CIProviderName] = "gitlab"
+	tags[constants.GitRepositoryURL] = os.Getenv("CI_REPOSITORY_URL")
+	tags[constants.GitCommitSHA] = os.Getenv("CI_COMMIT_SHA")
+	tags[constants.GitBranch] = firstEnv("CI_COMMIT_BRANCH", "CI_COMMIT_REF_NAME")
+	tags[constants.GitTag] = os.Getenv("CI_COMMIT_TAG")
+	tags[constants.CIWorkspacePath] = os.Getenv("CI_PROJECT_DIR")
+	tags[constants.CIPipelineID] = os.Getenv("CI_PIPELINE_ID")
+	tags[constants.CIPipelineName] = os.Getenv("CI_PROJECT_PATH")
+	tags[constants.CIPipelineNumber] = os.Getenv("CI_PIPELINE_IID")
+	tags[constants.CIPipelineURL] = url
+	tags[constants.CIJobURL] = os.Getenv("CI_JOB_URL")
+	tags[constants.CIJobName] = os.Getenv("CI_JOB_NAME")
+	tags[constants.CIStageName] = os.Getenv("CI_JOB_STAGE")
+	tags[constants.GitCommitMessage] = os.Getenv("CI_COMMIT_MESSAGE")
+	tags[constants.CINodeName] = os.Getenv("CI_RUNNER_ID")
+	tags[constants.CINodeLabels] = os.Getenv("CI_RUNNER_TAGS")
+
+	author := os.Getenv("CI_COMMIT_AUTHOR")
+	authorArray := strings.FieldsFunc(author, func(s rune) bool {
+		return s == '<' || s == '>'
+	})
+	tags[constants.GitCommitAuthorName] = strings.TrimSpace(authorArray[0])
+	tags[constants.GitCommitAuthorEmail] = strings.TrimSpace(authorArray[1])
+	tags[constants.GitCommitAuthorDate] = os.Getenv("CI_COMMIT_TIMESTAMP")
+
+	jsonString, err := getEnvVarsJSON("CI_PROJECT_URL", "CI_PIPELINE_ID", "CI_JOB_ID")
+	if err == nil {
+		tags[constants.CIEnvVars] = string(jsonString)
+	}
+
+	return tags
+}
+
+// extractJenkins extracts CI information specific to Jenkins.
+func extractJenkins() map[string]string {
+	tags := map[string]string{}
+	tags[constants.CIProviderName] = "jenkins"
+	tags[constants.GitRepositoryURL] = firstEnv("GIT_URL", "GIT_URL_1")
+	tags[constants.GitCommitSHA] = os.Getenv("GIT_COMMIT")
+
+	branchOrTag := os.Getenv("GIT_BRANCH")
+	empty := []byte("")
+	name, hasName := os.LookupEnv("JOB_NAME")
+
+	if strings.Contains(branchOrTag, "tags/") {
+		tags[constants.GitTag] = branchOrTag
+	} else {
+		tags[constants.GitBranch] = branchOrTag
+		// remove branch for job name
+		removeBranch := regexp.MustCompile(fmt.Sprintf("/%s", normalizeRef(branchOrTag)))
+		name = string(removeBranch.ReplaceAll([]byte(name), empty))
+	}
+
+	if hasName {
+		removeVars := regexp.MustCompile("/[^/]+=[^/]*")
+		name = string(removeVars.ReplaceAll([]byte(name), empty))
+	}
+
+	tags[constants.CIWorkspacePath] = os.Getenv("WORKSPACE")
+	tags[constants.CIPipelineID] = os.Getenv("BUILD_TAG")
+	tags[constants.CIPipelineNumber] = os.Getenv("BUILD_NUMBER")
+	tags[constants.CIPipelineName] = name
+	tags[constants.CIPipelineURL] = os.Getenv("BUILD_URL")
+	tags[constants.CINodeName] = os.Getenv("NODE_NAME")
+
+	jsonString, err := getEnvVarsJSON("DD_CUSTOM_TRACE_ID")
+	if err == nil {
+		tags[constants.CIEnvVars] = string(jsonString)
+	}
+
+	nodeLabels := os.Getenv("NODE_LABELS")
+	if len(nodeLabels) > 0 {
+		labelsArray := strings.Split(nodeLabels, " ")
+		jsonString, err := json.Marshal(labelsArray)
+		if err == nil {
+			tags[constants.CINodeLabels] = string(jsonString)
+		}
+	}
+
+	return tags
+}
+
+// extractTeamcity extracts CI information specific to TeamCity.
+func extractTeamcity() map[string]string {
+	tags := map[string]string{}
+	tags[constants.CIProviderName] = "teamcity"
+	tags[constants.CIJobURL] = os.Getenv("BUILD_URL")
+	tags[constants.CIJobName] = os.Getenv("TEAMCITY_BUILDCONF_NAME")
+	return tags
+}
+
+// extractCodefresh extracts CI information specific to Codefresh.
+func extractCodefresh() map[string]string {
+	tags := map[string]string{}
+	tags[constants.CIProviderName] = "codefresh"
+	tags[constants.CIPipelineID] = os.Getenv("CF_BUILD_ID")
+	tags[constants.CIPipelineName] = os.Getenv("CF_PIPELINE_NAME")
+	tags[constants.CIPipelineURL] = os.Getenv("CF_BUILD_URL")
+	tags[constants.CIJobName] = os.Getenv("CF_STEP_NAME")
+
+	jsonString, err := getEnvVarsJSON("CF_BUILD_ID")
+	if err == nil {
+		tags[constants.CIEnvVars] = string(jsonString)
+	}
+
+	cfBranch := os.Getenv("CF_BRANCH")
+	isTag := strings.Contains(cfBranch, "tags/")
+	var refKey string
+	if isTag {
+		refKey = constants.GitTag
+	} else {
+		refKey = constants.GitBranch
+	}
+	tags[refKey] = normalizeRef(cfBranch)
+
+	return tags
+}
+
+// extractTravis extracts CI information specific to Travis CI.
+func extractTravis() map[string]string {
+	tags := map[string]string{}
+	prSlug := os.Getenv("TRAVIS_PULL_REQUEST_SLUG")
+	repoSlug := prSlug
+	if strings.TrimSpace(repoSlug) == "" {
+		repoSlug = os.Getenv("TRAVIS_REPO_SLUG")
+	}
+	tags[constants.CIProviderName] = "travisci"
+	tags[constants.GitRepositoryURL] = fmt.Sprintf("https://github.com/%s.git", repoSlug)
+	tags[constants.GitCommitSHA] = os.Getenv("TRAVIS_COMMIT")
+	tags[constants.GitTag] = os.Getenv("TRAVIS_TAG")
+	tags[constants.GitBranch] = firstEnv("TRAVIS_PULL_REQUEST_BRANCH", "TRAVIS_BRANCH")
+	tags[constants.CIWorkspacePath] = os.Getenv("TRAVIS_BUILD_DIR")
+	tags[constants.CIPipelineID] = os.Getenv("TRAVIS_BUILD_ID")
+	tags[constants.CIPipelineNumber] = os.Getenv("TRAVIS_BUILD_NUMBER")
+	tags[constants.CIPipelineName] = repoSlug
+	tags[constants.CIPipelineURL] = os.Getenv("TRAVIS_BUILD_WEB_URL")
+	tags[constants.CIJobURL] = os.Getenv("TRAVIS_JOB_WEB_URL")
+	tags[constants.GitCommitMessage] = os.Getenv("TRAVIS_COMMIT_MESSAGE")
+	return tags
+}
+
+// extractAwsCodePipeline extracts CI information specific to AWS CodePipeline.
+func extractAwsCodePipeline() map[string]string {
+	tags := map[string]string{}
+
+	if !strings.HasPrefix(os.Getenv("CODEBUILD_INITIATOR"), "codepipeline") {
+		// CODEBUILD_INITIATOR is defined but this is not a codepipeline build
+		return tags
+	}
+
+	tags[constants.CIProviderName] = "awscodepipeline"
+	tags[constants.CIPipelineID] = os.Getenv("DD_PIPELINE_EXECUTION_ID")
+
+	jsonString, err := getEnvVarsJSON("CODEBUILD_BUILD_ARN", "DD_ACTION_EXECUTION_ID", "DD_PIPELINE_EXECUTION_ID")
+	if err == nil {
+		tags[constants.CIEnvVars] = string(jsonString)
+	}
+
+	return tags
+}
diff --git a/main/civisibility/utils/ci_providers_test.go b/main/civisibility/utils/ci_providers_test.go
new file mode 100644
index 0000000..b85acc1
--- /dev/null
+++ b/main/civisibility/utils/ci_providers_test.go
@@ -0,0 +1,116 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package utils
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"os"
+	"path/filepath"
+	"strings"
+	"testing"
+)
+
+func setEnvs(t *testing.T, env map[string]string) {
+	for key, value := range env {
+		t.Setenv(key, value)
+	}
+}
+
+func sortJSONKeys(jsonStr string) string {
+	tmp := map[string]string{}
+	_ = json.Unmarshal([]byte(jsonStr), &tmp)
+	jsonBytes, _ := json.Marshal(tmp)
+	return string(jsonBytes)
+}
+
+// TestTags asserts that all tags are extracted from environment variables.
+func TestTags(t *testing.T) {
+	// Reset provider env key when running in CI
+	resetProviders := map[string]string{}
+	for key := range providers {
+		if value, ok := os.LookupEnv(key); ok {
+			resetProviders[key] = value
+			_ = os.Unsetenv(key)
+		}
+	}
+	defer func() {
+		for key, value := range resetProviders {
+			_ = os.Setenv(key, value)
+		}
+	}()
+
+	paths, err := filepath.Glob("testdata/fixtures/providers/*.json")
+	if err != nil {
+		t.Fatal(err)
+	}
+	for _, path := range paths {
+		providerName := strings.TrimSuffix(filepath.Base(path), ".json")
+
+		t.Run(providerName, func(t *testing.T) {
+			fp, err := os.Open(path)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			data, err := io.ReadAll(fp)
+			if err != nil {
+				t.Fatal(err)
+			}
+
+			var examples [][]map[string]string
+			if err := json.Unmarshal(data, &examples); err != nil {
+				t.Fatal(err)
+			}
+
+			for i, line := range examples {
+				name := fmt.Sprintf("%d", i)
+				env := line[0]
+				tags := line[1]
+
+				// Because we have a fallback algorithm for some variables
+				// we need to initialize some of them to not use the one set by the github action running this test.
+				if providerName == "github" {
+					// We initialize GITHUB_RUN_ATTEMPT if it doesn't exist to avoid using the one set in the GitHub action.
+					if _, ok := env["GITHUB_RUN_ATTEMPT"]; !ok {
+						env["GITHUB_RUN_ATTEMPT"] = ""
+					}
+					// We initialize GITHUB_HEAD_REF if it doesn't exist to avoid using the one set in the GitHub action.
+					if _, ok := env["GITHUB_HEAD_REF"]; !ok {
+						env["GITHUB_HEAD_REF"] = ""
+					}
+					// We initialize GITHUB_REF if it doesn't exist to avoid using the one set in the GitHub action.
+					if _, ok := env["GITHUB_REF"]; !ok {
+						env["GITHUB_REF"] = ""
+					}
+				}
+
+				t.Run(name, func(t *testing.T) {
+					setEnvs(t, env)
+					providerTags := getProviderTags()
+
+					for expectedKey, expectedValue := range tags {
+						if actualValue, ok := providerTags[expectedKey]; ok {
+							if expectedKey == "_dd.ci.env_vars" {
+								expectedValue = sortJSONKeys(expectedValue)
+							}
+							if expectedValue != actualValue {
+								if expectedValue == strings.ReplaceAll(actualValue, "\\", "/") {
+									continue
+								}
+
+								t.Fatalf("Key: %s, the actual value (%s) is different to the expected value (%s)", expectedKey, actualValue, expectedValue)
+							}
+						} else {
+							t.Fatalf("Key: %s, doesn't exist.", expectedKey)
+						}
+					}
+				})
+			}
+		})
+	}
+}
diff --git a/main/civisibility/utils/codeowners.go b/main/civisibility/utils/codeowners.go
new file mode 100644
index 0000000..228bacb
--- /dev/null
+++ b/main/civisibility/utils/codeowners.go
@@ -0,0 +1,300 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package utils
+
+import (
+	"bufio"
+	"errors"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+	"sync"
+
+	"ci-visibility-test-github/main/civisibility/constants"
+)
+
+// This is a port of https://github.com/DataDog/dd-trace-dotnet/blob/v2.53.0/tracer/src/Datadog.Trace/Ci/CodeOwners.cs
+
+type (
+	// CodeOwners represents a structured data type that holds sections of code owners.
+	// Each section maps to a slice of entries, where each entry includes a pattern and a list of owners.
+	CodeOwners struct {
+		Sections []*Section
+	}
+
+	// Section represents a block of structured data of multiple entries in a single section
+	Section struct {
+		Name    string
+		Entries []Entry
+	}
+
+	// Entry represents a single entry in a CODEOWNERS file.
+	// It includes the pattern for matching files, the list of owners, and the section to which it belongs.
+	Entry struct {
+		Pattern string
+		Owners  []string
+		Section string
+	}
+)
+
+var (
+	// codeowners holds the parsed CODEOWNERS file data.
+	codeowners      *CodeOwners
+	codeownersMutex sync.Mutex
+)
+
+// GetCodeOwners retrieves and caches the CODEOWNERS data.
+// It looks for the CODEOWNERS file in various standard locations within the CI workspace.
+// This function is thread-safe due to the use of a mutex.
+//
+// Returns:
+//
+//	A pointer to a CodeOwners struct containing the parsed CODEOWNERS data, or nil if not found.
+func GetCodeOwners() *CodeOwners {
+	codeownersMutex.Lock()
+	defer codeownersMutex.Unlock()
+
+	if codeowners != nil {
+		return codeowners
+	}
+
+	tags := GetCITags()
+	if v, ok := tags[constants.CIWorkspacePath]; ok {
+		paths := []string{
+			filepath.Join(v, "CODEOWNERS"),
+			filepath.Join(v, ".github", "CODEOWNERS"),
+			filepath.Join(v, ".gitlab", "CODEOWNERS"),
+			filepath.Join(v, ".docs", "CODEOWNERS"),
+		}
+		for _, path := range paths {
+			if _, err := os.Stat(path); err == nil {
+				codeowners, err = NewCodeOwners(path)
+				if err == nil {
+					return codeowners
+				}
+			}
+		}
+	}
+
+	return nil
+}
+
+// NewCodeOwners creates a new instance of CodeOwners by parsing a CODEOWNERS file located at the given filePath.
+// It returns an error if the file cannot be read or parsed properly.
+func NewCodeOwners(filePath string) (*CodeOwners, error) {
+	if filePath == "" {
+		return nil, fmt.Errorf("filePath cannot be empty")
+	}
+
+	file, err := os.Open(filePath)
+	if err != nil {
+		return nil, err
+	}
+	defer func() {
+		err = file.Close()
+		if err != nil && !errors.Is(os.ErrClosed, err) {
+		}
+	}()
+
+	var entriesList []Entry
+	var sectionsList []string
+	var currentSectionName string
+
+	scanner := bufio.NewScanner(file)
+	for scanner.Scan() {
+		line := scanner.Text()
+		if len(line) == 0 || line[0] == '#' {
+			continue
+		}
+
+		// Identify section headers, which are lines enclosed in square brackets
+		if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
+			currentSectionName = line[1 : len(line)-1]
+			foundSectionName := findSectionIgnoreCase(sectionsList, currentSectionName)
+			if foundSectionName == "" {
+				sectionsList = append(sectionsList, currentSectionName)
+			} else {
+				currentSectionName = foundSectionName
+			}
+			continue
+		}
+
+		finalLine := line
+		var ownersList []string
+		terms := strings.Fields(line)
+		for _, term := range terms {
+			if len(term) == 0 {
+				continue
+			}
+
+			// Identify owners by their prefixes (either @ for usernames or containing @ for emails)
+			if term[0] == '@' || strings.Contains(term, "@") {
+				ownersList = append(ownersList, term)
+				pos := strings.Index(finalLine, term)
+				if pos > 0 {
+					finalLine = finalLine[:pos] + finalLine[pos+len(term):]
+				}
+			}
+		}
+
+		finalLine = strings.TrimSpace(finalLine)
+		if len(finalLine) == 0 {
+			continue
+		}
+
+		entriesList = append(entriesList, Entry{Pattern: finalLine, Owners: ownersList, Section: currentSectionName})
+	}
+
+	if err := scanner.Err(); err != nil {
+		return nil, err
+	}
+
+	// Reverse the entries list to maintain the order of appearance in the file
+	for i, j := 0, len(entriesList)-1; i < j; i, j = i+1, j-1 {
+		entriesList[i], entriesList[j] = entriesList[j], entriesList[i]
+	}
+
+	codeOwners := &CodeOwners{}
+	for _, entry := range entriesList {
+		var section *Section
+		for _, val := range codeOwners.Sections {
+			if val.Name == entry.Section {
+				section = val
+				break
+			}
+		}
+
+		if section == nil {
+			section = &Section{Name: entry.Section, Entries: []Entry{}}
+			codeOwners.Sections = append(codeOwners.Sections, section)
+		}
+
+		section.Entries = append(section.Entries, entry)
+	}
+
+	return codeOwners, nil
+}
+
+// findSectionIgnoreCase searches for a section name in a case-insensitive manner.
+// It returns the section name if found, otherwise returns an empty string.
+func findSectionIgnoreCase(sections []string, section string) string {
+	sectionLower := strings.ToLower(section)
+	for _, s := range sections {
+		if strings.ToLower(s) == sectionLower {
+			return s
+		}
+	}
+	return ""
+}
+
+// GetSection gets the first Section entry in the CodeOwners that matches the section name.
+// It returns a pointer to the matched entry, or nil if no match is found
+func (co *CodeOwners) GetSection(section string) *Section {
+	for _, value := range co.Sections {
+		if value.Name == section {
+			return value
+		}
+	}
+
+	return nil
+}
+
+// Match finds the first entry in the CodeOwners that matches the given value.
+// It returns a pointer to the matched entry, or nil if no match is found.
+func (co *CodeOwners) Match(value string) (*Entry, bool) {
+	var matchedEntries []Entry
+
+	for _, section := range co.Sections {
+		for _, entry := range section.Entries {
+			pattern := entry.Pattern
+			finalPattern := pattern
+
+			var includeAnythingBefore, includeAnythingAfter bool
+
+			if strings.HasPrefix(pattern, "/") {
+				includeAnythingBefore = false
+			} else {
+				if strings.HasPrefix(finalPattern, "*") {
+					finalPattern = finalPattern[1:]
+				}
+				includeAnythingBefore = true
+			}
+
+			if strings.HasSuffix(pattern, "/") {
+				includeAnythingAfter = true
+			} else if strings.HasSuffix(pattern, "/*") {
+				includeAnythingAfter = true
+				finalPattern = finalPattern[:len(finalPattern)-1]
+			} else {
+				includeAnythingAfter = false
+			}
+
+			if includeAnythingAfter {
+				found := includeAnythingBefore && strings.Contains(value, finalPattern) || strings.HasPrefix(value, finalPattern)
+				if !found {
+					continue
+				}
+
+				if !strings.HasSuffix(pattern, "/*") {
+					matchedEntries = append(matchedEntries, entry)
+					break
+				}
+
+				patternEnd := strings.Index(value, finalPattern)
+				if patternEnd != -1 {
+					patternEnd += len(finalPattern)
+					remainingString := value[patternEnd:]
+					if strings.Index(remainingString, "/") == -1 {
+						matchedEntries = append(matchedEntries, entry)
+						break
+					}
+				}
+			} else {
+				if includeAnythingBefore {
+					if strings.HasSuffix(value, finalPattern) {
+						matchedEntries = append(matchedEntries, entry)
+						break
+					}
+				} else if value == finalPattern {
+					matchedEntries = append(matchedEntries, entry)
+					break
+				}
+			}
+		}
+	}
+
+	switch len(matchedEntries) {
+	case 0:
+		return nil, false
+	case 1:
+		return &matchedEntries[0], true
+	default:
+		patterns := make([]string, 0)
+		owners := make([]string, 0)
+		sections := make([]string, 0)
+		for _, entry := range matchedEntries {
+			patterns = append(patterns, entry.Pattern)
+			owners = append(owners, entry.Owners...)
+			sections = append(sections, entry.Section)
+		}
+		return &Entry{
+			Pattern: strings.Join(patterns, " | "),
+			Owners:  owners,
+			Section: strings.Join(sections, " | "),
+		}, true
+	}
+}
+
+// GetOwnersString returns a formatted string of the owners list in an Entry.
+// It returns an empty string if there are no owners.
+func (e Entry) GetOwnersString() string {
+	if e.Owners == nil || len(e.Owners) == 0 {
+		return ""
+	}
+
+	return "[\"" + strings.Join(e.Owners, "\",\"") + "\"]"
+}
diff --git a/main/civisibility/utils/codeowners_test.go b/main/civisibility/utils/codeowners_test.go
new file mode 100644
index 0000000..95852be
--- /dev/null
+++ b/main/civisibility/utils/codeowners_test.go
@@ -0,0 +1,159 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package utils
+
+import (
+	"os"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewCodeOwners(t *testing.T) {
+	// Create a temporary file for testing
+	fileContent := `[Section 1]
+/path/to/file @owner1 @owner2
+/path/to/* @owner3
+
+[Section 2]
+/another/path @owner4
+`
+
+	tmpFile, err := os.CreateTemp("", "CODEOWNERS")
+	assert.NoError(t, err)
+	defer os.Remove(tmpFile.Name())
+
+	_, err = tmpFile.WriteString(fileContent)
+	assert.NoError(t, err)
+
+	err = tmpFile.Close()
+	assert.NoError(t, err)
+
+	// Test NewCodeOwners
+	codeOwners, err := NewCodeOwners(tmpFile.Name())
+	assert.NoError(t, err)
+	assert.NotNil(t, codeOwners)
+	assert.Equal(t, 2, len(codeOwners.Sections))
+	assert.Equal(t, 2, len(codeOwners.GetSection("Section 1").Entries))
+	assert.Equal(t, 1, len(codeOwners.GetSection("Section 2").Entries))
+
+	// Test empty file path
+	_, err = NewCodeOwners("")
+	assert.Error(t, err)
+}
+
+func TestFindSectionIgnoreCase(t *testing.T) {
+	sections := []string{"Section1", "section2", "SECTION3"}
+	assert.Equal(t, "Section1", findSectionIgnoreCase(sections, "section1"))
+	assert.Equal(t, "section2", findSectionIgnoreCase(sections, "SECTION2"))
+	assert.Equal(t, "SECTION3", findSectionIgnoreCase(sections, "Section3"))
+	assert.Equal(t, "", findSectionIgnoreCase(sections, "Section4"))
+}
+
+func TestMatch(t *testing.T) {
+	entries := []Entry{
+		{Pattern: "/path/to/file", Owners: []string{"@owner1", "@owner2"}, Section: "Section 1"},
+		{Pattern: "/path/to/*", Owners: []string{"@owner3"}, Section: "Section 1"},
+		{Pattern: "/another/path", Owners: []string{"@owner4"}, Section: "Section 2"},
+	}
+	sections := []*Section{
+		{Name: "Section 1", Entries: []Entry{entries[0], entries[1]}},
+		{Name: "Section 2", Entries: []Entry{entries[2]}},
+	}
+
+	codeOwners := &CodeOwners{Sections: sections}
+
+	// Test exact match
+	entry, ok := codeOwners.Match("/path/to/file")
+	assert.True(t, ok)
+	assert.Equal(t, entries[0], *entry)
+
+	// Test wildcard match
+	entry, ok = codeOwners.Match("/path/to/anything")
+	assert.True(t, ok)
+	assert.Equal(t, entries[1], *entry)
+
+	// Test no match
+	entry, ok = codeOwners.Match("/no/match")
+	assert.False(t, ok)
+}
+
+func TestGetOwnersString(t *testing.T) {
+	entry := Entry{Owners: []string{"@owner1", "@owner2"}}
+	assert.Equal(t, "[\"@owner1\",\"@owner2\"]", entry.GetOwnersString())
+
+	entry = Entry{}
+	assert.Equal(t, "", entry.GetOwnersString())
+}
+
+func TestGithubCodeOwners(t *testing.T) {
+	cOwners, err := NewCodeOwners("testdata/fixtures/codeowners/CODEOWNERS_GITHUB")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if cOwners == nil {
+		t.Fatal("nil codeowners")
+	}
+
+	data := []struct {
+		value    string
+		expected string
+	}{
+		{value: "unexistent/path/test.cs", expected: "[\"@global-owner1\",\"@global-owner2\"]"},
+		{value: "apps/test.cs", expected: "[\"@octocat\"]"},
+		{value: "/example/apps/test.cs", expected: "[\"@octocat\"]"},
+		{value: "/docs/test.cs", expected: "[\"@doctocat\"]"},
+		{value: "/examples/docs/test.cs", expected: "[\"docs@example.com\"]"},
+		{value: "/src/vendor/match.go", expected: "[\"docs@example.com\"]"},
+		{value: "/examples/docs/inside/test.cs", expected: "[\"@global-owner1\",\"@global-owner2\"]"},
+		{value: "/component/path/test.js", expected: "[\"@js-owner\"]"},
+		{value: "/mytextbox.txt", expected: "[\"@octo-org/octocats\"]"},
+		{value: "/scripts/artifacts/value.js", expected: "[\"@doctocat\",\"@octocat\"]"},
+		{value: "/apps/octo/test.cs", expected: "[\"@octocat\"]"},
+		{value: "/apps/github", expected: ""},
+	}
+
+	for _, item := range data {
+		t.Run(strings.ReplaceAll(item.value, "/", "_"), func(t *testing.T) {
+			match, ok := cOwners.Match(item.value)
+			assert.True(t, ok)
+			assert.EqualValues(t, item.expected, match.GetOwnersString())
+		})
+	}
+}
+
+func TestGitlabCodeOwners(t *testing.T) {
+	cOwners, err := NewCodeOwners("testdata/fixtures/codeowners/CODEOWNERS_GITLAB")
+	if err != nil {
+		t.Fatal(err)
+	}
+	if cOwners == nil {
+		t.Fatal("nil codeowners")
+	}
+
+	data := []struct {
+		value    string
+		expected string
+	}{
+		{value: "apps/README.md", expected: "[\"@docs\",\"@database\",\"@multiple\",\"@code\",\"@owners\"]"},
+		{value: "model/db", expected: "[\"@database\",\"@multiple\",\"@code\",\"@owners\"]"},
+		{value: "/config/data.conf", expected: "[\"@config-owner\"]"},
+		{value: "/docs/root.md", expected: "[\"@root-docs\"]"},
+		{value: "/docs/sub/root.md", expected: "[\"@all-docs\"]"},
+		{value: "/src/README", expected: "[\"@group\",\"@group/with-nested/subgroup\"]"},
+		{value: "/src/lib/internal.h", expected: "[\"@lib-owner\"]"},
+		{value: "src/ee/docs", expected: "[\"@docs\",\"@multiple\",\"@code\",\"@owners\"]"},
+	}
+
+	for _, item := range data {
+		t.Run(strings.ReplaceAll(item.value, "/", "_"), func(t *testing.T) {
+			match, ok := cOwners.Match(item.value)
+			assert.True(t, ok)
+			assert.EqualValues(t, item.expected, match.GetOwnersString())
+		})
+	}
+}
diff --git a/main/civisibility/utils/environmentTags.go b/main/civisibility/utils/environmentTags.go
new file mode 100644
index 0000000..5c36405
--- /dev/null
+++ b/main/civisibility/utils/environmentTags.go
@@ -0,0 +1,120 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package utils
+
+import (
+	"path/filepath"
+	"runtime"
+	"sync"
+
+	"ci-visibility-test-github/main/civisibility/constants"
+
+	"ci-visibility-test-github/main/osinfo"
+)
+
+var (
+	// ciTags holds the CI/CD environment variable information.
+	ciTags      map[string]string
+	ciTagsMutex sync.Mutex
+)
+
+// GetCITags retrieves and caches the CI/CD tags from environment variables.
+// It initializes the ciTags map if it is not already initialized.
+// This function is thread-safe due to the use of a mutex.
+//
+// Returns:
+//
+//	A map[string]string containing the CI/CD tags.
+func GetCITags() map[string]string {
+	ciTagsMutex.Lock()
+	defer ciTagsMutex.Unlock()
+
+	if ciTags == nil {
+		ciTags = createCITagsMap()
+	}
+
+	return ciTags
+}
+
+// GetRelativePathFromCITagsSourceRoot calculates the relative path from the CI workspace root to the specified path.
+// If the CI workspace root is not available in the tags, it returns the original path.
+//
+// Parameters:
+//
+//	path - The absolute or relative file path for which the relative path should be calculated.
+//
+// Returns:
+//
+//	The relative path from the CI workspace root to the specified path, or the original path if an error occurs.
+func GetRelativePathFromCITagsSourceRoot(path string) string {
+	tags := GetCITags()
+	if v, ok := tags[constants.CIWorkspacePath]; ok {
+		relPath, err := filepath.Rel(v, path)
+		if err == nil {
+			return filepath.ToSlash(relPath)
+		}
+	}
+
+	return path
+}
+
+// createCITagsMap creates a map of CI/CD tags by extracting information from environment variables and the local Git repository.
+// It also adds OS and runtime information to the tags.
+//
+// Returns:
+//
+//	A map[string]string containing the extracted CI/CD tags.
+func createCITagsMap() map[string]string {
+	localTags := getProviderTags()
+	localTags[constants.OSPlatform] = runtime.GOOS
+	localTags[constants.OSVersion] = osinfo.OSVersion()
+	localTags[constants.OSArchitecture] = runtime.GOARCH
+	localTags[constants.RuntimeName] = runtime.Compiler
+	localTags[constants.RuntimeVersion] = runtime.Version()
+
+	gitData, _ := getLocalGitData()
+
+	// Populate Git metadata from the local Git repository if not already present in localTags
+	if _, ok := localTags[constants.CIWorkspacePath]; !ok {
+		localTags[constants.CIWorkspacePath] = gitData.SourceRoot
+	}
+	if _, ok := localTags[constants.GitRepositoryURL]; !ok {
+		localTags[constants.GitRepositoryURL] = gitData.RepositoryURL
+	}
+	if _, ok := localTags[constants.GitCommitSHA]; !ok {
+		localTags[constants.GitCommitSHA] = gitData.CommitSha
+	}
+	if _, ok := localTags[constants.GitBranch]; !ok {
+		localTags[constants.GitBranch] = gitData.Branch
+	}
+
+	// If the commit SHA matches, populate additional Git metadata
+	if localTags[constants.GitCommitSHA] == gitData.CommitSha {
+		if _, ok := localTags[constants.GitCommitAuthorDate]; !ok {
+			localTags[constants.GitCommitAuthorDate] = gitData.AuthorDate.String()
+		}
+		if _, ok := localTags[constants.GitCommitAuthorName]; !ok {
+			localTags[constants.GitCommitAuthorName] = gitData.AuthorName
+		}
+		if _, ok := localTags[constants.GitCommitAuthorEmail]; !ok {
+			localTags[constants.GitCommitAuthorEmail] = gitData.AuthorEmail
+		}
+		if _, ok := localTags[constants.GitCommitCommitterDate]; !ok {
+			localTags[constants.GitCommitCommitterDate] = gitData.CommitterDate.String()
+		}
+		if _, ok := localTags[constants.GitCommitCommitterName]; !ok {
+			localTags[constants.GitCommitCommitterName] = gitData.CommitterName
+		}
+		if _, ok := localTags[constants.GitCommitCommitterEmail]; !ok {
+			localTags[constants.GitCommitCommitterEmail] = gitData.CommitterEmail
+		}
+		if _, ok := localTags[constants.GitCommitMessage]; !ok {
+			localTags[constants.GitCommitMessage] = gitData.CommitMessage
+		}
+	}
+
+	return localTags
+}
diff --git a/main/civisibility/utils/environmentTags_test.go b/main/civisibility/utils/environmentTags_test.go
new file mode 100644
index 0000000..ee4b200
--- /dev/null
+++ b/main/civisibility/utils/environmentTags_test.go
@@ -0,0 +1,40 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package utils
+
+import (
+	"testing"
+
+	"ci-visibility-test-github/main/civisibility/constants"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetCITagsCache(t *testing.T) {
+	ciTags = map[string]string{"key": "value"}
+
+	// First call to initialize ciTags
+	tags := GetCITags()
+	assert.Equal(t, "value", tags["key"])
+
+	tags["key"] = "newvalue"
+	tags = GetCITags()
+	assert.Equal(t, "newvalue", tags["key"])
+}
+
+func TestGetRelativePathFromCITagsSourceRoot(t *testing.T) {
+	ciTags = map[string]string{constants.CIWorkspacePath: "/ci/workspace"}
+	absPath := "/ci/workspace/subdir/file.txt"
+	expectedRelPath := "subdir/file.txt"
+
+	relPath := GetRelativePathFromCITagsSourceRoot(absPath)
+	assert.Equal(t, expectedRelPath, relPath)
+
+	// Test case when CIWorkspacePath is not set in ciTags
+	ciTags = map[string]string{}
+	relPath = GetRelativePathFromCITagsSourceRoot(absPath)
+	assert.Equal(t, absPath, relPath)
+}
diff --git a/main/civisibility/utils/git.go b/main/civisibility/utils/git.go
new file mode 100644
index 0000000..fb62a37
--- /dev/null
+++ b/main/civisibility/utils/git.go
@@ -0,0 +1,104 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package utils
+
+import (
+	"errors"
+	"os/exec"
+	"regexp"
+	"strconv"
+	"strings"
+	"time"
+)
+
+// localGitData holds various pieces of information about the local Git repository,
+// including the source root, repository URL, branch, commit SHA, author and committer details, and commit message.
+type localGitData struct {
+	SourceRoot     string
+	RepositoryURL  string
+	Branch         string
+	CommitSha      string
+	AuthorDate     time.Time
+	AuthorName     string
+	AuthorEmail    string
+	CommitterDate  time.Time
+	CommitterName  string
+	CommitterEmail string
+	CommitMessage  string
+}
+
+// regexpSensitiveInfo is a regular expression used to match and filter out sensitive information from URLs.
+var regexpSensitiveInfo = regexp.MustCompile("(https?://|ssh?://)[^/]*@")
+
+// getLocalGitData retrieves information about the local Git repository from the current HEAD.
+// It gathers details such as the repository URL, current branch, latest commit SHA, author and committer details, and commit message.
+//
+// Returns:
+//
+//	A localGitData struct populated with the retrieved Git data.
+//	An error if any Git command fails or the retrieved data is incomplete.
+func getLocalGitData() (localGitData, error) {
+	gitData := localGitData{}
+
+	// Extract the absolute path to the Git directory
+	out, err := exec.Command("git", "rev-parse", "--absolute-git-dir").Output()
+	if err == nil {
+		gitData.SourceRoot = strings.ReplaceAll(strings.Trim(string(out), "\n"), ".git", "")
+	}
+
+	// Extract the repository URL
+	out, err = exec.Command("git", "ls-remote", "--get-url").Output()
+	if err == nil {
+		gitData.RepositoryURL = filterSensitiveInfo(strings.Trim(string(out), "\n"))
+	}
+
+	// Extract the current branch name
+	out, err = exec.Command("git", "rev-parse", "--abbrev-ref", "HEAD").Output()
+	if err == nil {
+		gitData.Branch = strings.Trim(string(out), "\n")
+	}
+
+	// Get commit details from the latest commit using git log (git log -1 --pretty='%H","%aI","%an","%ae","%cI","%cn","%ce","%B')
+	out, err = exec.Command("git", "log", "-1", "--pretty=%H\",\"%at\",\"%an\",\"%ae\",\"%ct\",\"%cn\",\"%ce\",\"%B").Output()
+	if err != nil {
+		return gitData, err
+	}
+
+	// Split the output into individual components
+	outArray := strings.Split(string(out), "\",\"")
+	if len(outArray) < 8 {
+		return gitData, errors.New("git log failed")
+	}
+
+	// Parse author and committer dates from Unix timestamp
+	authorUnixDate, _ := strconv.ParseInt(outArray[1], 10, 64)
+	committerUnixDate, _ := strconv.ParseInt(outArray[4], 10, 64)
+
+	// Populate the localGitData struct with the parsed information
+	gitData.CommitSha = outArray[0]
+	gitData.AuthorDate = time.Unix(authorUnixDate, 0)
+	gitData.AuthorName = outArray[2]
+	gitData.AuthorEmail = outArray[3]
+	gitData.CommitterDate = time.Unix(committerUnixDate, 0)
+	gitData.CommitterName = outArray[5]
+	gitData.CommitterEmail = outArray[6]
+	gitData.CommitMessage = strings.Trim(outArray[7], "\n")
+	return gitData, nil
+}
+
+// filterSensitiveInfo removes sensitive information from a given URL using a regular expression.
+// It replaces the user credentials part of the URL (if present) with an empty string.
+//
+// Parameters:
+//
+//	url - The URL string from which sensitive information should be filtered out.
+//
+// Returns:
+//
+//	The sanitized URL string with sensitive information removed.
+func filterSensitiveInfo(url string) string {
+	return string(regexpSensitiveInfo.ReplaceAll([]byte(url), []byte("$1"))[:])
+}
diff --git a/main/civisibility/utils/git_test.go b/main/civisibility/utils/git_test.go
new file mode 100644
index 0000000..fd91ebe
--- /dev/null
+++ b/main/civisibility/utils/git_test.go
@@ -0,0 +1,62 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package utils
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestFilterSensitiveInfo(t *testing.T) {
+	tests := []struct {
+		input    string
+		expected string
+	}{
+		// Basic cases
+		{"https://user:pass@github.com/repo.git", "https://github.com/repo.git"},
+		{"ssh://user@github.com/repo.git", "ssh://github.com/repo.git"},
+		{"https://github.com/repo.git", "https://github.com/repo.git"},
+		{"http://user:pass@github.com/repo.git", "http://github.com/repo.git"},
+
+		// Edge cases
+		{"", ""},
+		{"https://@github.com/repo.git", "https://github.com/repo.git"},
+		{"ftp://user@github.com/repo.git", "ftp://user@github.com/repo.git"}, // Unsupported protocol, should remain unchanged
+		{"user@github.com/repo.git", "user@github.com/repo.git"},             // No protocol, should remain unchanged
+
+		// Complex cases
+		{"https://user:pass@github.com:8080/repo.git", "https://github.com:8080/repo.git"},
+		{"ssh://user:auth@github.com/repo.git", "ssh://github.com/repo.git"},
+		{"https://user:password@bitbucket.org/repo.git", "https://bitbucket.org/repo.git"},
+
+		// Cases with special characters
+		{"https://user:pa$$word@github.com/repo.git", "https://github.com/repo.git"},
+		{"ssh://user!@github.com/repo.git", "ssh://github.com/repo.git"},
+		{"https://user%40example.com@github.com/repo.git", "https://github.com/repo.git"}, // Encoded @ in username
+	}
+
+	for _, test := range tests {
+		result := filterSensitiveInfo(test.input)
+		assert.Equal(t, test.expected, result, "Failed for input: %s", test.input)
+	}
+}
+
+func TestGetLocalGitData(t *testing.T) {
+	data, err := getLocalGitData()
+
+	assert.NoError(t, err)
+	assert.NotEmpty(t, data.SourceRoot)
+	assert.NotEmpty(t, data.RepositoryURL)
+	assert.NotEmpty(t, data.CommitSha)
+	assert.NotEmpty(t, data.AuthorName)
+	assert.NotEmpty(t, data.AuthorEmail)
+	assert.NotEmpty(t, data.AuthorDate)
+	assert.NotEmpty(t, data.CommitterName)
+	assert.NotEmpty(t, data.CommitterEmail)
+	assert.NotEmpty(t, data.CommitterDate)
+	assert.NotEmpty(t, data.CommitMessage)
+}
diff --git a/main/civisibility/utils/home.go b/main/civisibility/utils/home.go
new file mode 100644
index 0000000..8625010
--- /dev/null
+++ b/main/civisibility/utils/home.go
@@ -0,0 +1,120 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package utils
+
+import (
+	"bytes"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"runtime"
+	"strconv"
+	"strings"
+)
+
+// This code is based on: https://github.com/mitchellh/go-homedir/blob/v1.1.0/homedir.go (MIT License)
+
+// ExpandPath expands a file path that starts with '~' to the user's home directory.
+// If the path does not start with '~', it is returned unchanged.
+//
+// Parameters:
+//
+//	path - The file path to be expanded.
+//
+// Returns:
+//
+//	The expanded file path, with '~' replaced by the user's home directory, if applicable.
+func ExpandPath(path string) string {
+	if len(path) == 0 || path[0] != '~' {
+		return path
+	}
+
+	// If the second character is not '/' or '\', return the path unchanged
+	if len(path) > 1 && path[1] != '/' && path[1] != '\\' {
+		return path
+	}
+
+	homeFolder := getHomeDir()
+	if len(homeFolder) > 0 {
+		return filepath.Join(homeFolder, path[1:])
+	}
+
+	return path
+}
+
+// getHomeDir returns the home directory of the current user.
+// The method used to determine the home directory depends on the operating system.
+//
+// On Windows, it prefers the HOME environment variable, then USERPROFILE, and finally combines HOMEDRIVE and HOMEPATH.
+// On Unix-like systems, it prefers the HOME environment variable, and falls back to various shell commands
+// to determine the home directory if necessary.
+//
+// Returns:
+//
+//	The home directory of the current user.
+func getHomeDir() string {
+	if runtime.GOOS == "windows" {
+		if home := os.Getenv("HOME"); home != "" {
+			// First prefer the HOME environment variable
+			return home
+		}
+		if userProfile := os.Getenv("USERPROFILE"); userProfile != "" {
+			// Prefer the USERPROFILE environment variable
+			return userProfile
+		}
+
+		homeDrive := os.Getenv("HOMEDRIVE")
+		homePath := os.Getenv("HOMEPATH")
+		return homeDrive + homePath
+	}
+
+	homeEnv := "HOME"
+	if runtime.GOOS == "plan9" {
+		// On plan9, environment variables are lowercase.
+		homeEnv = "home"
+	}
+
+	if home := os.Getenv(homeEnv); home != "" {
+		// Prefer the HOME environment variable
+		return home
+	}
+
+	var stdout bytes.Buffer
+	if runtime.GOOS == "darwin" {
+		// On macOS, use dscl to read the NFSHomeDirectory
+		cmd := exec.Command("sh", "-c", `dscl -q . -read /Users/"$(whoami)" NFSHomeDirectory | sed 's/^[^ ]*: //'`)
+		cmd.Stdout = &stdout
+		if err := cmd.Run(); err == nil {
+			result := strings.TrimSpace(stdout.String())
+			if result != "" {
+				return result
+			}
+		}
+	} else {
+		// On other Unix-like systems, use getent to read the passwd entry for the current user
+		cmd := exec.Command("getent", "passwd", strconv.Itoa(os.Getuid()))
+		cmd.Stdout = &stdout
+		if err := cmd.Run(); err == nil {
+			if passwd := strings.TrimSpace(stdout.String()); passwd != "" {
+				// The passwd entry is in the format: username:password:uid:gid:gecos:home:shell
+				passwdParts := strings.SplitN(passwd, ":", 7)
+				if len(passwdParts) > 5 {
+					return passwdParts[5]
+				}
+			}
+		}
+	}
+
+	// If all else fails, use the shell to determine the home directory
+	stdout.Reset()
+	cmd := exec.Command("sh", "-c", "cd && pwd")
+	cmd.Stdout = &stdout
+	if err := cmd.Run(); err == nil {
+		return strings.TrimSpace(stdout.String())
+	}
+
+	return ""
+}
diff --git a/main/civisibility/utils/home_test.go b/main/civisibility/utils/home_test.go
new file mode 100644
index 0000000..a87d8de
--- /dev/null
+++ b/main/civisibility/utils/home_test.go
@@ -0,0 +1,97 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package utils
+
+import (
+	"os"
+	"os/user"
+	"path/filepath"
+	"testing"
+)
+
+func patchEnv(key, value string) func() {
+	bck := os.Getenv(key)
+	deferFunc := func() {
+		_ = os.Setenv(key, bck)
+	}
+
+	if value != "" {
+		_ = os.Setenv(key, value)
+	} else {
+		_ = os.Unsetenv(key)
+	}
+
+	return deferFunc
+}
+
+func TestDir(t *testing.T) {
+	u, err := user.Current()
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	dir := getHomeDir()
+	if u.HomeDir != dir {
+		t.Fatalf("%#v != %#v", u.HomeDir, dir)
+	}
+
+	defer patchEnv("HOME", "")()
+	dir = getHomeDir()
+	if u.HomeDir != dir {
+		t.Fatalf("%#v != %#v", u.HomeDir, dir)
+	}
+}
+
+func TestExpand(t *testing.T) {
+	u, err := user.Current()
+	if err != nil {
+		t.Fatalf("err: %s", err)
+	}
+
+	cases := []struct {
+		Input  string
+		Output string
+	}{
+		{
+			"/foo",
+			"/foo",
+		},
+
+		{
+			"~/foo",
+			filepath.Join(u.HomeDir, "foo"),
+		},
+
+		{
+			"",
+			"",
+		},
+
+		{
+			"~",
+			u.HomeDir,
+		},
+
+		{
+			"~foo/foo",
+			"~foo/foo",
+		},
+	}
+
+	for _, tc := range cases {
+		actual := ExpandPath(tc.Input)
+		if actual != tc.Output {
+			t.Fatalf("Input: %#v\n\nOutput: %#v", tc.Input, actual)
+		}
+	}
+
+	defer patchEnv("HOME", "/custom/path/")()
+	expected := filepath.Join("/", "custom", "path", "foo/bar")
+	actual := ExpandPath("~/foo/bar")
+	if actual != expected {
+		t.Errorf("Expected: %v; actual: %v", expected, actual)
+	}
+}
diff --git a/main/civisibility/utils/names.go b/main/civisibility/utils/names.go
new file mode 100644
index 0000000..1edcb2b
--- /dev/null
+++ b/main/civisibility/utils/names.go
@@ -0,0 +1,82 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package utils
+
+import (
+	"bytes"
+	"fmt"
+	"path/filepath"
+	"runtime"
+	"strings"
+)
+
+// GetModuleAndSuiteName extracts the module name and suite name from a given program counter (pc).
+// This function utilizes runtime.FuncForPC to retrieve the full function name associated with the
+// program counter, then splits the string to separate the package name from the function name.
+//
+// Example 1:
+//
+//	Input:  github.com/DataDog/dd-sdk-go-testing.TestRun
+//	Output:
+//	   module: github.com/DataDog/dd-sdk-go-testing
+//	   suite:  testing_test.go
+//
+// Example 2:
+//
+//	Input:  github.com/DataDog/dd-sdk-go-testing.TestRun.func1
+//	Output:
+//	   module: github.com/DataDog/dd-sdk-go-testing
+//	   suite:  testing_test.go
+//
+// Parameters:
+//
+//	pc - The program counter for which the module and suite name should be retrieved.
+//
+// Returns:
+//
+//	module - The module name extracted from the full function name.
+//	suite  - The base name of the file where the function is located.
+func GetModuleAndSuiteName(pc uintptr) (module string, suite string) {
+	funcValue := runtime.FuncForPC(pc)
+	funcFullName := funcValue.Name()
+	lastSlash := strings.LastIndexByte(funcFullName, '/')
+	if lastSlash < 0 {
+		lastSlash = 0
+	}
+	firstDot := strings.IndexByte(funcFullName[lastSlash:], '.') + lastSlash
+	file, _ := funcValue.FileLine(funcValue.Entry())
+	return funcFullName[:firstDot], filepath.Base(file)
+}
+
+// GetStacktrace retrieves the current stack trace, skipping a specified number of frames.
+//
+// This function captures the stack trace of the current goroutine, formats it, and returns it as a string.
+// It uses runtime.Callers to capture the program counters of the stack frames and runtime.CallersFrames
+// to convert these program counters into readable frames. The stack trace is formatted to include the function
+// name, file name, and line number of each frame.
+//
+// Parameters:
+//
+//	skip - The number of stack frames to skip before capturing the stack trace.
+//
+// Returns:
+//
+//	A string representation of the current stack trace, with each frame on a new line.
+func GetStacktrace(skip int) string {
+	pcs := make([]uintptr, 256)
+	total := runtime.Callers(skip+2, pcs)
+	frames := runtime.CallersFrames(pcs[:total])
+	buffer := new(bytes.Buffer)
+	for {
+		if frame, ok := frames.Next(); ok {
+			_, _ = fmt.Fprintf(buffer, "%s\n\t%s:%d\n", frame.Function, frame.File, frame.Line)
+		} else {
+			break
+		}
+
+	}
+	return buffer.String()
+}
diff --git a/main/civisibility/utils/names_test.go b/main/civisibility/utils/names_test.go
new file mode 100644
index 0000000..019aa02
--- /dev/null
+++ b/main/civisibility/utils/names_test.go
@@ -0,0 +1,39 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2024 Datadog, Inc.
+
+package utils
+
+import (
+	"runtime"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetModuleAndSuiteName(t *testing.T) {
+	pc, _, _, _ := runtime.Caller(0) // Get the program counter of this function
+	_, suite := GetModuleAndSuiteName(pc)
+	// expectedModule := "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/utils"
+	expectedSuite := "names_test.go"
+
+	// assert.True(t, strings.HasPrefix(module, expectedModule))
+	assert.Equal(t, expectedSuite, suite)
+}
+
+func TestGetStacktrace(t *testing.T) {
+	stacktrace := GetStacktrace(0)
+	assert.Contains(t, stacktrace, "names_test.go")     // Ensure that the current test file is part of the stack trace
+	assert.Contains(t, stacktrace, "TestGetStacktrace") // Ensure that the current test function is part of the stack trace
+}
+
+func TestGetStacktraceWithSkip(t *testing.T) {
+	stacktrace := getStacktraceHelper()
+	assert.Contains(t, stacktrace, "names_test.go")
+	assert.NotContains(t, stacktrace, "getStacktraceHelper") // Ensure the helper function is skipped
+}
+
+func getStacktraceHelper() string {
+	return GetStacktrace(1) // Skip this helper function frame
+}
diff --git a/main/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITHUB b/main/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITHUB
new file mode 100644
index 0000000..afbd198
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITHUB
@@ -0,0 +1,54 @@
+# This is a comment.
+# Each line is a file pattern followed by one or more owners.
+
+# These owners will be the default owners for everything in
+# the repo. Unless a later match takes precedence,
+# @global-owner1 and @global-owner2 will be requested for
+# review when someone opens a pull request.
+*       @global-owner1 @global-owner2
+
+# Order is important; the last matching pattern takes the most
+# precedence. When someone opens a pull request that only
+# modifies JS files, only @js-owner and not the global
+# owner(s) will be requested for a review.
+*.js    @js-owner
+
+# You can also use email addresses if you prefer. They'll be
+# used to look up users just like we do for commit author
+# emails.
+*.go docs@example.com
+
+# Teams can be specified as code owners as well. Teams should
+# be identified in the format @org/team-name. Teams must have
+# explicit write access to the repository. In this example,
+# the octocats team in the octo-org organization owns all .txt files.
+*.txt @octo-org/octocats
+
+# In this example, @doctocat owns any files in the build/logs
+# directory at the root of the repository and any of its
+# subdirectories.
+/build/logs/ @doctocat
+
+# The `docs/*` pattern will match files like
+# `docs/getting-started.md` but not further nested files like
+# `docs/build-app/troubleshooting.md`.
+docs/*  docs@example.com
+
+# In this example, @octocat owns any file in an apps directory
+# anywhere in your repository.
+apps/ @octocat
+
+# In this example, @doctocat owns any file in the `/docs`
+# directory in the root of your repository and any of its
+# subdirectories.
+/docs/ @doctocat
+
+# In this example, any change inside the `/scripts` directory
+# will require approval from @doctocat or @octocat.
+/scripts/ @doctocat @octocat
+
+# In this example, @octocat owns any file in the `/apps`
+# directory in the root of your repository except for the `/apps/github`
+# subdirectory, as its owners are left empty.
+/apps/ @octocat
+/apps/github
\ No newline at end of file
diff --git a/main/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITLAB b/main/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITLAB
new file mode 100644
index 0000000..d0831c7
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITLAB
@@ -0,0 +1,64 @@
+# This is an example of a CODEOWNERS file.
+# Lines that start with `#` are ignored.
+
+# app/ @commented-rule
+
+# Specify a default Code Owner by using a wildcard:
+* @default-codeowner
+
+# Specify multiple Code Owners by using a tab or space:
+* @multiple @code @owners
+
+# Rules defined later in the file take precedence over the rules
+# defined before.
+# For example, for all files with a filename ending in `.rb`:
+*.rb @ruby-owner
+
+# Files with a `#` can still be accessed by escaping the pound sign:
+\#file_with_pound.rb @owner-file-with-pound
+
+# Specify multiple Code Owners separated by spaces or tabs.
+# In the following case the CODEOWNERS file from the root of the repo
+# has 3 Code Owners (@multiple @code @owners):
+CODEOWNERS @multiple @code @owners
+
+# You can use both usernames or email addresses to match
+# users. Everything else is ignored. For example, this code
+# specifies the `@legal` and a user with email `janedoe@gitlab.com` as the
+# owner for the LICENSE file:
+LICENSE @legal this_does_not_match janedoe@gitlab.com
+
+# Use group names to match groups, and nested groups to specify
+# them as owners for a file:
+README @group @group/with-nested/subgroup
+
+# End a path in a `/` to specify the Code Owners for every file
+# nested in that directory, on any level:
+/docs/ @all-docs
+
+# End a path in `/*` to specify Code Owners for every file in
+# a directory, but not nested deeper. This code matches
+# `docs/index.md` but not `docs/projects/index.md`:
+/docs/* @root-docs
+
+# This code makes matches a `lib` directory nested anywhere in the repository:
+lib/ @lib-owner
+
+# This code match only a `config` directory in the root of the repository:
+/config/ @config-owner
+
+# If the path contains spaces, escape them like this:
+path\ with\ spaces/ @space-owner
+
+# Code Owners section:
+[Documentation]
+ee/docs    @docs
+docs       @docs
+
+[Database]
+README.md  @database
+model/db   @database
+
+# This section is combined with the previously defined [Documentation] section:
+[DOCUMENTATION]
+README.md  @docs
\ No newline at end of file
diff --git a/main/civisibility/utils/testdata/fixtures/providers/appveyor.json b/main/civisibility/utils/testdata/fixtures/providers/appveyor.json
new file mode 100644
index 0000000..def208d
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/appveyor.json
@@ -0,0 +1,555 @@
+[
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "/foo/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "foo/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "/foo/bar~",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/foo/bar~",
+      "git.branch": "master",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "/foo/~/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/foo/~/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "~/foo/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/not-my-home/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "~foo/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "~foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "~",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/not-my-home",
+      "git.branch": "master",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "/foo/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "/foo/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "origin/master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "/foo/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "refs/heads/master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "/foo/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "refs/heads/feature/one",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "feature/one",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "/foo/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH": "origin/pr",
+      "APPVEYOR_REPO_BRANCH": "origin/master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "pr",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "/foo/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH": "refs/heads/pr",
+      "APPVEYOR_REPO_BRANCH": "refs/heads/master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "pr",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "/foo/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "origin/master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github",
+      "APPVEYOR_REPO_TAG_NAME": "origin/tags/0.1.0"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_FOLDER": "/foo/bar",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_BRANCH": "refs/heads/master",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "APPVEYOR_REPO_PROVIDER": "github",
+      "APPVEYOR_REPO_TAG_NAME": "refs/heads/tags/0.1.0"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "appveyor-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "appveyor-commit-author-name",
+      "git.commit.message": "appveyor-commit-message\nappveyor-commit-message-extended",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/appveyor-repo-name.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "APPVEYOR": "true",
+      "APPVEYOR_BUILD_ID": "appveyor-build-id",
+      "APPVEYOR_BUILD_NUMBER": "appveyor-pipeline-number",
+      "APPVEYOR_REPO_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "APPVEYOR_REPO_COMMIT_AUTHOR": "appveyor-commit-author-name",
+      "APPVEYOR_REPO_COMMIT_AUTHOR_EMAIL": "appveyor-commit-author-email@datadoghq.com",
+      "APPVEYOR_REPO_COMMIT_MESSAGE": "appveyor-commit-message",
+      "APPVEYOR_REPO_COMMIT_MESSAGE_EXTENDED": "appveyor-commit-message-extended",
+      "APPVEYOR_REPO_NAME": "appveyor-repo-name",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2"
+    },
+    {
+      "ci.job.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.pipeline.id": "appveyor-build-id",
+      "ci.pipeline.name": "appveyor-repo-name",
+      "ci.pipeline.number": "appveyor-pipeline-number",
+      "ci.pipeline.url": "https://ci.appveyor.com/project/appveyor-repo-name/builds/appveyor-build-id",
+      "ci.provider.name": "appveyor",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/awscodepipeline.json b/main/civisibility/utils/testdata/fixtures/providers/awscodepipeline.json
new file mode 100644
index 0000000..6f30713
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/awscodepipeline.json
@@ -0,0 +1,62 @@
+[
+  [
+    {
+      "CODEBUILD_BUILD_ARN": "arn:aws:codebuild:eu-north-1:12345678:build/codebuild-demo-project:b1e6661e-e4f2-4156-9ab9-82a19",
+      "CODEBUILD_INITIATOR": "codepipeline/test-pipeline",
+      "DD_ACTION_EXECUTION_ID": "35519dc3-7c45-493c-9ba6-cd78ea11f69d",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_PIPELINE_EXECUTION_ID": "bb1f15ed-fde2-494d-8e13-88785bca9cc0"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CODEBUILD_BUILD_ARN\":\"arn:aws:codebuild:eu-north-1:12345678:build/codebuild-demo-project:b1e6661e-e4f2-4156-9ab9-82a19\",\"DD_PIPELINE_EXECUTION_ID\":\"bb1f15ed-fde2-494d-8e13-88785bca9cc0\",\"DD_ACTION_EXECUTION_ID\":\"35519dc3-7c45-493c-9ba6-cd78ea11f69d\"}",
+      "ci.pipeline.id": "bb1f15ed-fde2-494d-8e13-88785bca9cc0",
+      "ci.provider.name": "awscodepipeline",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "CODEBUILD_INITIATOR": "lambdafunction/test-lambda",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git"
+    },
+    {
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/azurepipelines.json b/main/civisibility/utils/testdata/fixtures/providers/azurepipelines.json
new file mode 100644
index 0000000..d072c27
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/azurepipelines.json
@@ -0,0 +1,935 @@
+[
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "master",
+      "BUILD_SOURCESDIRECTORY": "/foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "master",
+      "BUILD_SOURCESDIRECTORY": "/foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_PULLREQUEST_SOURCEREPOSITORYURI": "https://azure-pipelines-server-uri.com/pull.git",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/pull.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "origin/master",
+      "BUILD_SOURCESDIRECTORY": "foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "origin/master",
+      "BUILD_SOURCESDIRECTORY": "/foo/bar~",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "HOME": "/not-my-home",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/foo/bar~",
+      "git.branch": "master",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "origin/master",
+      "BUILD_SOURCESDIRECTORY": "/foo/~/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "HOME": "/not-my-home",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/foo/~/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "origin/master",
+      "BUILD_SOURCESDIRECTORY": "~/foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "HOME": "/not-my-home",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/not-my-home/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "origin/master",
+      "BUILD_SOURCESDIRECTORY": "~foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "HOME": "/not-my-home",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "~foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "origin/master",
+      "BUILD_SOURCESDIRECTORY": "~",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "HOME": "/not-my-home",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/not-my-home",
+      "git.branch": "master",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "origin/master",
+      "BUILD_SOURCESDIRECTORY": "/foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "refs/heads/master",
+      "BUILD_SOURCESDIRECTORY": "/foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "refs/heads/feature/one",
+      "BUILD_SOURCESDIRECTORY": "/foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "feature/one",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "origin/tags/0.1.0",
+      "BUILD_SOURCESDIRECTORY": "/foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "refs/heads/tags/0.1.0",
+      "BUILD_SOURCESDIRECTORY": "/foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "origin/master",
+      "BUILD_SOURCESDIRECTORY": "/foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_PULLREQUEST_SOURCEBRANCH": "origin/pr",
+      "SYSTEM_PULLREQUEST_SOURCECOMMITID": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "pr",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "refs/heads/master",
+      "BUILD_SOURCESDIRECTORY": "/foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_PULLREQUEST_SOURCEBRANCH": "refs/heads/pr",
+      "SYSTEM_PULLREQUEST_SOURCECOMMITID": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "SYSTEM_STAGEDISPLAYNAME": "azure-pipelines-stage-name",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.stage.name": "azure-pipelines-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "pr",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://azure-pipelines-server-uri.com/build.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEBRANCH": "refs/heads/feature/one",
+      "BUILD_SOURCESDIRECTORY": "/foo/bar",
+      "BUILD_SOURCEVERSION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBDISPLAYNAME": "azure-pipelines-job-name",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_PULLREQUEST_SOURCEBRANCH": "refs/heads/pr",
+      "SYSTEM_PULLREQUEST_SOURCECOMMITID": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.name": "azure-pipelines-job-name",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "pr",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://azure-pipelines-server-uri.com/build.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://dev.azure.com/fabrikamfiber/repo",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "DD_TEST_CASE_NAME": "http-repository-url-no-git-suffix",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.repository_url": "https://dev.azure.com/fabrikamfiber/repo"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "ssh://host.xz:54321/path/to/repo/",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "DD_TEST_CASE_NAME": "ssh-repository-url-no-git-suffix",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo/"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://user:password@dev.azure.com/fabrikamfiber/repo.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.repository_url": "https://dev.azure.com/fabrikamfiber/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://user:password@dev.azure.com:1234/fabrikamfiber/repo.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.repository_url": "https://dev.azure.com:1234/fabrikamfiber/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://user:password@1.1.1.1:1234/fabrikamfiber/repo.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.repository_url": "https://1.1.1.1:1234/fabrikamfiber/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://user:password@1.1.1.1:1234/fabrikamfiber/repo_with_@_yeah.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.repository_url": "https://1.1.1.1:1234/fabrikamfiber/repo_with_@_yeah.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "https://user@dev.azure.com/fabrikamfiber/repo.git",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.repository_url": "https://dev.azure.com/fabrikamfiber/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "ssh://user@host.xz:54321/path/to/repo.git/",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "ssh://user:password@host.xz:54321/path/to/repo.git/",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ],
+  [
+    {
+      "BUILD_BUILDID": "azure-pipelines-build-id",
+      "BUILD_DEFINITIONNAME": "azure-pipelines-name",
+      "BUILD_REPOSITORY_URI": "ssh://user:password@1.1.1.1:54321/path/to/repo.git/",
+      "BUILD_REQUESTEDFOREMAIL": "azure-pipelines-commit-author-email@datadoghq.com",
+      "BUILD_REQUESTEDFORID": "azure-pipelines-commit-author",
+      "BUILD_SOURCEVERSIONMESSAGE": "azure-pipelines-commit-message",
+      "SYSTEM_JOBID": "azure-pipelines-job-id",
+      "SYSTEM_TASKINSTANCEID": "azure-pipelines-task-id",
+      "SYSTEM_TEAMFOUNDATIONSERVERURI": "https://azure-pipelines-server-uri.com/",
+      "SYSTEM_TEAMPROJECTID": "azure-pipelines-project-id",
+      "TF_BUILD": "True"
+    },
+    {
+      "_dd.ci.env_vars": "{\"SYSTEM_TEAMPROJECTID\":\"azure-pipelines-project-id\",\"BUILD_BUILDID\":\"azure-pipelines-build-id\",\"SYSTEM_JOBID\":\"azure-pipelines-job-id\"}",
+      "ci.job.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id&view=logs&j=azure-pipelines-job-id&t=azure-pipelines-task-id",
+      "ci.pipeline.id": "azure-pipelines-build-id",
+      "ci.pipeline.name": "azure-pipelines-name",
+      "ci.pipeline.number": "azure-pipelines-build-id",
+      "ci.pipeline.url": "https://azure-pipelines-server-uri.com/azure-pipelines-project-id/_build/results?buildId=azure-pipelines-build-id",
+      "ci.provider.name": "azurepipelines",
+      "git.commit.author.email": "azure-pipelines-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "azure-pipelines-commit-author",
+      "git.commit.message": "azure-pipelines-commit-message",
+      "git.repository_url": "ssh://1.1.1.1:54321/path/to/repo.git/"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/bitbucket.json b/main/civisibility/utils/testdata/fixtures/providers/bitbucket.json
new file mode 100644
index 0000000..bbd8cd3
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/bitbucket.json
@@ -0,0 +1,595 @@
+[
+  [
+    {
+      "BITBUCKET_BRANCH": "master",
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "/foo/bar",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BRANCH": "master",
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "foo/bar",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BRANCH": "master",
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "foo/bar",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_GIT_SSH_ORIGIN": "git@github.com:DataDog/dummy-example.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/dummy-example.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BRANCH": "master",
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "/foo/bar~",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "/foo/bar~",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BRANCH": "master",
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "/foo/~/bar",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "/foo/~/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BRANCH": "master",
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "~/foo/bar",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "/not-my-home/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BRANCH": "master",
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "~foo/bar",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "~foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BRANCH": "master",
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "~",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "/not-my-home",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BRANCH": "master",
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "/foo/bar",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BRANCH": "origin/master",
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "/foo/bar",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BRANCH": "refs/heads/master",
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "/foo/bar",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BRANCH": "refs/heads/feature/one",
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "/foo/bar",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "feature/one",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "/foo/bar",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo",
+      "BITBUCKET_TAG": "origin/tags/0.1.0"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_CLONE_DIR": "/foo/bar",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo",
+      "BITBUCKET_TAG": "refs/heads/tags/0.1.0"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket-repo-url.com/repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket-repo-url.com/repo.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://bitbucket.org/DataDog/dogweb",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo",
+      "DD_TEST_CASE_NAME": "http-repository-url-no-git-suffix"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket.org/DataDog/dogweb"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "ssh://host.xz:54321/path/to/repo/",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo",
+      "DD_TEST_CASE_NAME": "ssh-repository-url-no-git-suffix"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo/"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://user:password@bitbucket.org/DataDog/dogweb.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket.org/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://user@bitbucket.org/DataDog/dogweb.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket.org/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://user:password@bitbucket.org:1234/DataDog/dogweb.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitbucket.org:1234/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://user:password@1.1.1.1/DataDog/dogweb.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://user:password@1.1.1.1:1234/DataDog/dogweb.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "https://user:password@1.1.1.1:1234/DataDog/dogweb_with_@_yeah.git",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/DataDog/dogweb_with_@_yeah.git"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "ssh://user@host.xz:54321/path/to/repo.git/",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ],
+  [
+    {
+      "BITBUCKET_BUILD_NUMBER": "bitbucket-build-num",
+      "BITBUCKET_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITBUCKET_GIT_HTTP_ORIGIN": "ssh://user:password@host.xz:54321/path/to/repo.git/",
+      "BITBUCKET_PIPELINE_UUID": "{bitbucket-uuid}",
+      "BITBUCKET_REPO_FULL_NAME": "bitbucket-repo"
+    },
+    {
+      "ci.job.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.pipeline.id": "bitbucket-uuid",
+      "ci.pipeline.name": "bitbucket-repo",
+      "ci.pipeline.number": "bitbucket-build-num",
+      "ci.pipeline.url": "https://bitbucket.org/bitbucket-repo/addon/pipelines/home#!/results/bitbucket-build-num",
+      "ci.provider.name": "bitbucket",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/bitrise.json b/main/civisibility/utils/testdata/fixtures/providers/bitrise.json
new file mode 100644
index 0000000..579ace5
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/bitrise.json
@@ -0,0 +1,694 @@
+[
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "/foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://bitrise-build-url.com/repo.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitrise-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "/foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "git@github.com:DataDog/dummy-example.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/dummy-example.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "origin/master",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "/foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://bitrise-build-url.com/repo.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitrise-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "origin/master",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://bitrise-build-url.com/repo.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitrise-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "origin/master",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "/foo/bar~",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://bitrise-build-url.com/repo.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/foo/bar~",
+      "git.branch": "master",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitrise-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "origin/master",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "/foo/~/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://bitrise-build-url.com/repo.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/foo/~/bar",
+      "git.branch": "master",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitrise-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "origin/master",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "~/foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://bitrise-build-url.com/repo.git",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/not-my-home/foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitrise-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "origin/master",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "~foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://bitrise-build-url.com/repo.git",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "~foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitrise-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "origin/master",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "~",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://bitrise-build-url.com/repo.git",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/not-my-home",
+      "git.branch": "master",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitrise-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "refs/heads/master",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "/foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://bitrise-build-url.com/repo.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitrise-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "refs/heads/feature/one",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "/foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://bitrise-build-url.com/repo.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "feature/one",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitrise-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "origin/tags/0.1.0",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_GIT_TAG": "origin/tags/0.1.0",
+      "BITRISE_SOURCE_DIR": "/foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://bitrise-build-url.com/repo.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitrise-build-url.com/repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "refs/heads/tags/0.1.0",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_GIT_TAG": "refs/heads/tags/0.1.0",
+      "BITRISE_SOURCE_DIR": "/foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://bitrise-build-url.com/repo.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://bitrise-build-url.com/repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "origin/master",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "/foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "http://hostname.com/repo.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "origin/master",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "/foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "git@hostname.com:org/repo.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@hostname.com:org/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_BRANCH": "origin/notmaster",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_SOURCE_DIR": "/foo/bar",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "git@hostname.com:org/repo.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "notmaster",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@hostname.com:org/repo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "DD_TEST_CASE_NAME": "http-repository-url-no-git-suffix",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://github.com/DataDog/dogweb"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "DD_TEST_CASE_NAME": "ssh-repository-url-no-git-suffix",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "ssh://host.xz:54321/path/to/repo/"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo/"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://user:password@github.com/DataDog/dogweb.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://user@github.com/DataDog/dogweb.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://user:password@github.com:1234/DataDog/dogweb.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com:1234/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://user:password@1.1.1.1/DataDog/dogweb.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://user:password@1.1.1.1:1234/DataDog/dogweb.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "https://user:password@1.1.1.1:1234/DataDog/dogweb_with_@_yeah.git"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/DataDog/dogweb_with_@_yeah.git"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "ssh://user@host.xz:54321/path/to/repo.git/"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ],
+  [
+    {
+      "BITRISE_BUILD_NUMBER": "bitrise-pipeline-number",
+      "BITRISE_BUILD_SLUG": "bitrise-pipeline-id",
+      "BITRISE_BUILD_URL": "https://bitrise-build-url.com//",
+      "BITRISE_GIT_MESSAGE": "bitrise-git-commit-message",
+      "BITRISE_TRIGGERED_WORKFLOW_ID": "bitrise-pipeline-name",
+      "GIT_CLONE_COMMIT_HASH": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_REPOSITORY_URL": "ssh://user:password@host.xz:54321/path/to/repo.git/"
+    },
+    {
+      "ci.pipeline.id": "bitrise-pipeline-id",
+      "ci.pipeline.name": "bitrise-pipeline-name",
+      "ci.pipeline.number": "bitrise-pipeline-number",
+      "ci.pipeline.url": "https://bitrise-build-url.com//",
+      "ci.provider.name": "bitrise",
+      "git.commit.message": "bitrise-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/buddy.json b/main/civisibility/utils/testdata/fixtures/providers/buddy.json
new file mode 100644
index 0000000..9c64a19
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/buddy.json
@@ -0,0 +1,483 @@
+[
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "https://github.com/buddyworks/my-project.git"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "master",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/buddyworks/my-project.git",
+      "git.tag": "v1.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "my-name-is-rotag/fix-original-bug",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "https://github.com/buddyworks/my-project.git"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "my-name-is-rotag/fix-original-bug",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/buddyworks/my-project.git",
+      "git.tag": "v1.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "refs/heads/feature/one",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "refs/heads/tags/0.2.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "https://github.com/buddyworks/my-project.git"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "feature/one",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/buddyworks/my-project.git",
+      "git.tag": "0.2.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "https://github.com/buddyworks/my-project.git",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "user-supplied-tag"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "user-supplied-tag"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "https://github.com/buddyworks/my-project.git",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "v1.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "https://github.com/buddyworks/my-project",
+      "DD_TEST_CASE_NAME": "http-repository-url-no-git-suffix"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "master",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/buddyworks/my-project",
+      "git.tag": "v1.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "ssh://host.xz:54321/path/to/repo/",
+      "DD_TEST_CASE_NAME": "ssh-repository-url-no-git-suffix"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "master",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo/",
+      "git.tag": "v1.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "https://user:password@github.com/buddyworks/my-project.git"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "master",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/buddyworks/my-project.git",
+      "git.tag": "v1.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "https://user@github.com/buddyworks/my-project.git"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "master",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/buddyworks/my-project.git",
+      "git.tag": "v1.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "https://user:password@github.com:1234/buddyworks/my-project.git"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "master",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com:1234/buddyworks/my-project.git",
+      "git.tag": "v1.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "https://user:password@1.1.1.1/buddyworks/my-project.git"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "master",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1/buddyworks/my-project.git",
+      "git.tag": "v1.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "https://user:password@1.1.1.1:1234/buddyworks/my-project.git"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "master",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/buddyworks/my-project.git",
+      "git.tag": "v1.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "https://user:password@1.1.1.1:1234/buddyworks/my-project_with_@_yeah.git"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "master",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/buddyworks/my-project_with_@_yeah.git",
+      "git.tag": "v1.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "ssh://user@host.xz:54321/path/to/repo.git/"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "master",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/",
+      "git.tag": "v1.0"
+    }
+  ],
+  [
+    {
+      "BUDDY": "true",
+      "BUDDY_EXECUTION_BRANCH": "master",
+      "BUDDY_EXECUTION_ID": "buddy-execution-id",
+      "BUDDY_EXECUTION_REVISION": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL": "mikebenson@buddy.works",
+      "BUDDY_EXECUTION_REVISION_COMMITTER_NAME": "Mike Benson",
+      "BUDDY_EXECUTION_REVISION_MESSAGE": "Create buddy.yml",
+      "BUDDY_EXECUTION_TAG": "v1.0",
+      "BUDDY_EXECUTION_URL": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "BUDDY_PIPELINE_ID": "456",
+      "BUDDY_PIPELINE_NAME": "Deploy to Production",
+      "BUDDY_SCM_URL": "ssh://user:password@host.xz:54321/path/to/repo.git/"
+    },
+    {
+      "ci.pipeline.id": "456/buddy-execution-id",
+      "ci.pipeline.name": "Deploy to Production",
+      "ci.pipeline.number": "buddy-execution-id",
+      "ci.pipeline.url": "https://app.buddy.works/myworkspace/my-project/pipelines/pipeline/456/execution/5d9dc42c422f5a268b389d08",
+      "ci.provider.name": "buddy",
+      "git.branch": "master",
+      "git.commit.committer.email": "mikebenson@buddy.works",
+      "git.commit.committer.name": "Mike Benson",
+      "git.commit.message": "Create buddy.yml",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/",
+      "git.tag": "v1.0"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/buildkite.json b/main/civisibility/utils/testdata/fixtures/providers/buildkite.json
new file mode 100644
index 0000000..86ff170
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/buildkite.json
@@ -0,0 +1,1022 @@
+[
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/bar~",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/bar~",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/~/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/~/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "~/foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": "",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/not-my-home/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "~foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": "",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "~foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "~",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": "",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/not-my-home",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://user@hostname.com/repo.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://user%E2%82%AC@hostname.com/repo.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://user:pwd@hostname.com/repo.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "git@hostname.com:org/repo.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@hostname.com:org/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "origin/master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "refs/heads/master",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "refs/heads/feature/one",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "feature/one",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": "0.1.0"
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": "origin/tags/0.1.0"
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_CHECKOUT_PATH": "/foo/bar",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "http://hostname.com/repo.git",
+      "BUILDKITE_TAG": "refs/heads/tags/0.1.0"
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_TAG": "",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git"
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_TAG": "",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2"
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "https://github.com/DataDog/dogweb",
+      "BUILDKITE_TAG": "",
+      "DD_TEST_CASE_NAME": "http-repository-url-no-git-suffix"
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "ssh://host.xz:54321/path/to/repo/",
+      "BUILDKITE_TAG": "",
+      "DD_TEST_CASE_NAME": "ssh-repository-url-no-git-suffix"
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo/"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "https://user:password@github.com/DataDog/dogweb.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "https://user@github.com/DataDog/dogweb.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "https://user:password@github.com:1234/DataDog/dogweb.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com:1234/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "https://user:password@1.1.1.1/DataDog/dogweb.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "https://user:password@1.1.1.1:1234/DataDog/dogweb.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "https://user:password@1.1.1.1:1234/DataDog/dogweb_with_@_yeah.git",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/DataDog/dogweb_with_@_yeah.git"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "ssh://user@host.xz:54321/path/to/repo.git/",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_REPO": "ssh://user:password@host.xz:54321/path/to/repo.git/",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ],
+  [
+    {
+      "BUILDKITE": "true",
+      "BUILDKITE_AGENT_ID": "1a222222-e999-3636-8ddd-802222222222",
+      "BUILDKITE_AGENT_META_DATA_MYOTHERTAG": "my-other-value",
+      "BUILDKITE_AGENT_META_DATA_MYTAG": "my-value",
+      "BUILDKITE_BRANCH": "",
+      "BUILDKITE_BUILD_AUTHOR": "buildkite-git-commit-author-name",
+      "BUILDKITE_BUILD_AUTHOR_EMAIL": "buildkite-git-commit-author-email@datadoghq.com",
+      "BUILDKITE_BUILD_ID": "buildkite-pipeline-id",
+      "BUILDKITE_BUILD_NUMBER": "buildkite-pipeline-number",
+      "BUILDKITE_BUILD_URL": "https://buildkite-build-url.com",
+      "BUILDKITE_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "BUILDKITE_JOB_ID": "buildkite-job-id",
+      "BUILDKITE_MESSAGE": "buildkite-git-commit-message",
+      "BUILDKITE_PIPELINE_SLUG": "buildkite-pipeline-name",
+      "BUILDKITE_TAG": ""
+    },
+    {
+      "_dd.ci.env_vars": "{\"BUILDKITE_BUILD_ID\":\"buildkite-pipeline-id\",\"BUILDKITE_JOB_ID\":\"buildkite-job-id\"}",
+      "ci.job.url": "https://buildkite-build-url.com#buildkite-job-id",
+      "ci.node.labels": "[\"mytag:my-value\",\"myothertag:my-other-value\"]",
+      "ci.node.name": "1a222222-e999-3636-8ddd-802222222222",
+      "ci.pipeline.id": "buildkite-pipeline-id",
+      "ci.pipeline.name": "buildkite-pipeline-name",
+      "ci.pipeline.number": "buildkite-pipeline-number",
+      "ci.pipeline.url": "https://buildkite-build-url.com",
+      "ci.provider.name": "buildkite",
+      "git.commit.author.email": "buildkite-git-commit-author-email@datadoghq.com",
+      "git.commit.author.name": "buildkite-git-commit-author-name",
+      "git.commit.message": "buildkite-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/circleci.json b/main/civisibility/utils/testdata/fixtures/providers/circleci.json
new file mode 100644
index 0000000..2a697af
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/circleci.json
@@ -0,0 +1,754 @@
+[
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://circleci-build-url.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://circleci-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://circleci-build-url.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://circleci-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://circleci-build-url.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "/foo/bar~"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/foo/bar~",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://circleci-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://circleci-build-url.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "/foo/~/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/foo/~/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://circleci-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://circleci-build-url.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "~/foo/bar",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/not-my-home/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://circleci-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://circleci-build-url.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "~foo/bar",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "~foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://circleci-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://circleci-build-url.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "~",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/not-my-home",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://circleci-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "refs/heads/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://circleci-build-url.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://circleci-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "refs/heads/feature/one",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://circleci-build-url.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "feature/one",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://circleci-build-url.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/tags/0.1.0",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://circleci-build-url.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_TAG": "origin/tags/0.1.0",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://circleci-build-url.com/repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "refs/heads/tags/0.1.0",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://circleci-build-url.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_TAG": "refs/heads/tags/0.1.0",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://circleci-build-url.com/repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "http://hostname.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "http://user@hostname.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "http://user%E2%82%AC@hostname.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "http://user:pwd@hostname.com/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BRANCH": "origin/master",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "git@hostname.com:org/repo.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "CIRCLE_WORKING_DIRECTORY": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@hostname.com:org/repo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://github.com/DataDog/dogweb",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "DD_TEST_CASE_NAME": "http-repository-url-no-git-suffix"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "ssh://host.xz:54321/path/to/repo/",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id",
+      "DD_TEST_CASE_NAME": "ssh-repository-url-no-git-suffix"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo/"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://user:password@github.com/DataDog/dogweb.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://user@github.com/DataDog/dogweb.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://user:password@github.com:1234/DataDog/dogweb.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com:1234/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://user:password@1.1.1.1/DataDog/dogweb.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://user:password@1.1.1.1:1234/DataDog/dogweb.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "https://user:password@1.1.1.1:1234/DataDog/dogweb_with_@_yeah.git",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/DataDog/dogweb_with_@_yeah.git"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "ssh://user@host.xz:54321/path/to/repo.git/",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ],
+  [
+    {
+      "CIRCLECI": "circleCI",
+      "CIRCLE_BUILD_NUM": "circleci-pipeline-number",
+      "CIRCLE_BUILD_URL": "https://circleci-build-url.com/",
+      "CIRCLE_JOB": "circleci-job-name",
+      "CIRCLE_PROJECT_REPONAME": "circleci-pipeline-name",
+      "CIRCLE_REPOSITORY_URL": "ssh://user:password@host.xz:54321/path/to/repo.git/",
+      "CIRCLE_SHA1": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CIRCLE_WORKFLOW_ID": "circleci-pipeline-id"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CIRCLE_WORKFLOW_ID\":\"circleci-pipeline-id\",\"CIRCLE_BUILD_NUM\":\"circleci-pipeline-number\"}",
+      "ci.job.name": "circleci-job-name",
+      "ci.job.url": "https://circleci-build-url.com/",
+      "ci.pipeline.id": "circleci-pipeline-id",
+      "ci.pipeline.name": "circleci-pipeline-name",
+      "ci.pipeline.url": "https://app.circleci.com/pipelines/workflows/circleci-pipeline-id",
+      "ci.provider.name": "circleci",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/codefresh.json b/main/civisibility/utils/testdata/fixtures/providers/codefresh.json
new file mode 100644
index 0000000..d719df1
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/codefresh.json
@@ -0,0 +1,162 @@
+[
+  [
+    {
+      "CF_BUILD_ID": "6410367cee516146a4c4c66e",
+      "CF_BUILD_URL": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "CF_PIPELINE_NAME": "My simple project/Example Java Project Pipeline",
+      "CF_STEP_NAME": "mah-job-name"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CF_BUILD_ID\":\"6410367cee516146a4c4c66e\"}",
+      "ci.job.name": "mah-job-name",
+      "ci.pipeline.id": "6410367cee516146a4c4c66e",
+      "ci.pipeline.name": "My simple project/Example Java Project Pipeline",
+      "ci.pipeline.url": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "ci.provider.name": "codefresh"
+    }
+  ],
+  [
+    {
+      "CF_BRANCH": "origin/master",
+      "CF_BUILD_ID": "6410367cee516146a4c4c66e",
+      "CF_BUILD_URL": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "CF_PIPELINE_NAME": "My simple project/Example Java Project Pipeline",
+      "CF_STEP_NAME": "mah-job-name"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CF_BUILD_ID\":\"6410367cee516146a4c4c66e\"}",
+      "ci.job.name": "mah-job-name",
+      "ci.pipeline.id": "6410367cee516146a4c4c66e",
+      "ci.pipeline.name": "My simple project/Example Java Project Pipeline",
+      "ci.pipeline.url": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "ci.provider.name": "codefresh",
+      "git.branch": "master"
+    }
+  ],
+  [
+    {
+      "CF_BRANCH": "refs/heads/feature/one",
+      "CF_BUILD_ID": "6410367cee516146a4c4c66e",
+      "CF_BUILD_URL": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "CF_PIPELINE_NAME": "My simple project/Example Java Project Pipeline",
+      "CF_STEP_NAME": "mah-job-name"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CF_BUILD_ID\":\"6410367cee516146a4c4c66e\"}",
+      "ci.job.name": "mah-job-name",
+      "ci.pipeline.id": "6410367cee516146a4c4c66e",
+      "ci.pipeline.name": "My simple project/Example Java Project Pipeline",
+      "ci.pipeline.url": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "ci.provider.name": "codefresh",
+      "git.branch": "feature/one"
+    }
+  ],
+  [
+    {
+      "CF_BRANCH": "origin/tags/0.1.0",
+      "CF_BUILD_ID": "6410367cee516146a4c4c66e",
+      "CF_BUILD_URL": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "CF_PIPELINE_NAME": "My simple project/Example Java Project Pipeline",
+      "CF_STEP_NAME": "mah-job-name"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CF_BUILD_ID\":\"6410367cee516146a4c4c66e\"}",
+      "ci.job.name": "mah-job-name",
+      "ci.pipeline.id": "6410367cee516146a4c4c66e",
+      "ci.pipeline.name": "My simple project/Example Java Project Pipeline",
+      "ci.pipeline.url": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "ci.provider.name": "codefresh",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "CF_BRANCH": "refs/heads/tags/0.1.0",
+      "CF_BUILD_ID": "6410367cee516146a4c4c66e",
+      "CF_BUILD_URL": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "CF_PIPELINE_NAME": "My simple project/Example Java Project Pipeline",
+      "CF_STEP_NAME": "mah-job-name"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CF_BUILD_ID\":\"6410367cee516146a4c4c66e\"}",
+      "ci.job.name": "mah-job-name",
+      "ci.pipeline.id": "6410367cee516146a4c4c66e",
+      "ci.pipeline.name": "My simple project/Example Java Project Pipeline",
+      "ci.pipeline.url": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "ci.provider.name": "codefresh",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "CF_BUILD_ID": "6410367cee516146a4c4c66e",
+      "CF_BUILD_URL": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "CF_PIPELINE_NAME": "My simple project/Example Java Project Pipeline",
+      "CF_STEP_NAME": "mah-job-name",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CF_BUILD_ID\":\"6410367cee516146a4c4c66e\"}",
+      "ci.job.name": "mah-job-name",
+      "ci.pipeline.id": "6410367cee516146a4c4c66e",
+      "ci.pipeline.name": "My simple project/Example Java Project Pipeline",
+      "ci.pipeline.url": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "ci.provider.name": "codefresh",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "CF_BUILD_ID": "6410367cee516146a4c4c66e",
+      "CF_BUILD_URL": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "CF_PIPELINE_NAME": "My simple project/Example Java Project Pipeline",
+      "CF_STEP_NAME": "mah-job-name",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CF_BUILD_ID\":\"6410367cee516146a4c4c66e\"}",
+      "ci.job.name": "mah-job-name",
+      "ci.pipeline.id": "6410367cee516146a4c4c66e",
+      "ci.pipeline.name": "My simple project/Example Java Project Pipeline",
+      "ci.pipeline.url": "https://g.codefresh.io/build/6410367cee516146a4c4c66e",
+      "ci.provider.name": "codefresh",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/github.json b/main/civisibility/utils/testdata/fixtures/providers/github.json
new file mode 100644
index 0000000..3dd5ac6
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/github.json
@@ -0,0 +1,714 @@
+[
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "master",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://ghenterprise.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://ghenterprise.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://ghenterprise.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://ghenterprise.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://ghenterprise.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "master",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "master",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "master",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "/foo/bar~"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/foo/bar~",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "master",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "/foo/~/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/foo/~/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "master",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "~/foo/bar",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/not-my-home/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "master",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "~foo/bar",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "~foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "master",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "~",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/not-my-home",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "origin/master",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "refs/heads/master",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "refs/heads/feature/one",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "feature/one",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "origin/tags/0.1.0",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "refs/heads/tags/0.1.0",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_HEAD_REF": "origin/other",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "origin/master",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "other",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_HEAD_REF": "refs/heads/other",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "refs/heads/master",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "other",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_HEAD_REF": "refs/heads/feature/other",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REF": "refs/heads/feature/one",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "feature/other",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "foo/bar",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2",
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name",
+      "GITHUB_WORKSPACE": "foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "ci.workspace_path": "foo/bar",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id",
+      "ci.provider.name": "github",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://user:password@github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://user@github.com",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://user:password@github.com:1234",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://github.com:1234\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://github.com:1234/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://github.com:1234/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com:1234/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://user:password@1.1.1.1",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://1.1.1.1\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://1.1.1.1/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://1.1.1.1/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1/ghactions-repo.git"
+    }
+  ],
+  [
+    {
+      "GITHUB_ACTION": "run",
+      "GITHUB_JOB": "github-job-name",
+      "GITHUB_REPOSITORY": "ghactions-repo",
+      "GITHUB_RUN_ATTEMPT": "ghactions-run-attempt",
+      "GITHUB_RUN_ID": "ghactions-pipeline-id",
+      "GITHUB_RUN_NUMBER": "ghactions-pipeline-number",
+      "GITHUB_SERVER_URL": "https://user:password@1.1.1.1:1234",
+      "GITHUB_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GITHUB_WORKFLOW": "ghactions-pipeline-name"
+    },
+    {
+      "_dd.ci.env_vars": "{\"GITHUB_SERVER_URL\":\"https://1.1.1.1:1234\",\"GITHUB_REPOSITORY\":\"ghactions-repo\",\"GITHUB_RUN_ID\":\"ghactions-pipeline-id\",\"GITHUB_RUN_ATTEMPT\":\"ghactions-run-attempt\"}",
+      "ci.job.name": "github-job-name",
+      "ci.job.url": "https://1.1.1.1:1234/ghactions-repo/commit/b9f0fb3fdbb94c9d24b2c75b49663122a529e123/checks",
+      "ci.pipeline.id": "ghactions-pipeline-id",
+      "ci.pipeline.name": "ghactions-pipeline-name",
+      "ci.pipeline.number": "ghactions-pipeline-number",
+      "ci.pipeline.url": "https://1.1.1.1:1234/ghactions-repo/actions/runs/ghactions-pipeline-id/attempts/ghactions-run-attempt",
+      "ci.provider.name": "github",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/ghactions-repo.git"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/gitlab.json b/main/civisibility/utils/testdata/fixtures/providers/gitlab.json
new file mode 100644
index 0000000..24882c1
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/gitlab.json
@@ -0,0 +1,1047 @@
+[
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://gitlab.com/repo/myrepo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar~",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar~",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://gitlab.com/repo/myrepo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/~/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/~/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://gitlab.com/repo/myrepo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "~/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "GITLAB_CI": "gitlab",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/not-my-home/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://gitlab.com/repo/myrepo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "~foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "GITLAB_CI": "gitlab",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "~foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://gitlab.com/repo/myrepo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "~",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "GITLAB_CI": "gitlab",
+      "HOME": "/not-my-home",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/not-my-home",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://gitlab.com/repo/myrepo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "refs/heads/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://gitlab.com/repo/myrepo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "refs/heads/feature/one",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "feature/one",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://gitlab.com/repo/myrepo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TAG": "origin/tags/0.1.0",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://gitlab.com/repo/myrepo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TAG": "refs/heads/tags/0.1.0",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://gitlab.com/repo/myrepo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TAG": "0.1.0",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://gitlab.com/repo/myrepo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "http://hostname.com/repo",
+      "DD_TEST_CASE_NAME": "http-repository-url-no-git-suffix",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "ssh://host.xz:54321/path/to/repo/",
+      "DD_TEST_CASE_NAME": "ssh-repository-url-no-git-suffix",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo/"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "http://hostname.com/repo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "http://user@hostname.com/repo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "http://user%E2%82%AC@hostname.com/repo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "http://user:pwd@hostname.com/repo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "git@hostname.com:org/repo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@hostname.com:org/repo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "http://user:pwd@hostname.com:1234/repo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com:1234/repo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "http://user:pwd@1.1.1.1/repo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://1.1.1.1/repo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "http://user:pwd@1.1.1.1:1234/repo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://1.1.1.1:1234/repo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "http://user:pwd@1.1.1.1:1234/repo_with_@_yeah.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://1.1.1.1:1234/repo_with_@_yeah.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ],
+  [
+    {
+      "CI_COMMIT_AUTHOR": "John Doe <john@doe.com>",
+      "CI_COMMIT_MESSAGE": "gitlab-git-commit-message",
+      "CI_COMMIT_REF_NAME": "origin/master",
+      "CI_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "CI_COMMIT_TIMESTAMP": "2021-07-21T11:43:07-04:00",
+      "CI_JOB_ID": "gitlab-job-id",
+      "CI_JOB_NAME": "gitlab-job-name",
+      "CI_JOB_STAGE": "gitlab-stage-name",
+      "CI_JOB_URL": "https://gitlab.com/job",
+      "CI_PIPELINE_ID": "gitlab-pipeline-id",
+      "CI_PIPELINE_IID": "gitlab-pipeline-number",
+      "CI_PIPELINE_URL": "https://foo/repo/-/pipelines/1234",
+      "CI_PROJECT_DIR": "/foo/bar",
+      "CI_PROJECT_PATH": "gitlab-pipeline-name",
+      "CI_PROJECT_URL": "https://gitlab.com/repo",
+      "CI_REPOSITORY_URL": "https://gitlab.com/repo/myrepo.git",
+      "CI_RUNNER_ID": "9393040",
+      "CI_RUNNER_TAGS": "[\"arch:arm64\",\"linux\"]",
+      "GITLAB_CI": "gitlab"
+    },
+    {
+      "_dd.ci.env_vars": "{\"CI_PROJECT_URL\":\"https://gitlab.com/repo\",\"CI_PIPELINE_ID\":\"gitlab-pipeline-id\",\"CI_JOB_ID\":\"gitlab-job-id\"}",
+      "ci.job.name": "gitlab-job-name",
+      "ci.job.url": "https://gitlab.com/job",
+      "ci.node.labels": "[\"arch:arm64\",\"linux\"]",
+      "ci.node.name": "9393040",
+      "ci.pipeline.id": "gitlab-pipeline-id",
+      "ci.pipeline.name": "gitlab-pipeline-name",
+      "ci.pipeline.number": "gitlab-pipeline-number",
+      "ci.pipeline.url": "https://foo/repo/-/pipelines/1234",
+      "ci.provider.name": "gitlab",
+      "ci.stage.name": "gitlab-stage-name",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.author.date": "2021-07-21T11:43:07-04:00",
+      "git.commit.author.email": "john@doe.com",
+      "git.commit.author.name": "John Doe",
+      "git.commit.message": "gitlab-git-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://gitlab.com/repo/myrepo.git"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/jenkins.json b/main/civisibility/utils/testdata/fixtures/providers/jenkins.json
new file mode 100644
index 0000000..38d4487
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/jenkins.json
@@ -0,0 +1,904 @@
+[
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL_1": "https://jenkins.com/repo/sample.git",
+      "GIT_URL_2": "https://jenkins.com/repo/otherSample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar~"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar~",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/~/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/~/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "HOME": "/not-my-home",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job",
+      "USERPROFILE": "/not-my-home",
+      "WORKSPACE": "~/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/not-my-home/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "HOME": "/not-my-home",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job",
+      "USERPROFILE": "/not-my-home",
+      "WORKSPACE": "~foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "~foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "HOME": "/not-my-home",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job",
+      "USERPROFILE": "/not-my-home",
+      "WORKSPACE": "~"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/not-my-home",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "refs/heads/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName/master",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "refs/heads/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName/another",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName/another",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "refs/heads/feature/one",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName/feature/one",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "feature/one",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "refs/heads/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName/KEY1=VALUE1,KEY2=VALUE2",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "refs/heads/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName/KEY1=VALUE1,KEY2=VALUE2/master",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/tags/0.1.0",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "refs/heads/tags/0.1.0",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "https://jenkins.com/repo/sample.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://jenkins.com/repo/sample.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "http://hostname.com/repo.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "http://user@hostname.com/repo.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "http://user%E2%82%AC@hostname.com/repo.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "http://user:pwd@hostname.com/repo.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "http://hostname.com/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_BRANCH": "origin/master",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL": "git@hostname.com:org/repo.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_NAME": "jobName",
+      "JOB_URL": "https://jenkins.com/job",
+      "WORKSPACE": "/foo/bar"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.name": "jobName",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@hostname.com:org/repo.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "DD_TEST_CASE_NAME": "http-repository-url-no-git-suffix",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL_1": "https://github.com/DataDog/dogweb",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "DD_TEST_CASE_NAME": "ssh-repository-url-no-git-suffix",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL_1": "ssh://host.xz:54321/path/to/repo/",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo/"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL_1": "https://user:password@github.com/DataDog/dogweb.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL_1": "https://user@github.com/DataDog/dogweb.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL_1": "https://user:password@github.com:1234/DataDog/dogweb.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com:1234/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL_1": "https://user:password@1.1.1.1/DataDog/dogweb.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL_1": "https://user:password@1.1.1.1:1234/DataDog/dogweb.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL_1": "https://user:password@1.1.1.1:1234/DataDog/dogweb_with_@_yeah.git",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/DataDog/dogweb_with_@_yeah.git"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL_1": "ssh://user@host.xz:54321/path/to/repo.git/",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "GIT_URL_1": "ssh://user:password@host.xz:54321/path/to/repo.git/",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ],
+  [
+    {
+      "BUILD_NUMBER": "jenkins-pipeline-number",
+      "BUILD_TAG": "jenkins-pipeline-id",
+      "BUILD_URL": "https://jenkins.com/pipeline",
+      "DD_CUSTOM_TRACE_ID": "jenkins-custom-trace-id",
+      "GIT_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "JENKINS_URL": "jenkins",
+      "JOB_URL": "https://jenkins.com/job",
+      "NODE_LABELS": "built-in linux",
+      "NODE_NAME": "my-node"
+    },
+    {
+      "_dd.ci.env_vars": "{\"DD_CUSTOM_TRACE_ID\":\"jenkins-custom-trace-id\"}",
+      "ci.node.labels": "[\"built-in\",\"linux\"]",
+      "ci.node.name": "my-node",
+      "ci.pipeline.id": "jenkins-pipeline-id",
+      "ci.pipeline.number": "jenkins-pipeline-number",
+      "ci.pipeline.url": "https://jenkins.com/pipeline",
+      "ci.provider.name": "jenkins",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/teamcity.json b/main/civisibility/utils/testdata/fixtures/providers/teamcity.json
new file mode 100644
index 0000000..086c1c1
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/teamcity.json
@@ -0,0 +1,78 @@
+[
+  [
+    {
+      "BUILD_URL": "https://teamcity.com/repo",
+      "TEAMCITY_BUILDCONF_NAME": "Test 1",
+      "TEAMCITY_VERSION": "2022.10 (build 116751)"
+    },
+    {
+      "ci.job.name": "Test 1",
+      "ci.job.url": "https://teamcity.com/repo",
+      "ci.provider.name": "teamcity"
+    }
+  ],
+  [
+    {
+      "BUILD_URL": "https://teamcity.com/repo",
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "TEAMCITY_BUILDCONF_NAME": "Test 1",
+      "TEAMCITY_VERSION": "2022.10 (build 116751)"
+    },
+    {
+      "ci.job.name": "Test 1",
+      "ci.job.url": "https://teamcity.com/repo",
+      "ci.provider.name": "teamcity",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "BUILD_URL": "https://teamcity.com/repo",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2",
+      "TEAMCITY_BUILDCONF_NAME": "Test 1",
+      "TEAMCITY_VERSION": "2022.10 (build 116751)"
+    },
+    {
+      "ci.job.name": "Test 1",
+      "ci.job.url": "https://teamcity.com/repo",
+      "ci.provider.name": "teamcity",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/travisci.json b/main/civisibility/utils/testdata/fixtures/providers/travisci.json
new file mode 100644
index 0000000..2131938
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/travisci.json
@@ -0,0 +1,525 @@
+[
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/tags/0.1.0",
+      "TRAVIS_BUILD_DIR": "/foo/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo",
+      "TRAVIS_TAG": "origin/tags/0.1.0"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "refs/heads/tags/0.1.0",
+      "TRAVIS_BUILD_DIR": "/foo/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo",
+      "TRAVIS_TAG": "refs/heads/tags/0.1.0"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/master",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "git.branch": "master",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/master",
+      "TRAVIS_BUILD_DIR": "foo/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/master",
+      "TRAVIS_BUILD_DIR": "/foo/bar~",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/foo/bar~",
+      "git.branch": "master",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/master",
+      "TRAVIS_BUILD_DIR": "/foo/~/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/foo/~/bar",
+      "git.branch": "master",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "HOME": "/not-my-home",
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/master",
+      "TRAVIS_BUILD_DIR": "~/foo/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/not-my-home/foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/master",
+      "TRAVIS_BUILD_DIR": "~foo/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "~foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "HOME": "/not-my-home",
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/master",
+      "TRAVIS_BUILD_DIR": "~",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo",
+      "USERPROFILE": "/not-my-home"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/not-my-home",
+      "git.branch": "master",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/master",
+      "TRAVIS_BUILD_DIR": "/foo/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "refs/heads/master",
+      "TRAVIS_BUILD_DIR": "/foo/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "refs/heads/feature/one",
+      "TRAVIS_BUILD_DIR": "/foo/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "feature/one",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/other",
+      "TRAVIS_BUILD_DIR": "/foo/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_PULL_REQUEST_BRANCH": "origin/master",
+      "TRAVIS_PULL_REQUEST_SLUG": "user/repo",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/other",
+      "TRAVIS_BUILD_DIR": "/foo/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_PULL_REQUEST_BRANCH": "refs/heads/master",
+      "TRAVIS_PULL_REQUEST_SLUG": "user/repo",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "master",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/other",
+      "TRAVIS_BUILD_DIR": "/foo/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_PULL_REQUEST_BRANCH": "refs/heads/feature/one",
+      "TRAVIS_PULL_REQUEST_SLUG": "user/repo",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/foo/bar",
+      "git.branch": "feature/one",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_BRANCH": "user-supplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "TRAVIS": "travisCI",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "git.branch": "user-supplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2",
+      "TRAVIS": "travisCI",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ],
+  [
+    {
+      "TRAVIS": "travisCI",
+      "TRAVIS_BRANCH": "origin/tags/0.1.0",
+      "TRAVIS_BUILD_DIR": "/foo/bar",
+      "TRAVIS_BUILD_ID": "travis-pipeline-id",
+      "TRAVIS_BUILD_NUMBER": "travis-pipeline-number",
+      "TRAVIS_BUILD_WEB_URL": "https://travisci.com/pipeline",
+      "TRAVIS_COMMIT": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "TRAVIS_COMMIT_MESSAGE": "travis-commit-message",
+      "TRAVIS_JOB_WEB_URL": "https://travisci.com/job",
+      "TRAVIS_REPO_SLUG": "user/repo",
+      "TRAVIS_TAG": "origin/tags/0.1.0"
+    },
+    {
+      "ci.job.url": "https://travisci.com/job",
+      "ci.pipeline.id": "travis-pipeline-id",
+      "ci.pipeline.name": "user/repo",
+      "ci.pipeline.number": "travis-pipeline-number",
+      "ci.pipeline.url": "https://travisci.com/pipeline",
+      "ci.provider.name": "travisci",
+      "ci.workspace_path": "/foo/bar",
+      "git.commit.message": "travis-commit-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/user/repo.git",
+      "git.tag": "0.1.0"
+    }
+  ]
+]
diff --git a/main/civisibility/utils/testdata/fixtures/providers/usersupplied.json b/main/civisibility/utils/testdata/fixtures/providers/usersupplied.json
new file mode 100644
index 0000000..f07f4e3
--- /dev/null
+++ b/main/civisibility/utils/testdata/fixtures/providers/usersupplied.json
@@ -0,0 +1,400 @@
+[
+  [
+    {
+      "DD_GIT_BRANCH": "usersupplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git"
+    },
+    {
+      "git.branch": "usersupplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_BRANCH": "origin/usersupplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git"
+    },
+    {
+      "git.branch": "usersupplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_BRANCH": "refs/heads/usersupplied-branch",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git"
+    },
+    {
+      "git.branch": "usersupplied-branch",
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_BRANCH": "origin/tags/0.1.0",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "DD_GIT_BRANCH": "refs/heads/tags/0.1.0",
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.1.0"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "git@github.com:DataDog/userrepo.git",
+      "DD_GIT_TAG": "0.0.2"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "git@github.com:DataDog/userrepo.git",
+      "git.tag": "0.0.2"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "https://github.com/DataDog/dogweb",
+      "DD_TEST_CASE_NAME": "http-repository-url-no-git-suffix"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "ssh://host.xz:54321/path/to/repo/",
+      "DD_TEST_CASE_NAME": "ssh-repository-url-no-git-suffix"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo/"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "https://user:password@github.com/DataDog/dogweb.git"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "https://user@github.com/DataDog/dogweb.git"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "https://user:password@github.com:1234/DataDog/dogweb.git"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://github.com:1234/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "https://user:password@1.1.1.1/DataDog/dogweb.git"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "https://user:password@1.1.1.1:1234/DataDog/dogweb.git"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/DataDog/dogweb.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "https://user:password@1.1.1.1:1234/DataDog/dogweb_with_@_yeah.git"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "https://1.1.1.1:1234/DataDog/dogweb_with_@_yeah.git"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "ssh://user@host.xz:54321/path/to/repo.git/"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ],
+  [
+    {
+      "DD_GIT_COMMIT_AUTHOR_DATE": "usersupplied-authordate",
+      "DD_GIT_COMMIT_AUTHOR_EMAIL": "usersupplied-authoremail",
+      "DD_GIT_COMMIT_AUTHOR_NAME": "usersupplied-authorname",
+      "DD_GIT_COMMIT_COMMITTER_DATE": "usersupplied-comitterdate",
+      "DD_GIT_COMMIT_COMMITTER_EMAIL": "usersupplied-comitteremail",
+      "DD_GIT_COMMIT_COMMITTER_NAME": "usersupplied-comittername",
+      "DD_GIT_COMMIT_MESSAGE": "usersupplied-message",
+      "DD_GIT_COMMIT_SHA": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "DD_GIT_REPOSITORY_URL": "ssh://user:password@host.xz:54321/path/to/repo.git/"
+    },
+    {
+      "git.commit.author.date": "usersupplied-authordate",
+      "git.commit.author.email": "usersupplied-authoremail",
+      "git.commit.author.name": "usersupplied-authorname",
+      "git.commit.committer.date": "usersupplied-comitterdate",
+      "git.commit.committer.email": "usersupplied-comitteremail",
+      "git.commit.committer.name": "usersupplied-comittername",
+      "git.commit.message": "usersupplied-message",
+      "git.commit.sha": "b9f0fb3fdbb94c9d24b2c75b49663122a529e123",
+      "git.repository_url": "ssh://host.xz:54321/path/to/repo.git/"
+    }
+  ]
+]
diff --git a/main/osinfo/osinfo.go b/main/osinfo/osinfo.go
new file mode 100644
index 0000000..7519a91
--- /dev/null
+++ b/main/osinfo/osinfo.go
@@ -0,0 +1,21 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2022 Datadog, Inc.
+
+// Package osinfo provides information about the current operating system release
+package osinfo
+
+// OSName returns the name of the operating system, including the distribution
+// for Linux when possible.
+func OSName() string {
+	// call out to OS-specific implementation
+	return osName()
+}
+
+// OSVersion returns the operating system release, e.g. major/minor version
+// number and build ID.
+func OSVersion() string {
+	// call out to OS-specific implementation
+	return osVersion()
+}
diff --git a/main/osinfo/osinfo_darwin.go b/main/osinfo/osinfo_darwin.go
new file mode 100644
index 0000000..32ead5f
--- /dev/null
+++ b/main/osinfo/osinfo_darwin.go
@@ -0,0 +1,24 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2016 Datadog, Inc.
+
+package osinfo
+
+import (
+	"os/exec"
+	"runtime"
+	"strings"
+)
+
+func osName() string {
+	return runtime.GOOS
+}
+
+func osVersion() string {
+	out, err := exec.Command("sw_vers", "-productVersion").Output()
+	if err != nil {
+		return "unknown"
+	}
+	return strings.Trim(string(out), "\n")
+}
diff --git a/main/osinfo/osinfo_default.go b/main/osinfo/osinfo_default.go
new file mode 100644
index 0000000..72d70d3
--- /dev/null
+++ b/main/osinfo/osinfo_default.go
@@ -0,0 +1,21 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2016 Datadog, Inc.
+
+//go:build !windows && !linux && !darwin && !freebsd
+// +build !windows,!linux,!darwin,!freebsd
+
+package osinfo
+
+import (
+	"runtime"
+)
+
+func osName() string {
+	return runtime.GOOS
+}
+
+func osVersion() string {
+	return "unknown"
+}
diff --git a/main/osinfo/osinfo_freebsd.go b/main/osinfo/osinfo_freebsd.go
new file mode 100644
index 0000000..543f2ff
--- /dev/null
+++ b/main/osinfo/osinfo_freebsd.go
@@ -0,0 +1,24 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2016 Datadog, Inc.
+
+package osinfo
+
+import (
+	"os/exec"
+	"runtime"
+	"strings"
+)
+
+func osName() string {
+	return runtime.GOOS
+}
+
+func osVersion() string {
+	out, err := exec.Command("uname", "-r").Output()
+	if err != nil {
+		return "unknown"
+	}
+	return strings.Split(string(out), "-")[0]
+}
diff --git a/main/osinfo/osinfo_linux.go b/main/osinfo/osinfo_linux.go
new file mode 100644
index 0000000..96d1e66
--- /dev/null
+++ b/main/osinfo/osinfo_linux.go
@@ -0,0 +1,52 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2016 Datadog, Inc.
+
+package osinfo
+
+import (
+	"bufio"
+	"os"
+	"strings"
+)
+
+func osName() string {
+	f, err := os.Open("/etc/os-release")
+	if err != nil {
+		return "Linux (Unknown Distribution)"
+	}
+	defer f.Close()
+	s := bufio.NewScanner(f)
+	name := "Linux (Unknown Distribution)"
+	for s.Scan() {
+		parts := strings.SplitN(s.Text(), "=", 2)
+		switch parts[0] {
+		case "NAME":
+			name = strings.Trim(parts[1], "\"")
+		}
+	}
+	return name
+}
+
+func osVersion() string {
+	f, err := os.Open("/etc/os-release")
+	if err != nil {
+		return "unknown"
+	}
+	defer f.Close()
+	s := bufio.NewScanner(f)
+	version := "unknown"
+	for s.Scan() {
+		parts := strings.SplitN(s.Text(), "=", 2)
+		switch parts[0] {
+		case "VERSION":
+			version = strings.Trim(parts[1], "\"")
+		case "VERSION_ID":
+			if version == "" {
+				version = strings.Trim(parts[1], "\"")
+			}
+		}
+	}
+	return version
+}
diff --git a/main/osinfo/osinfo_windows.go b/main/osinfo/osinfo_windows.go
new file mode 100644
index 0000000..659bd9c
--- /dev/null
+++ b/main/osinfo/osinfo_windows.go
@@ -0,0 +1,54 @@
+// Unless explicitly stated otherwise all files in this repository are licensed
+// under the Apache License Version 2.0.
+// This product includes software developed at Datadog (https://www.datadoghq.com/).
+// Copyright 2016 Datadog, Inc.
+
+package osinfo
+
+import (
+	"fmt"
+	"runtime"
+	"strings"
+
+	"golang.org/x/sys/windows/registry"
+)
+
+func osName() string {
+	return runtime.GOOS
+}
+
+func osVersion() string {
+	k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE)
+	if err != nil {
+		return "unknown"
+	}
+	defer k.Close()
+
+	var version strings.Builder
+
+	maj, _, err := k.GetIntegerValue("CurrentMajorVersionNumber")
+	if err == nil {
+		version.WriteString(fmt.Sprintf("%d", maj))
+		min, _, err := k.GetIntegerValue("CurrentMinorVersionNumber")
+		if err == nil {
+			version.WriteString(fmt.Sprintf(".%d", min))
+		}
+	} else {
+		version.WriteString("unknown")
+	}
+
+	ed, _, err := k.GetStringValue("EditionID")
+	if err == nil {
+		version.WriteString(" " + ed)
+	} else {
+		version.WriteString(" Unknown Edition")
+	}
+
+	build, _, err := k.GetStringValue("CurrentBuild")
+	if err == nil {
+		version.WriteString(" Build " + build)
+	} else {
+		version.WriteString(" Unknown Build")
+	}
+	return version.String()
+}
diff --git a/main/subtractions/subtractions.go b/main/subtractions/subtractions.go
new file mode 100644
index 0000000..4949d21
--- /dev/null
+++ b/main/subtractions/subtractions.go
@@ -0,0 +1,5 @@
+package subtractions
+
+func subtractNumbers(x int, y int) int {
+	return x - y
+}
diff --git a/main/subtractions/subtractions_test.go b/main/subtractions/subtractions_test.go
new file mode 100644
index 0000000..eaf49e1
--- /dev/null
+++ b/main/subtractions/subtractions_test.go
@@ -0,0 +1,19 @@
+package subtractions
+
+import "testing"
+
+func Test_SubtractNumbers(t *testing.T) {
+	ans := subtractNumbers(10, 7)
+
+	if ans != 3 {
+		t.Fatal("Expected 3 but got ", ans)
+	}
+}
+
+func Test_SubtractNumbers2(t *testing.T) {
+	ans := subtractNumbers(10, 6)
+
+	if ans != 4 {
+		t.Fatal("Expected 4 but got ", ans)
+	}
+}
diff --git a/python_test.py b/python_test.py
new file mode 100644
index 0000000..1aaa797
--- /dev/null
+++ b/python_test.py
@@ -0,0 +1,5 @@
+def test_one():
+    assert 5 == 5
+
+def test_two():
+    assert 4 == 4