From 7698a06a79d88acf59d95a24fa6bf49ab930bda2 Mon Sep 17 00:00:00 2001 From: Tony Redondo Date: Mon, 8 Jul 2024 12:02:23 +0200 Subject: [PATCH] [CI Visibility] Initial internal consts and utilities (#2736) Co-authored-by: Manuel Palenzuela Merino Co-authored-by: liashenko --- CODEOWNERS | 3 + ddtrace/tracer/civisibility_payload.go | 117 ++ ddtrace/tracer/civisibility_payload_test.go | 120 ++ ddtrace/tracer/civisibility_transport.go | 200 ++++ ddtrace/tracer/civisibility_transport_test.go | 112 ++ ddtrace/tracer/civisibility_tslv.go | 441 +++++++ ddtrace/tracer/civisibility_tslv_msgp.go | 924 +++++++++++++++ ddtrace/tracer/civisibility_writer.go | 119 ++ ddtrace/tracer/civisibility_writer_test.go | 101 ++ ddtrace/tracer/option.go | 12 + ddtrace/tracer/tracer.go | 4 +- internal/civisibility/constants/ci.go | 44 + internal/civisibility/constants/env.go | 27 + internal/civisibility/constants/git.go | 52 + internal/civisibility/constants/os.go | 21 + internal/civisibility/constants/runtime.go | 16 + internal/civisibility/constants/span_types.go | 28 + internal/civisibility/constants/tags.go | 43 + internal/civisibility/constants/test_tags.go | 90 ++ .../civisibility/integrations/civisibility.go | 118 ++ .../integrations/gotesting/reflections.go | 103 ++ .../gotesting/reflections_test.go | 150 +++ .../integrations/gotesting/testing.go | 353 ++++++ .../integrations/gotesting/testingB.go | 336 ++++++ .../integrations/gotesting/testingT.go | 216 ++++ .../integrations/gotesting/testing_test.go | 350 ++++++ .../civisibility/integrations/manual_api.go | 218 ++++ .../integrations/manual_api_ddtest.go | 157 +++ .../integrations/manual_api_ddtestmodule.go | 140 +++ .../integrations/manual_api_ddtestsession.go | 170 +++ .../integrations/manual_api_ddtestsuite.go | 120 ++ .../manual_api_mocktracer_test.go | 277 +++++ .../integrations/manual_api_test.go | 329 ++++++ internal/civisibility/utils/ci_providers.go | 569 +++++++++ .../civisibility/utils/ci_providers_test.go | 116 ++ internal/civisibility/utils/codeowners.go | 304 +++++ .../civisibility/utils/codeowners_test.go | 159 +++ .../civisibility/utils/environmentTags.go | 119 ++ .../utils/environmentTags_test.go | 40 + internal/civisibility/utils/git.go | 104 ++ internal/civisibility/utils/git_test.go | 62 + internal/civisibility/utils/home.go | 120 ++ internal/civisibility/utils/home_test.go | 97 ++ internal/civisibility/utils/names.go | 82 ++ internal/civisibility/utils/names_test.go | 40 + .../fixtures/codeowners/CODEOWNERS_GITHUB | 54 + .../fixtures/codeowners/CODEOWNERS_GITLAB | 64 + .../testdata/fixtures/providers/appveyor.json | 555 +++++++++ .../fixtures/providers/awscodepipeline.json | 62 + .../fixtures/providers/azurepipelines.json | 935 +++++++++++++++ .../fixtures/providers/bitbucket.json | 595 ++++++++++ .../testdata/fixtures/providers/bitrise.json | 694 +++++++++++ .../testdata/fixtures/providers/buddy.json | 483 ++++++++ .../fixtures/providers/buildkite.json | 1022 ++++++++++++++++ .../testdata/fixtures/providers/circleci.json | 754 ++++++++++++ .../fixtures/providers/codefresh.json | 162 +++ .../testdata/fixtures/providers/github.json | 714 +++++++++++ .../testdata/fixtures/providers/gitlab.json | 1047 +++++++++++++++++ .../testdata/fixtures/providers/jenkins.json | 904 ++++++++++++++ .../testdata/fixtures/providers/teamcity.json | 78 ++ .../testdata/fixtures/providers/travisci.json | 525 +++++++++ .../fixtures/providers/usersupplied.json | 400 +++++++ 62 files changed, 16370 insertions(+), 1 deletion(-) create mode 100644 ddtrace/tracer/civisibility_payload.go create mode 100644 ddtrace/tracer/civisibility_payload_test.go create mode 100644 ddtrace/tracer/civisibility_transport.go create mode 100644 ddtrace/tracer/civisibility_transport_test.go create mode 100644 ddtrace/tracer/civisibility_tslv.go create mode 100644 ddtrace/tracer/civisibility_tslv_msgp.go create mode 100644 ddtrace/tracer/civisibility_writer.go create mode 100644 ddtrace/tracer/civisibility_writer_test.go create mode 100644 internal/civisibility/constants/ci.go create mode 100644 internal/civisibility/constants/env.go create mode 100644 internal/civisibility/constants/git.go create mode 100644 internal/civisibility/constants/os.go create mode 100644 internal/civisibility/constants/runtime.go create mode 100644 internal/civisibility/constants/span_types.go create mode 100644 internal/civisibility/constants/tags.go create mode 100644 internal/civisibility/constants/test_tags.go create mode 100644 internal/civisibility/integrations/civisibility.go create mode 100644 internal/civisibility/integrations/gotesting/reflections.go create mode 100644 internal/civisibility/integrations/gotesting/reflections_test.go create mode 100644 internal/civisibility/integrations/gotesting/testing.go create mode 100644 internal/civisibility/integrations/gotesting/testingB.go create mode 100644 internal/civisibility/integrations/gotesting/testingT.go create mode 100644 internal/civisibility/integrations/gotesting/testing_test.go create mode 100644 internal/civisibility/integrations/manual_api.go create mode 100644 internal/civisibility/integrations/manual_api_ddtest.go create mode 100644 internal/civisibility/integrations/manual_api_ddtestmodule.go create mode 100644 internal/civisibility/integrations/manual_api_ddtestsession.go create mode 100644 internal/civisibility/integrations/manual_api_ddtestsuite.go create mode 100644 internal/civisibility/integrations/manual_api_mocktracer_test.go create mode 100644 internal/civisibility/integrations/manual_api_test.go create mode 100644 internal/civisibility/utils/ci_providers.go create mode 100644 internal/civisibility/utils/ci_providers_test.go create mode 100644 internal/civisibility/utils/codeowners.go create mode 100644 internal/civisibility/utils/codeowners_test.go create mode 100644 internal/civisibility/utils/environmentTags.go create mode 100644 internal/civisibility/utils/environmentTags_test.go create mode 100644 internal/civisibility/utils/git.go create mode 100644 internal/civisibility/utils/git_test.go create mode 100644 internal/civisibility/utils/home.go create mode 100644 internal/civisibility/utils/home_test.go create mode 100644 internal/civisibility/utils/names.go create mode 100644 internal/civisibility/utils/names_test.go create mode 100644 internal/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITHUB create mode 100644 internal/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITLAB create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/appveyor.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/awscodepipeline.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/azurepipelines.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/bitbucket.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/bitrise.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/buddy.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/buildkite.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/circleci.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/codefresh.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/github.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/gitlab.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/jenkins.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/teamcity.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/travisci.json create mode 100644 internal/civisibility/utils/testdata/fixtures/providers/usersupplied.json diff --git a/CODEOWNERS b/CODEOWNERS index f2a329aa03..6bba60c12b 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -28,6 +28,9 @@ go.sum /datastreams @Datadog/data-streams-monitoring /internal/datastreams @Datadog/data-streams-monitoring +# civisibility +/internal/civisibility @DataDog/ci-app-libraries + # Gitlab configuration .gitlab-ci.yml @DataDog/dd-trace-go-guild @DataDog/apm-core-reliability-and-performance /.gitlab-ci @DataDog/dd-trace-go-guild @DataDog/apm-core-reliability-and-performance diff --git a/ddtrace/tracer/civisibility_payload.go b/ddtrace/tracer/civisibility_payload.go new file mode 100644 index 0000000000..df8ffc04cc --- /dev/null +++ b/ddtrace/tracer/civisibility_payload.go @@ -0,0 +1,117 @@ +// 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 tracer + +import ( + "bytes" + "sync/atomic" + + "github.com/tinylib/msgp/msgp" + "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" + "gopkg.in/DataDog/dd-trace-go.v1/internal/version" +) + +// ciVisibilityPayload represents a payload specifically designed for CI Visibility events. +// It embeds the generic payload structure and adds methods to handle CI Visibility specific data. +type ciVisibilityPayload struct { + *payload +} + +// push adds a new CI Visibility event to the payload buffer. +// It grows the buffer to accommodate the new event, encodes the event in MessagePack format, and updates the event count. +// +// Parameters: +// +// event - The CI Visibility event to be added to the payload. +// +// Returns: +// +// An error if encoding the event fails. +func (p *ciVisibilityPayload) push(event *ciVisibilityEvent) error { + p.buf.Grow(event.Msgsize()) + if err := msgp.Encode(&p.buf, event); err != nil { + return err + } + atomic.AddUint32(&p.count, 1) + p.updateHeader() + return nil +} + +// newCiVisibilityPayload creates a new instance of civisibilitypayload. +// +// Returns: +// +// A pointer to a newly initialized civisibilitypayload instance. +func newCiVisibilityPayload() *ciVisibilityPayload { + return &ciVisibilityPayload{newPayload()} +} + +// getBuffer retrieves the complete body of the CI Visibility payload, including metadata. +// It reads the current payload buffer, adds metadata, and encodes the entire payload in MessagePack format. +// +// Parameters: +// +// config - A pointer to the config structure containing environment settings. +// +// Returns: +// +// A pointer to a bytes.Buffer containing the encoded CI Visibility payload. +// An error if reading from the buffer or encoding the payload fails. +func (p *ciVisibilityPayload) getBuffer(config *config) (*bytes.Buffer, error) { + + /* + The Payload format in the CI Visibility protocol is like this: + { + "version": 1, + "metadata": { + "*": { + "runtime-id": "...", + "language": "...", + "library_version": "...", + "env": "..." + } + }, + "events": [ + // ... + ] + } + + The event format can be found in the `civisibility_tslv.go` file in the ciVisibilityEvent documentation + */ + + // Create a buffer to read the current payload + payloadBuf := new(bytes.Buffer) + if _, err := payloadBuf.ReadFrom(p.payload); err != nil { + return nil, err + } + + // Create the metadata map + allMetadata := map[string]string{ + "language": "go", + "runtime-id": globalconfig.RuntimeID(), + "library_version": version.Tag, + } + if config.env != "" { + allMetadata["env"] = config.env + } + + // Create the visibility payload + visibilityPayload := ciTestCyclePayload{ + Version: 1, + Metadata: map[string]map[string]string{ + "*": allMetadata, + }, + Events: payloadBuf.Bytes(), + } + + // Create a new buffer to encode the visibility payload in MessagePack format + encodedBuf := new(bytes.Buffer) + if err := msgp.Encode(encodedBuf, &visibilityPayload); err != nil { + return nil, err + } + + return encodedBuf, nil +} diff --git a/ddtrace/tracer/civisibility_payload_test.go b/ddtrace/tracer/civisibility_payload_test.go new file mode 100644 index 0000000000..4057bb36e7 --- /dev/null +++ b/ddtrace/tracer/civisibility_payload_test.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 tracer + +import ( + "bytes" + "io" + "strconv" + "strings" + "sync/atomic" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tinylib/msgp/msgp" +) + +func newCiVisibilityEventsList(n int) []*ciVisibilityEvent { + list := make([]*ciVisibilityEvent, n) + for i := 0; i < n; i++ { + s := newBasicSpan("span.list." + strconv.Itoa(i%5+1)) + s.Start = fixedTime + list[i] = getCiVisibilityEvent(s) + } + + return list +} + +// TestCiVisibilityPayloadIntegrity tests that whatever we push into the payload +// allows us to read the same content as would have been encoded by +// the codec. +func TestCiVisibilityPayloadIntegrity(t *testing.T) { + want := new(bytes.Buffer) + for _, n := range []int{10, 1 << 10, 1 << 17} { + t.Run(strconv.Itoa(n), func(t *testing.T) { + assert := assert.New(t) + p := newCiVisibilityPayload() + var allEvents ciVisibilityEvents + + for i := 0; i < n; i++ { + list := newCiVisibilityEventsList(i%5 + 1) + allEvents = append(allEvents, list...) + for _, event := range list { + p.push(event) + } + } + + want.Reset() + err := msgp.Encode(want, allEvents) + assert.NoError(err) + assert.Equal(want.Len(), p.size()) + assert.Equal(p.itemCount(), len(allEvents)) + + got, err := io.ReadAll(p) + assert.NoError(err) + assert.Equal(want.Bytes(), got) + }) + } +} + +// TestCiVisibilityPayloadDecode ensures that whatever we push into the payload can +// be decoded by the codec. +func TestCiVisibilityPayloadDecode(t *testing.T) { + assert := assert.New(t) + for _, n := range []int{10, 1 << 10} { + t.Run(strconv.Itoa(n), func(t *testing.T) { + p := newCiVisibilityPayload() + for i := 0; i < n; i++ { + list := newCiVisibilityEventsList(i%5 + 1) + for _, event := range list { + p.push(event) + } + } + var got ciVisibilityEvents + err := msgp.Decode(p, &got) + assert.NoError(err) + }) + } +} + +func BenchmarkCiVisibilityPayloadThroughput(b *testing.B) { + b.Run("10K", benchmarkCiVisibilityPayloadThroughput(1)) + b.Run("100K", benchmarkCiVisibilityPayloadThroughput(10)) + b.Run("1MB", benchmarkCiVisibilityPayloadThroughput(100)) +} + +// benchmarkCiVisibilityPayloadThroughput benchmarks the throughput of the payload by subsequently +// pushing a list of civisibility events containing count spans of approximately 10KB in size each, until the +// payload is filled. +func benchmarkCiVisibilityPayloadThroughput(count int) func(*testing.B) { + return func(b *testing.B) { + p := newCiVisibilityPayload() + s := newBasicSpan("X") + s.Meta["key"] = strings.Repeat("X", 10*1024) + e := getCiVisibilityEvent(s) + events := make(ciVisibilityEvents, count) + for i := 0; i < count; i++ { + events[i] = e + } + + b.ReportAllocs() + b.ResetTimer() + reset := func() { + p.header = make([]byte, 8) + p.off = 8 + atomic.StoreUint32(&p.count, 0) + p.buf.Reset() + } + for i := 0; i < b.N; i++ { + reset() + for _, event := range events { + for p.size() < payloadMaxLimit { + p.push(event) + } + } + } + } +} diff --git a/ddtrace/tracer/civisibility_transport.go b/ddtrace/tracer/civisibility_transport.go new file mode 100644 index 0000000000..db64b5d73d --- /dev/null +++ b/ddtrace/tracer/civisibility_transport.go @@ -0,0 +1,200 @@ +// 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 tracer + +import ( + "bytes" + "compress/gzip" + "fmt" + "io" + "net/http" + "os" + "runtime" + "strconv" + "strings" + + "gopkg.in/DataDog/dd-trace-go.v1/internal" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" + "gopkg.in/DataDog/dd-trace-go.v1/internal/log" + "gopkg.in/DataDog/dd-trace-go.v1/internal/version" +) + +// Constants for CI Visibility API paths and subdomains. +const ( + TestCycleSubdomain = "citestcycle-intake" // Subdomain for test cycle intake. + TestCyclePath = "api/v2/citestcycle" // API path for test cycle. + EvpProxyPath = "evp_proxy/v2" // Path for EVP proxy. +) + +// Ensure that civisibilityTransport implements the transport interface. +var _ transport = (*ciVisibilityTransport)(nil) + +// ciVisibilityTransport is a structure that handles sending CI Visibility payloads +// to the Datadog endpoint, either in agentless mode or through the EVP proxy. +type ciVisibilityTransport struct { + config *config // Configuration for the tracer. + testCycleURLPath string // URL path for the test cycle endpoint. + headers map[string]string // HTTP headers to be included in the requests. + agentless bool // Gets if the transport is configured in agentless mode (eg: Gzip support) +} + +// newCiVisibilityTransport creates and initializes a new civisibilityTransport +// based on the provided tracer configuration. It sets up the appropriate headers +// and determines the URL path based on whether agentless mode is enabled. +// +// Parameters: +// +// config - The tracer configuration. +// +// Returns: +// +// A pointer to an initialized civisibilityTransport instance. +func newCiVisibilityTransport(config *config) *ciVisibilityTransport { + // Initialize the default headers with encoder metadata. + defaultHeaders := map[string]string{ + "Datadog-Meta-Lang": "go", + "Datadog-Meta-Lang-Version": strings.TrimPrefix(runtime.Version(), "go"), + "Datadog-Meta-Lang-Interpreter": runtime.Compiler + "-" + runtime.GOARCH + "-" + runtime.GOOS, + "Datadog-Meta-Tracer-Version": version.Tag, + "Content-Type": "application/msgpack", + } + if cid := internal.ContainerID(); cid != "" { + defaultHeaders["Datadog-Container-ID"] = cid + } + if eid := internal.EntityID(); eid != "" { + defaultHeaders["Datadog-Entity-ID"] = eid + } + + // Determine if agentless mode is enabled through an environment variable. + agentlessEnabled := internal.BoolEnv(constants.CIVisibilityAgentlessEnabledEnvironmentVariable, false) + + testCycleURL := "" + if agentlessEnabled { + // Agentless mode is enabled. + APIKeyValue := os.Getenv(constants.APIKeyEnvironmentVariable) + if APIKeyValue == "" { + log.Error("An API key is required for agentless mode. Use the DD_API_KEY env variable to set it") + } + + defaultHeaders["dd-api-key"] = APIKeyValue + + // Check for a custom agentless URL. + agentlessURL := "" + if v := os.Getenv(constants.CIVisibilityAgentlessURLEnvironmentVariable); v != "" { + agentlessURL = v + } + + if agentlessURL == "" { + // Use the standard agentless URL format. + site := "datadoghq.com" + if v := os.Getenv("DD_SITE"); v != "" { + site = v + } + + testCycleURL = fmt.Sprintf("https://%s.%s/%s", TestCycleSubdomain, site, TestCyclePath) + } else { + // Use the custom agentless URL. + testCycleURL = fmt.Sprintf("%s/%s", agentlessURL, TestCyclePath) + } + } else { + // Use agent mode with the EVP proxy. + defaultHeaders["X-Datadog-EVP-Subdomain"] = TestCycleSubdomain + testCycleURL = fmt.Sprintf("%s/%s/%s", config.agentURL.String(), EvpProxyPath, TestCyclePath) + } + + return &ciVisibilityTransport{ + config: config, + testCycleURLPath: testCycleURL, + headers: defaultHeaders, + agentless: agentlessEnabled, + } +} + +// send sends the CI Visibility payload to the Datadog endpoint. +// It prepares the payload, creates the HTTP request, and handles the response. +// +// Parameters: +// +// p - The payload to be sent. +// +// Returns: +// +// An io.ReadCloser for reading the response body, and an error if the operation fails. +func (t *ciVisibilityTransport) send(p *payload) (body io.ReadCloser, err error) { + ciVisibilityPayload := &ciVisibilityPayload{p} + buffer, bufferErr := ciVisibilityPayload.getBuffer(t.config) + if bufferErr != nil { + return nil, fmt.Errorf("cannot create buffer payload: %v", bufferErr) + } + + if t.agentless { + // Compress payload + var gzipBuffer bytes.Buffer + gzipWriter := gzip.NewWriter(&gzipBuffer) + _, err = io.Copy(gzipWriter, buffer) + if err != nil { + return nil, fmt.Errorf("cannot compress request body: %v", err) + } + err = gzipWriter.Close() + if err != nil { + return nil, fmt.Errorf("cannot compress request body: %v", err) + } + buffer = &gzipBuffer + } + + req, err := http.NewRequest("POST", t.testCycleURLPath, buffer) + if err != nil { + return nil, fmt.Errorf("cannot create http request: %v", err) + } + for header, value := range t.headers { + req.Header.Set(header, value) + } + req.Header.Set("Content-Length", strconv.Itoa(buffer.Len())) + if t.agentless { + req.Header.Set("Content-Encoding", "gzip") + } + + response, err := t.config.httpClient.Do(req) + if err != nil { + return nil, err + } + if code := response.StatusCode; code >= 400 { + // error, check the body for context information and + // return a nice error. + msg := make([]byte, 1000) + n, _ := response.Body.Read(msg) + _ = response.Body.Close() + txt := http.StatusText(code) + if n > 0 { + return nil, fmt.Errorf("%s (Status: %s)", msg[:n], txt) + } + return nil, fmt.Errorf("%s", txt) + } + return response.Body, nil +} + +// sendStats is a no-op for CI Visibility transport as it does not support sending stats payloads. +// +// Parameters: +// +// payload - The stats payload to be sent. +// +// Returns: +// +// An error indicating that stats are not supported. +func (t *ciVisibilityTransport) sendStats(*statsPayload) error { + // Stats are not supported by CI Visibility agentless / EVP proxy. + return nil +} + +// endpoint returns the URL path of the test cycle endpoint. +// +// Returns: +// +// The URL path as a string. +func (t *ciVisibilityTransport) endpoint() string { + return t.testCycleURLPath +} diff --git a/ddtrace/tracer/civisibility_transport_test.go b/ddtrace/tracer/civisibility_transport_test.go new file mode 100644 index 0000000000..72240f2c7e --- /dev/null +++ b/ddtrace/tracer/civisibility_transport_test.go @@ -0,0 +1,112 @@ +// 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 tracer + +import ( + "bytes" + "compress/gzip" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tinylib/msgp/msgp" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" +) + +func TestCiVisibilityTransport(t *testing.T) { + t.Run("agentless", func(t *testing.T) { runTransportTest(t, true, true) }) + t.Run("agentless_no_api_key", func(t *testing.T) { runTransportTest(t, true, false) }) + t.Run("agentbased", func(t *testing.T) { runTransportTest(t, false, true) }) +} + +func runTransportTest(t *testing.T, agentless, shouldSetAPIKey bool) { + assert := assert.New(t) + + testCases := []struct { + payload [][]*span + }{ + {getTestTrace(1, 1)}, + {getTestTrace(10, 1)}, + {getTestTrace(100, 10)}, + } + + remainingEvents := 1000 + 10 + 1 + var hits int + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + hits++ + metaLang := r.Header.Get("Datadog-Meta-Lang") + assert.NotNil(metaLang) + + if agentless && shouldSetAPIKey { + apikey := r.Header.Get("dd-api-key") + assert.Equal("12345", apikey) + } + + contentType := r.Header.Get("Content-Type") + assert.Equal("application/msgpack", contentType) + + assert.True(strings.HasSuffix(r.RequestURI, TestCyclePath)) + + bodyBuffer := new(bytes.Buffer) + if r.Header.Get("Content-Encoding") == "gzip" { + gzipReader, err := gzip.NewReader(r.Body) + assert.NoError(err) + + _, err = bodyBuffer.ReadFrom(gzipReader) + assert.NoError(err) + } else { + _, err := bodyBuffer.ReadFrom(r.Body) + assert.NoError(err) + } + + var testCyclePayload ciTestCyclePayload + err := msgp.Decode(bodyBuffer, &testCyclePayload) + assert.NoError(err) + + var events ciVisibilityEvents + err = msgp.Decode(bytes.NewBuffer(testCyclePayload.Events), &events) + assert.NoError(err) + + remainingEvents = remainingEvents - len(events) + })) + defer srv.Close() + + parsedURL, _ := url.Parse(srv.URL) + c := config{ + ciVisibilityEnabled: true, + httpClient: defaultHTTPClient(0), + agentURL: parsedURL, + } + + // Set CI Visibility environment variables for the test + if agentless { + t.Setenv(constants.CIVisibilityAgentlessEnabledEnvironmentVariable, "1") + t.Setenv(constants.CIVisibilityAgentlessURLEnvironmentVariable, srv.URL) + if shouldSetAPIKey { + t.Setenv(constants.APIKeyEnvironmentVariable, "12345") + } + } + + for _, tc := range testCases { + transport := newCiVisibilityTransport(&c) + + p := newCiVisibilityPayload() + for _, t := range tc.payload { + for _, span := range t { + err := p.push(getCiVisibilityEvent(span)) + assert.NoError(err) + } + } + + _, err := transport.send(p.payload) + assert.NoError(err) + } + assert.Equal(hits, len(testCases)) + assert.Equal(remainingEvents, 0) +} diff --git a/ddtrace/tracer/civisibility_tslv.go b/ddtrace/tracer/civisibility_tslv.go new file mode 100644 index 0000000000..377f6d5656 --- /dev/null +++ b/ddtrace/tracer/civisibility_tslv.go @@ -0,0 +1,441 @@ +// 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. + +//go:generate msgp -unexported -marshal=false -o=civisibility_tslv_msgp.go -tests=false + +package tracer + +import ( + "strconv" + + "github.com/tinylib/msgp/msgp" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" +) + +type ( + // ciTestCyclePayloadList implements msgp.Decodable on top of a slice of ciVisibilityPayloads. + // This type is only used in tests. + ciTestCyclePayloadList []*ciTestCyclePayload + + // ciVisibilityEvents is a slice of ciVisibilityEvent pointers. + ciVisibilityEvents []*ciVisibilityEvent +) + +// Ensure that ciVisibilityEvent and related types implement necessary interfaces. +var ( + _ ddtrace.Span = (*ciVisibilityEvent)(nil) + _ msgp.Encodable = (*ciVisibilityEvent)(nil) + _ msgp.Decodable = (*ciVisibilityEvent)(nil) + + _ msgp.Encodable = (*ciVisibilityEvents)(nil) + _ msgp.Decodable = (*ciVisibilityEvents)(nil) + + _ msgp.Encodable = (*ciTestCyclePayload)(nil) + _ msgp.Decodable = (*ciTestCyclePayloadList)(nil) +) + +// ciTestCyclePayload represents the payload for CI test cycles, including version, metadata, and events. +type ciTestCyclePayload struct { + Version int32 `msg:"version"` // Version of the payload format + Metadata map[string]map[string]string `msg:"metadata"` // Metadata associated with the payload + Events msgp.Raw `msg:"events"` // Encoded events data +} + +// ciVisibilityEvent represents a CI visibility event, including type, version, and content. +// It implements the ddtrace.Span interface. +// According to the CI Visibility event specification it has the following format for tests: +// +// { +// "type": "test", +// "version": 2, +// "content": { +// "type": "test", +// "trace_id": 123456, +// "span_id": 654321, +// "parent_id": 0, +// "test_session_id": 123456789, +// "test_module_id": 234567890, +// "test_suite_id": 123123123, +// "name": "...", +// "resource": "...", +// "error": 0, +// "meta": { +// ... +// }, +// "metrics": { +// ... +// }, +// "start": 1654698415668011500, +// "duration": 796143, +// "service": "..." +// } +// } +// +// For test suites: +// +// { +// "type": "test_suite_end", +// "version": 1, +// "content": { +// "type": "test_suite_end", +// "test_module_id": 234567890, +// "test_session_id": 123456789, +// "test_suite_id": 123123123, +// "name": "...", +// "resource": "...", +// "error": 0, +// "meta": { +// ... +// }, +// "metrics": { +// ... +// }, +// "start": 1654698415668011500, +// "duration": 796143, +// "service": "..." +// } +// } +// +// For test modules: +// +// { +// "type": "test_module_end", +// "version": 1, +// "content": { +// "type": "test_module_end", +// "test_session_id": 123456789, +// "test_module_id": 234567890, +// "error": 0, +// "name": "...", +// "resource": "...", +// "meta": { +// ... +// }, +// "metrics": { +// ... +// }, +// "start": 1654698415668011500, +// "duration": 796143, +// "service": "..." +// } +// } +// +// For test sessions: +// +// { +// "type": "test_session_end", +// "version": 1, +// "content": { +// "type": "test_session_end", +// "test_session_id": 123456789, +// "name": "...", +// "resource": "...", +// "error": 0, +// "meta": { +// ... +// }, +// "metrics": { +// ... +// }, +// "start": 1654698415668011500, +// "duration": 796143, +// "service": "..." +// } +// } +// +// A complete specification for the meta and metrics maps for each type can be found at: https://github.com/DataDog/datadog-ci-spec/tree/main/spec/citest +type ciVisibilityEvent struct { + Type string `msg:"type"` // Type of the CI visibility event + Version int32 `msg:"version"` // Version of the event type + Content tslvSpan `msg:"content"` // Content of the event + + span *span `msg:"-"` // Associated span (not marshaled) +} + +// SetTag sets a tag on the event's span and updates the content metadata and metrics. +// +// Parameters: +// +// key - The tag key. +// value - The tag value. +func (e *ciVisibilityEvent) SetTag(key string, value interface{}) { + e.span.SetTag(key, value) + e.Content.Meta = e.span.Meta + e.Content.Metrics = e.span.Metrics +} + +// SetOperationName sets the operation name of the event's span and updates the content name. +// +// Parameters: +// +// operationName - The new operation name. +func (e *ciVisibilityEvent) SetOperationName(operationName string) { + e.span.SetOperationName(operationName) + e.Content.Name = e.span.Name +} + +// BaggageItem retrieves the baggage item associated with the given key from the event's span. +// +// Parameters: +// +// key - The baggage item key. +// +// Returns: +// +// The baggage item value. +func (e *ciVisibilityEvent) BaggageItem(key string) string { + return e.span.BaggageItem(key) +} + +// SetBaggageItem sets a baggage item on the event's span. +// +// Parameters: +// +// key - The baggage item key. +// val - The baggage item value. +func (e *ciVisibilityEvent) SetBaggageItem(key, val string) { + e.span.SetBaggageItem(key, val) +} + +// Finish completes the event's span with optional finish options. +// +// Parameters: +// +// opts - Optional finish options. +func (e *ciVisibilityEvent) Finish(opts ...ddtrace.FinishOption) { + e.span.Finish(opts...) +} + +// Context returns the span context of the event's span. +// +// Returns: +// +// The span context. +func (e *ciVisibilityEvent) Context() ddtrace.SpanContext { + return e.span.Context() +} + +// tslvSpan represents the detailed information of a span for CI visibility. +type tslvSpan struct { + SessionID uint64 `msg:"test_session_id,omitempty"` // identifier of this session + ModuleID uint64 `msg:"test_module_id,omitempty"` // identifier of this module + SuiteID uint64 `msg:"test_suite_id,omitempty"` // identifier of this suite + CorrelationID string `msg:"itr_correlation_id,omitempty"` // Correlation Id for Intelligent Test Runner transactions + Name string `msg:"name"` // operation name + Service string `msg:"service"` // service name (i.e. "grpc.server", "http.request") + Resource string `msg:"resource"` // resource name (i.e. "/user?id=123", "SELECT * FROM users") + Type string `msg:"type"` // protocol associated with the span (i.e. "web", "db", "cache") + Start int64 `msg:"start"` // span start time expressed in nanoseconds since epoch + Duration int64 `msg:"duration"` // duration of the span expressed in nanoseconds + SpanID uint64 `msg:"span_id,omitempty"` // identifier of this span + TraceID uint64 `msg:"trace_id,omitempty"` // lower 64-bits of the root span identifier + ParentID uint64 `msg:"parent_id,omitempty"` // identifier of the span's direct parent + Error int32 `msg:"error"` // error status of the span; 0 means no errors + Meta map[string]string `msg:"meta,omitempty"` // arbitrary map of metadata + Metrics map[string]float64 `msg:"metrics,omitempty"` // arbitrary map of numeric metrics +} + +// getCiVisibilityEvent creates a ciVisibilityEvent from a span based on the span type. +// +// Parameters: +// +// span - The span to convert into a ciVisibilityEvent. +// +// Returns: +// +// A pointer to the created ciVisibilityEvent. +func getCiVisibilityEvent(span *span) *ciVisibilityEvent { + switch span.Type { + case constants.SpanTypeTest: + return createTestEventFromSpan(span) + case constants.SpanTypeTestSuite: + return createTestSuiteEventFromSpan(span) + case constants.SpanTypeTestModule: + return createTestModuleEventFromSpan(span) + case constants.SpanTypeTestSession: + return createTestSessionEventFromSpan(span) + default: + return createSpanEventFromSpan(span) + } +} + +// createTestEventFromSpan creates a ciVisibilityEvent of type Test from a span. +// +// Parameters: +// +// span - The span to convert. +// +// Returns: +// +// A pointer to the created ciVisibilityEvent. +func createTestEventFromSpan(span *span) *ciVisibilityEvent { + tSpan := createTslvSpan(span) + tSpan.SessionID = getAndRemoveMetaToUInt64(span, constants.TestSessionIDTag) + tSpan.ModuleID = getAndRemoveMetaToUInt64(span, constants.TestModuleIDTag) + tSpan.SuiteID = getAndRemoveMetaToUInt64(span, constants.TestSuiteIDTag) + tSpan.CorrelationID = getAndRemoveMeta(span, constants.ItrCorrelationIDTag) + tSpan.SpanID = span.SpanID + tSpan.TraceID = span.TraceID + return &ciVisibilityEvent{ + span: span, + Type: constants.SpanTypeTest, + Version: 2, + Content: tSpan, + } +} + +// createTestSuiteEventFromSpan creates a ciVisibilityEvent of type TestSuite from a span. +// +// Parameters: +// +// span - The span to convert. +// +// Returns: +// +// A pointer to the created ciVisibilityEvent. +func createTestSuiteEventFromSpan(span *span) *ciVisibilityEvent { + tSpan := createTslvSpan(span) + tSpan.SessionID = getAndRemoveMetaToUInt64(span, constants.TestSessionIDTag) + tSpan.ModuleID = getAndRemoveMetaToUInt64(span, constants.TestModuleIDTag) + tSpan.SuiteID = getAndRemoveMetaToUInt64(span, constants.TestSuiteIDTag) + return &ciVisibilityEvent{ + span: span, + Type: constants.SpanTypeTestSuite, + Version: 1, + Content: tSpan, + } +} + +// createTestModuleEventFromSpan creates a ciVisibilityEvent of type TestModule from a span. +// +// Parameters: +// +// span - The span to convert. +// +// Returns: +// +// A pointer to the created ciVisibilityEvent. +func createTestModuleEventFromSpan(span *span) *ciVisibilityEvent { + tSpan := createTslvSpan(span) + tSpan.SessionID = getAndRemoveMetaToUInt64(span, constants.TestSessionIDTag) + tSpan.ModuleID = getAndRemoveMetaToUInt64(span, constants.TestModuleIDTag) + return &ciVisibilityEvent{ + span: span, + Type: constants.SpanTypeTestModule, + Version: 1, + Content: tSpan, + } +} + +// createTestSessionEventFromSpan creates a ciVisibilityEvent of type TestSession from a span. +// +// Parameters: +// +// span - The span to convert. +// +// Returns: +// +// A pointer to the created ciVisibilityEvent. +func createTestSessionEventFromSpan(span *span) *ciVisibilityEvent { + tSpan := createTslvSpan(span) + tSpan.SessionID = getAndRemoveMetaToUInt64(span, constants.TestSessionIDTag) + return &ciVisibilityEvent{ + span: span, + Type: constants.SpanTypeTestSession, + Version: 1, + Content: tSpan, + } +} + +// createSpanEventFromSpan creates a ciVisibilityEvent of type Span from a span. +// +// Parameters: +// +// span - The span to convert. +// +// Returns: +// +// A pointer to the created ciVisibilityEvent. +func createSpanEventFromSpan(span *span) *ciVisibilityEvent { + tSpan := createTslvSpan(span) + tSpan.SpanID = span.SpanID + tSpan.TraceID = span.TraceID + return &ciVisibilityEvent{ + span: span, + Type: constants.SpanTypeSpan, + Version: 1, + Content: tSpan, + } +} + +// createTslvSpan creates a tslvSpan from a span. +// +// Parameters: +// +// span - The span to convert. +// +// Returns: +// +// The created tslvSpan. +func createTslvSpan(span *span) tslvSpan { + return tslvSpan{ + Name: span.Name, + Service: span.Service, + Resource: span.Resource, + Type: span.Type, + Start: span.Start, + Duration: span.Duration, + ParentID: span.ParentID, + Error: span.Error, + Meta: span.Meta, + Metrics: span.Metrics, + } +} + +// getAndRemoveMeta retrieves a metadata value from a span and removes it from the span's metadata and metrics. +// +// Parameters: +// +// span - The span to modify. +// key - The metadata key to retrieve and remove. +// +// Returns: +// +// The retrieved metadata value. +func getAndRemoveMeta(span *span, key string) string { + span.Lock() + defer span.Unlock() + if span.Meta == nil { + span.Meta = make(map[string]string, 1) + } + + if v, ok := span.Meta[key]; ok { + delete(span.Meta, key) + delete(span.Metrics, key) + return v + } + + return "" +} + +// getAndRemoveMetaToUInt64 retrieves a metadata value from a span, removes it, and converts it to a uint64. +// +// Parameters: +// +// span - The span to modify. +// key - The metadata key to retrieve and convert. +// +// Returns: +// +// The retrieved and converted metadata value as a uint64. +func getAndRemoveMetaToUInt64(span *span, key string) uint64 { + strValue := getAndRemoveMeta(span, key) + i, err := strconv.ParseUint(strValue, 10, 64) + if err != nil { + return 0 + } + return i +} diff --git a/ddtrace/tracer/civisibility_tslv_msgp.go b/ddtrace/tracer/civisibility_tslv_msgp.go new file mode 100644 index 0000000000..63fa4b8499 --- /dev/null +++ b/ddtrace/tracer/civisibility_tslv_msgp.go @@ -0,0 +1,924 @@ +package tracer + +// Code generated by github.com/tinylib/msgp DO NOT EDIT. + +import ( + "github.com/tinylib/msgp/msgp" +) + +// DecodeMsg implements msgp.Decodable +func (z *ciTestCyclePayload) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "version": + z.Version, err = dc.ReadInt32() + if err != nil { + err = msgp.WrapError(err, "Version") + return + } + case "metadata": + var zb0002 uint32 + zb0002, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err, "Metadata") + return + } + if z.Metadata == nil { + z.Metadata = make(map[string]map[string]string, zb0002) + } else if len(z.Metadata) > 0 { + for key := range z.Metadata { + delete(z.Metadata, key) + } + } + for zb0002 > 0 { + zb0002-- + var za0001 string + var za0002 map[string]string + za0001, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Metadata") + return + } + var zb0003 uint32 + zb0003, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err, "Metadata", za0001) + return + } + if za0002 == nil { + za0002 = make(map[string]string, zb0003) + } else if len(za0002) > 0 { + for key := range za0002 { + delete(za0002, key) + } + } + for zb0003 > 0 { + zb0003-- + var za0003 string + var za0004 string + za0003, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Metadata", za0001) + return + } + za0004, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Metadata", za0001, za0003) + return + } + za0002[za0003] = za0004 + } + z.Metadata[za0001] = za0002 + } + case "events": + err = z.Events.DecodeMsg(dc) + if err != nil { + err = msgp.WrapError(err, "Events") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z *ciTestCyclePayload) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 3 + // write "version" + err = en.Append(0x83, 0xa7, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e) + if err != nil { + return + } + err = en.WriteInt32(z.Version) + if err != nil { + err = msgp.WrapError(err, "Version") + return + } + // write "metadata" + err = en.Append(0xa8, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61) + if err != nil { + return + } + err = en.WriteMapHeader(uint32(len(z.Metadata))) + if err != nil { + err = msgp.WrapError(err, "Metadata") + return + } + for za0001, za0002 := range z.Metadata { + err = en.WriteString(za0001) + if err != nil { + err = msgp.WrapError(err, "Metadata") + return + } + err = en.WriteMapHeader(uint32(len(za0002))) + if err != nil { + err = msgp.WrapError(err, "Metadata", za0001) + return + } + for za0003, za0004 := range za0002 { + err = en.WriteString(za0003) + if err != nil { + err = msgp.WrapError(err, "Metadata", za0001) + return + } + err = en.WriteString(za0004) + if err != nil { + err = msgp.WrapError(err, "Metadata", za0001, za0003) + return + } + } + } + // write "events" + err = en.Append(0xa6, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x73) + if err != nil { + return + } + err = z.Events.EncodeMsg(en) + if err != nil { + err = msgp.WrapError(err, "Events") + return + } + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z *ciTestCyclePayload) Msgsize() (s int) { + s = 1 + 8 + msgp.Int32Size + 9 + msgp.MapHeaderSize + if z.Metadata != nil { + for za0001, za0002 := range z.Metadata { + _ = za0002 + s += msgp.StringPrefixSize + len(za0001) + msgp.MapHeaderSize + if za0002 != nil { + for za0003, za0004 := range za0002 { + _ = za0004 + s += msgp.StringPrefixSize + len(za0003) + msgp.StringPrefixSize + len(za0004) + } + } + } + } + s += 7 + z.Events.Msgsize() + return +} + +// DecodeMsg implements msgp.Decodable +func (z *ciTestCyclePayloadList) DecodeMsg(dc *msgp.Reader) (err error) { + var zb0002 uint32 + zb0002, err = dc.ReadArrayHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + if cap((*z)) >= int(zb0002) { + (*z) = (*z)[:zb0002] + } else { + (*z) = make(ciTestCyclePayloadList, zb0002) + } + for zb0001 := range *z { + if dc.IsNil() { + err = dc.ReadNil() + if err != nil { + err = msgp.WrapError(err, zb0001) + return + } + (*z)[zb0001] = nil + } else { + if (*z)[zb0001] == nil { + (*z)[zb0001] = new(ciTestCyclePayload) + } + err = (*z)[zb0001].DecodeMsg(dc) + if err != nil { + err = msgp.WrapError(err, zb0001) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z ciTestCyclePayloadList) EncodeMsg(en *msgp.Writer) (err error) { + err = en.WriteArrayHeader(uint32(len(z))) + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0003 := range z { + if z[zb0003] == nil { + err = en.WriteNil() + if err != nil { + return + } + } else { + err = z[zb0003].EncodeMsg(en) + if err != nil { + err = msgp.WrapError(err, zb0003) + return + } + } + } + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z ciTestCyclePayloadList) Msgsize() (s int) { + s = msgp.ArrayHeaderSize + for zb0003 := range z { + if z[zb0003] == nil { + s += msgp.NilSize + } else { + s += z[zb0003].Msgsize() + } + } + return +} + +// DecodeMsg implements msgp.Decodable +func (z *ciVisibilityEvent) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "type": + z.Type, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Type") + return + } + case "version": + z.Version, err = dc.ReadInt32() + if err != nil { + err = msgp.WrapError(err, "Version") + return + } + case "content": + err = z.Content.DecodeMsg(dc) + if err != nil { + err = msgp.WrapError(err, "Content") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z *ciVisibilityEvent) EncodeMsg(en *msgp.Writer) (err error) { + // map header, size 3 + // write "type" + err = en.Append(0x83, 0xa4, 0x74, 0x79, 0x70, 0x65) + if err != nil { + return + } + err = en.WriteString(z.Type) + if err != nil { + err = msgp.WrapError(err, "Type") + return + } + // write "version" + err = en.Append(0xa7, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e) + if err != nil { + return + } + err = en.WriteInt32(z.Version) + if err != nil { + err = msgp.WrapError(err, "Version") + return + } + // write "content" + err = en.Append(0xa7, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74) + if err != nil { + return + } + err = z.Content.EncodeMsg(en) + if err != nil { + err = msgp.WrapError(err, "Content") + return + } + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z *ciVisibilityEvent) Msgsize() (s int) { + s = 1 + 5 + msgp.StringPrefixSize + len(z.Type) + 8 + msgp.Int32Size + 8 + z.Content.Msgsize() + return +} + +// DecodeMsg implements msgp.Decodable +func (z *ciVisibilityEvents) DecodeMsg(dc *msgp.Reader) (err error) { + var zb0002 uint32 + zb0002, err = dc.ReadArrayHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + if cap((*z)) >= int(zb0002) { + (*z) = (*z)[:zb0002] + } else { + (*z) = make(ciVisibilityEvents, zb0002) + } + for zb0001 := range *z { + if dc.IsNil() { + err = dc.ReadNil() + if err != nil { + err = msgp.WrapError(err, zb0001) + return + } + (*z)[zb0001] = nil + } else { + if (*z)[zb0001] == nil { + (*z)[zb0001] = new(ciVisibilityEvent) + } + var field []byte + _ = field + var zb0003 uint32 + zb0003, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err, zb0001) + return + } + for zb0003 > 0 { + zb0003-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err, zb0001) + return + } + switch msgp.UnsafeString(field) { + case "type": + (*z)[zb0001].Type, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, zb0001, "Type") + return + } + case "version": + (*z)[zb0001].Version, err = dc.ReadInt32() + if err != nil { + err = msgp.WrapError(err, zb0001, "Version") + return + } + case "content": + err = (*z)[zb0001].Content.DecodeMsg(dc) + if err != nil { + err = msgp.WrapError(err, zb0001, "Content") + return + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err, zb0001) + return + } + } + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z ciVisibilityEvents) EncodeMsg(en *msgp.Writer) (err error) { + err = en.WriteArrayHeader(uint32(len(z))) + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0004 := range z { + if z[zb0004] == nil { + err = en.WriteNil() + if err != nil { + return + } + } else { + // map header, size 3 + // write "type" + err = en.Append(0x83, 0xa4, 0x74, 0x79, 0x70, 0x65) + if err != nil { + return + } + err = en.WriteString(z[zb0004].Type) + if err != nil { + err = msgp.WrapError(err, zb0004, "Type") + return + } + // write "version" + err = en.Append(0xa7, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e) + if err != nil { + return + } + err = en.WriteInt32(z[zb0004].Version) + if err != nil { + err = msgp.WrapError(err, zb0004, "Version") + return + } + // write "content" + err = en.Append(0xa7, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74) + if err != nil { + return + } + err = z[zb0004].Content.EncodeMsg(en) + if err != nil { + err = msgp.WrapError(err, zb0004, "Content") + return + } + } + } + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z ciVisibilityEvents) Msgsize() (s int) { + s = msgp.ArrayHeaderSize + for zb0004 := range z { + if z[zb0004] == nil { + s += msgp.NilSize + } else { + s += 1 + 5 + msgp.StringPrefixSize + len(z[zb0004].Type) + 8 + msgp.Int32Size + 8 + z[zb0004].Content.Msgsize() + } + } + return +} + +// DecodeMsg implements msgp.Decodable +func (z *tslvSpan) DecodeMsg(dc *msgp.Reader) (err error) { + var field []byte + _ = field + var zb0001 uint32 + zb0001, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err) + return + } + for zb0001 > 0 { + zb0001-- + field, err = dc.ReadMapKeyPtr() + if err != nil { + err = msgp.WrapError(err) + return + } + switch msgp.UnsafeString(field) { + case "test_session_id": + z.SessionID, err = dc.ReadUint64() + if err != nil { + err = msgp.WrapError(err, "SessionId") + return + } + case "test_module_id": + z.ModuleID, err = dc.ReadUint64() + if err != nil { + err = msgp.WrapError(err, "ModuleId") + return + } + case "test_suite_id": + z.SuiteID, err = dc.ReadUint64() + if err != nil { + err = msgp.WrapError(err, "SuiteId") + return + } + case "itr_correlation_id": + z.CorrelationID, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "CorrelationId") + return + } + case "name": + z.Name, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Name") + return + } + case "service": + z.Service, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Service") + return + } + case "resource": + z.Resource, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Resource") + return + } + case "type": + z.Type, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Type") + return + } + case "start": + z.Start, err = dc.ReadInt64() + if err != nil { + err = msgp.WrapError(err, "Start") + return + } + case "duration": + z.Duration, err = dc.ReadInt64() + if err != nil { + err = msgp.WrapError(err, "Duration") + return + } + case "span_id": + z.SpanID, err = dc.ReadUint64() + if err != nil { + err = msgp.WrapError(err, "SpanID") + return + } + case "trace_id": + z.TraceID, err = dc.ReadUint64() + if err != nil { + err = msgp.WrapError(err, "TraceID") + return + } + case "parent_id": + z.ParentID, err = dc.ReadUint64() + if err != nil { + err = msgp.WrapError(err, "ParentID") + return + } + case "error": + z.Error, err = dc.ReadInt32() + if err != nil { + err = msgp.WrapError(err, "Error") + return + } + case "meta": + var zb0002 uint32 + zb0002, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err, "Meta") + return + } + if z.Meta == nil { + z.Meta = make(map[string]string, zb0002) + } else if len(z.Meta) > 0 { + for key := range z.Meta { + delete(z.Meta, key) + } + } + for zb0002 > 0 { + zb0002-- + var za0001 string + var za0002 string + za0001, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Meta") + return + } + za0002, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Meta", za0001) + return + } + z.Meta[za0001] = za0002 + } + case "metrics": + var zb0003 uint32 + zb0003, err = dc.ReadMapHeader() + if err != nil { + err = msgp.WrapError(err, "Metrics") + return + } + if z.Metrics == nil { + z.Metrics = make(map[string]float64, zb0003) + } else if len(z.Metrics) > 0 { + for key := range z.Metrics { + delete(z.Metrics, key) + } + } + for zb0003 > 0 { + zb0003-- + var za0003 string + var za0004 float64 + za0003, err = dc.ReadString() + if err != nil { + err = msgp.WrapError(err, "Metrics") + return + } + za0004, err = dc.ReadFloat64() + if err != nil { + err = msgp.WrapError(err, "Metrics", za0003) + return + } + z.Metrics[za0003] = za0004 + } + default: + err = dc.Skip() + if err != nil { + err = msgp.WrapError(err) + return + } + } + } + return +} + +// EncodeMsg implements msgp.Encodable +func (z *tslvSpan) EncodeMsg(en *msgp.Writer) (err error) { + // omitempty: check for empty values + zb0001Len := uint32(16) + var zb0001Mask uint16 /* 16 bits */ + _ = zb0001Mask + if z.SessionID == 0 { + zb0001Len-- + zb0001Mask |= 0x1 + } + if z.ModuleID == 0 { + zb0001Len-- + zb0001Mask |= 0x2 + } + if z.SuiteID == 0 { + zb0001Len-- + zb0001Mask |= 0x4 + } + if z.CorrelationID == "" { + zb0001Len-- + zb0001Mask |= 0x8 + } + if z.SpanID == 0 { + zb0001Len-- + zb0001Mask |= 0x400 + } + if z.TraceID == 0 { + zb0001Len-- + zb0001Mask |= 0x800 + } + if z.ParentID == 0 { + zb0001Len-- + zb0001Mask |= 0x1000 + } + if z.Meta == nil { + zb0001Len-- + zb0001Mask |= 0x4000 + } + if z.Metrics == nil { + zb0001Len-- + zb0001Mask |= 0x8000 + } + // variable map header, size zb0001Len + err = en.WriteMapHeader(zb0001Len) + if err != nil { + return + } + if zb0001Len == 0 { + return + } + if (zb0001Mask & 0x1) == 0 { // if not empty + // write "test_session_id" + err = en.Append(0xaf, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteUint64(z.SessionID) + if err != nil { + err = msgp.WrapError(err, "SessionID") + return + } + } + if (zb0001Mask & 0x2) == 0 { // if not empty + // write "test_module_id" + err = en.Append(0xae, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteUint64(z.ModuleID) + if err != nil { + err = msgp.WrapError(err, "ModuleID") + return + } + } + if (zb0001Mask & 0x4) == 0 { // if not empty + // write "test_suite_id" + err = en.Append(0xad, 0x74, 0x65, 0x73, 0x74, 0x5f, 0x73, 0x75, 0x69, 0x74, 0x65, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteUint64(z.SuiteID) + if err != nil { + err = msgp.WrapError(err, "SuiteID") + return + } + } + if (zb0001Mask & 0x8) == 0 { // if not empty + // write "itr_correlation_id" + err = en.Append(0xb2, 0x69, 0x74, 0x72, 0x5f, 0x63, 0x6f, 0x72, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteString(z.CorrelationID) + if err != nil { + err = msgp.WrapError(err, "CorrelationID") + return + } + } + // write "name" + err = en.Append(0xa4, 0x6e, 0x61, 0x6d, 0x65) + if err != nil { + return + } + err = en.WriteString(z.Name) + if err != nil { + err = msgp.WrapError(err, "Name") + return + } + // write "service" + err = en.Append(0xa7, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65) + if err != nil { + return + } + err = en.WriteString(z.Service) + if err != nil { + err = msgp.WrapError(err, "Service") + return + } + // write "resource" + err = en.Append(0xa8, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65) + if err != nil { + return + } + err = en.WriteString(z.Resource) + if err != nil { + err = msgp.WrapError(err, "Resource") + return + } + // write "type" + err = en.Append(0xa4, 0x74, 0x79, 0x70, 0x65) + if err != nil { + return + } + err = en.WriteString(z.Type) + if err != nil { + err = msgp.WrapError(err, "Type") + return + } + // write "start" + err = en.Append(0xa5, 0x73, 0x74, 0x61, 0x72, 0x74) + if err != nil { + return + } + err = en.WriteInt64(z.Start) + if err != nil { + err = msgp.WrapError(err, "Start") + return + } + // write "duration" + err = en.Append(0xa8, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e) + if err != nil { + return + } + err = en.WriteInt64(z.Duration) + if err != nil { + err = msgp.WrapError(err, "Duration") + return + } + if (zb0001Mask & 0x400) == 0 { // if not empty + // write "span_id" + err = en.Append(0xa7, 0x73, 0x70, 0x61, 0x6e, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteUint64(z.SpanID) + if err != nil { + err = msgp.WrapError(err, "SpanID") + return + } + } + if (zb0001Mask & 0x800) == 0 { // if not empty + // write "trace_id" + err = en.Append(0xa8, 0x74, 0x72, 0x61, 0x63, 0x65, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteUint64(z.TraceID) + if err != nil { + err = msgp.WrapError(err, "TraceID") + return + } + } + if (zb0001Mask & 0x1000) == 0 { // if not empty + // write "parent_id" + err = en.Append(0xa9, 0x70, 0x61, 0x72, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64) + if err != nil { + return + } + err = en.WriteUint64(z.ParentID) + if err != nil { + err = msgp.WrapError(err, "ParentID") + return + } + } + // write "error" + err = en.Append(0xa5, 0x65, 0x72, 0x72, 0x6f, 0x72) + if err != nil { + return + } + err = en.WriteInt32(z.Error) + if err != nil { + err = msgp.WrapError(err, "Error") + return + } + if (zb0001Mask & 0x4000) == 0 { // if not empty + // write "meta" + err = en.Append(0xa4, 0x6d, 0x65, 0x74, 0x61) + if err != nil { + return + } + err = en.WriteMapHeader(uint32(len(z.Meta))) + if err != nil { + err = msgp.WrapError(err, "Meta") + return + } + for za0001, za0002 := range z.Meta { + err = en.WriteString(za0001) + if err != nil { + err = msgp.WrapError(err, "Meta") + return + } + err = en.WriteString(za0002) + if err != nil { + err = msgp.WrapError(err, "Meta", za0001) + return + } + } + } + if (zb0001Mask & 0x8000) == 0 { // if not empty + // write "metrics" + err = en.Append(0xa7, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73) + if err != nil { + return + } + err = en.WriteMapHeader(uint32(len(z.Metrics))) + if err != nil { + err = msgp.WrapError(err, "Metrics") + return + } + for za0003, za0004 := range z.Metrics { + err = en.WriteString(za0003) + if err != nil { + err = msgp.WrapError(err, "Metrics") + return + } + err = en.WriteFloat64(za0004) + if err != nil { + err = msgp.WrapError(err, "Metrics", za0003) + return + } + } + } + return +} + +// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message +func (z *tslvSpan) Msgsize() (s int) { + s = 3 + 16 + msgp.Uint64Size + 15 + msgp.Uint64Size + 14 + msgp.Uint64Size + 19 + msgp.StringPrefixSize + len(z.CorrelationID) + 5 + msgp.StringPrefixSize + len(z.Name) + 8 + msgp.StringPrefixSize + len(z.Service) + 9 + msgp.StringPrefixSize + len(z.Resource) + 5 + msgp.StringPrefixSize + len(z.Type) + 6 + msgp.Int64Size + 9 + msgp.Int64Size + 8 + msgp.Uint64Size + 9 + msgp.Uint64Size + 10 + msgp.Uint64Size + 6 + msgp.Int32Size + 5 + msgp.MapHeaderSize + if z.Meta != nil { + for za0001, za0002 := range z.Meta { + _ = za0002 + s += msgp.StringPrefixSize + len(za0001) + msgp.StringPrefixSize + len(za0002) + } + } + s += 8 + msgp.MapHeaderSize + if z.Metrics != nil { + for za0003, za0004 := range z.Metrics { + _ = za0004 + s += msgp.StringPrefixSize + len(za0003) + msgp.Float64Size + } + } + return +} diff --git a/ddtrace/tracer/civisibility_writer.go b/ddtrace/tracer/civisibility_writer.go new file mode 100644 index 0000000000..1582b200a8 --- /dev/null +++ b/ddtrace/tracer/civisibility_writer.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 tracer + +import ( + "sync" + "time" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/log" +) + +// Constants defining the payload size limits for agentless mode. +const ( + // agentlessPayloadMaxLimit is the maximum payload size allowed, indicating the + // maximum size of the package that the intake can receive. + agentlessPayloadMaxLimit = 5 * 1024 * 1024 // 5 MB + + // agentlessPayloadSizeLimit specifies the maximum allowed size of the payload before + // it triggers a flush to the transport. + agentlessPayloadSizeLimit = agentlessPayloadMaxLimit / 2 +) + +// Ensure that ciVisibilityTraceWriter implements the traceWriter interface. +var _ traceWriter = (*ciVisibilityTraceWriter)(nil) + +// ciVisibilityTraceWriter is responsible for buffering and sending CI visibility trace data +// to the Datadog backend. It manages the payload size and flushes the data when necessary. +type ciVisibilityTraceWriter struct { + config *config // Configuration for the tracer. + payload *ciVisibilityPayload // Encodes and buffers events in msgpack format. + climit chan struct{} // Limits the number of concurrent outgoing connections. + wg sync.WaitGroup // Waits for all uploads to finish. +} + +// newCiVisibilityTraceWriter creates a new instance of ciVisibilityTraceWriter. +// +// Parameters: +// +// c - The tracer configuration. +// +// Returns: +// +// A pointer to an initialized ciVisibilityTraceWriter. +func newCiVisibilityTraceWriter(c *config) *ciVisibilityTraceWriter { + return &ciVisibilityTraceWriter{ + config: c, + payload: newCiVisibilityPayload(), + climit: make(chan struct{}, concurrentConnectionLimit), + } +} + +// add adds a new trace to the payload. If the payload size exceeds the limit, +// it triggers a flush to send the data. +// +// Parameters: +// +// trace - A slice of spans representing the trace to be added. +func (w *ciVisibilityTraceWriter) add(trace []*span) { + for _, s := range trace { + cvEvent := getCiVisibilityEvent(s) + if err := w.payload.push(cvEvent); err != nil { + log.Error("Error encoding msgpack: %v", err) + } + if w.payload.size() > agentlessPayloadSizeLimit { + w.flush() + } + } +} + +// stop stops the trace writer, ensuring all data is flushed and all uploads are completed. +func (w *ciVisibilityTraceWriter) stop() { + w.flush() + w.wg.Wait() +} + +// flush sends the current payload to the transport. It ensures that the payload is reset +// and the resources are freed after the flush operation is completed. +func (w *ciVisibilityTraceWriter) flush() { + if w.payload.itemCount() == 0 { + return + } + + w.wg.Add(1) + w.climit <- struct{}{} + oldp := w.payload + w.payload = newCiVisibilityPayload() + + go func(p *ciVisibilityPayload) { + defer func(start time.Time) { + // Once the payload has been used, clear the buffer for garbage + // collection to avoid a memory leak when references to this object + // may still be kept by faulty transport implementations or the + // standard library. See dd-trace-go#976 + p.clear() + + <-w.climit + w.wg.Done() + }(time.Now()) + + var count, size int + var err error + for attempt := 0; attempt <= w.config.sendRetries; attempt++ { + size, count = p.size(), p.itemCount() + log.Debug("Sending payload: size: %d events: %d\n", size, count) + _, err = w.config.transport.send(p.payload) + if err == nil { + log.Debug("sent events after %d attempts", attempt+1) + return + } + log.Error("failure sending events (attempt %d), will retry: %v", attempt+1, err) + p.reset() + time.Sleep(time.Millisecond) + } + log.Error("lost %d events: %v", count, err) + }(oldp) +} diff --git a/ddtrace/tracer/civisibility_writer_test.go b/ddtrace/tracer/civisibility_writer_test.go new file mode 100644 index 0000000000..0757ec05bb --- /dev/null +++ b/ddtrace/tracer/civisibility_writer_test.go @@ -0,0 +1,101 @@ +// 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 tracer + +import ( + "errors" + "fmt" + "io" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/tinylib/msgp/msgp" +) + +func TestCIVisibilityImplementsTraceWriter(t *testing.T) { + assert.Implements(t, (*traceWriter)(nil), &ciVisibilityTraceWriter{}) +} + +type failingCiVisibilityTransport struct { + dummyTransport + failCount int + sendAttempts int + tracesSent bool + events ciVisibilityEvents + assert *assert.Assertions +} + +func (t *failingCiVisibilityTransport) send(p *payload) (io.ReadCloser, error) { + t.sendAttempts++ + + ciVisibilityPayload := &ciVisibilityPayload{p} + + var events ciVisibilityEvents + err := msgp.Decode(ciVisibilityPayload, &events) + if err != nil { + return nil, err + } + if t.sendAttempts == 1 { + t.events = events + } else { + t.assert.Equal(t.events, events) + } + + if t.failCount > 0 { + t.failCount-- + return nil, errors.New("oops, I failed") + } + + t.tracesSent = true + return io.NopCloser(strings.NewReader("OK")), nil +} + +func TestCiVisibilityTraceWriterFlushRetries(t *testing.T) { + testcases := []struct { + configRetries int + failCount int + tracesSent bool + expAttempts int + }{ + {configRetries: 0, failCount: 0, tracesSent: true, expAttempts: 1}, + {configRetries: 0, failCount: 1, tracesSent: false, expAttempts: 1}, + + {configRetries: 1, failCount: 0, tracesSent: true, expAttempts: 1}, + {configRetries: 1, failCount: 1, tracesSent: true, expAttempts: 2}, + {configRetries: 1, failCount: 2, tracesSent: false, expAttempts: 2}, + + {configRetries: 2, failCount: 0, tracesSent: true, expAttempts: 1}, + {configRetries: 2, failCount: 1, tracesSent: true, expAttempts: 2}, + {configRetries: 2, failCount: 2, tracesSent: true, expAttempts: 3}, + {configRetries: 2, failCount: 3, tracesSent: false, expAttempts: 3}, + } + + ss := []*span{makeSpan(0)} + for _, test := range testcases { + name := fmt.Sprintf("%d-%d-%t-%d", test.configRetries, test.failCount, test.tracesSent, test.expAttempts) + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + p := &failingCiVisibilityTransport{ + failCount: test.failCount, + assert: assert, + } + c := newConfig(func(c *config) { + c.transport = p + c.sendRetries = test.configRetries + }) + + h := newCiVisibilityTraceWriter(c) + h.add(ss) + + h.flush() + h.wg.Wait() + + assert.Equal(test.expAttempts, p.sendAttempts) + assert.Equal(test.tracesSent, p.tracesSent) + }) + } +} diff --git a/ddtrace/tracer/option.go b/ddtrace/tracer/option.go index d8ce6843a2..f6af69b695 100644 --- a/ddtrace/tracer/option.go +++ b/ddtrace/tracer/option.go @@ -25,6 +25,7 @@ import ( "gopkg.in/DataDog/dd-trace-go.v1/ddtrace" "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" "gopkg.in/DataDog/dd-trace-go.v1/internal" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" "gopkg.in/DataDog/dd-trace-go.v1/internal/globalconfig" "gopkg.in/DataDog/dd-trace-go.v1/internal/log" "gopkg.in/DataDog/dd-trace-go.v1/internal/namingschema" @@ -275,6 +276,9 @@ type config struct { // globalSampleRate holds sample rate read from environment variables. globalSampleRate float64 + + // ciVisibilityEnabled controls if the tracer is loaded with CI Visibility mode. default false + ciVisibilityEnabled bool } // orchestrionConfig contains Orchestrion configuration. @@ -540,6 +544,14 @@ func newConfig(opts ...StartOption) *config { globalTagsOrigin := c.globalTags.cfgOrigin c.initGlobalTags(c.globalTags.get(), globalTagsOrigin) + // Check if CI Visibility mode is enabled + if internal.BoolEnv(constants.CIVisibilityEnabledEnvironmentVariable, false) { + c.ciVisibilityEnabled = true // Enable CI Visibility mode + c.httpClientTimeout = time.Second * 45 // Increase timeout up to 45 seconds (same as other tracers in CIVis mode) + c.logStartup = false // If we are in CI Visibility mode we don't want to log the startup to stdout to avoid polluting the output + c.transport = newCiVisibilityTransport(c) // Replace the default transport with the CI Visibility transport + } + return c } diff --git a/ddtrace/tracer/tracer.go b/ddtrace/tracer/tracer.go index 35e3f2ce43..af794c7857 100644 --- a/ddtrace/tracer/tracer.go +++ b/ddtrace/tracer/tracer.go @@ -235,7 +235,9 @@ func newUnstartedTracer(opts ...StartOption) *tracer { log.Warn("Runtime and health metrics disabled: %v", err) } var writer traceWriter - if c.logToStdout { + if c.ciVisibilityEnabled { + writer = newCiVisibilityTraceWriter(c) + } else if c.logToStdout { writer = newLogTraceWriter(c, statsd) } else { writer = newAgentTraceWriter(c, sampler, statsd) diff --git a/internal/civisibility/constants/ci.go b/internal/civisibility/constants/ci.go new file mode 100644 index 0000000000..d1ca2e80b8 --- /dev/null +++ b/internal/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/internal/civisibility/constants/env.go b/internal/civisibility/constants/env.go new file mode 100644 index 0000000000..ebc00fcb69 --- /dev/null +++ b/internal/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/internal/civisibility/constants/git.go b/internal/civisibility/constants/git.go new file mode 100644 index 0000000000..6265be391a --- /dev/null +++ b/internal/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/internal/civisibility/constants/os.go b/internal/civisibility/constants/os.go new file mode 100644 index 0000000000..e065f179f2 --- /dev/null +++ b/internal/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/internal/civisibility/constants/runtime.go b/internal/civisibility/constants/runtime.go new file mode 100644 index 0000000000..15257ef258 --- /dev/null +++ b/internal/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/internal/civisibility/constants/span_types.go b/internal/civisibility/constants/span_types.go new file mode 100644 index 0000000000..55f3fe58ed --- /dev/null +++ b/internal/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/internal/civisibility/constants/tags.go b/internal/civisibility/constants/tags.go new file mode 100644 index 0000000000..4563cb8839 --- /dev/null +++ b/internal/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/internal/civisibility/constants/test_tags.go b/internal/civisibility/constants/test_tags.go new file mode 100644 index 0000000000..3a621a20be --- /dev/null +++ b/internal/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/internal/civisibility/integrations/civisibility.go b/internal/civisibility/integrations/civisibility.go new file mode 100644 index 0000000000..aaa92c04bb --- /dev/null +++ b/internal/civisibility/integrations/civisibility.go @@ -0,0 +1,118 @@ +// 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" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/mocktracer" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/utils" +) + +// 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/internal/civisibility/integrations/gotesting/reflections.go b/internal/civisibility/integrations/gotesting/reflections.go new file mode 100644 index 0000000000..6aaac3ed4c --- /dev/null +++ b/internal/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/internal/civisibility/integrations/gotesting/reflections_test.go b/internal/civisibility/integrations/gotesting/reflections_test.go new file mode 100644 index 0000000000..453cd7a0e9 --- /dev/null +++ b/internal/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/internal/civisibility/integrations/gotesting/testing.go b/internal/civisibility/integrations/gotesting/testing.go new file mode 100644 index 0000000000..f1c35c726a --- /dev/null +++ b/internal/civisibility/integrations/gotesting/testing.go @@ -0,0 +1,353 @@ +// 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" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/utils" +) + +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/internal/civisibility/integrations/gotesting/testingB.go b/internal/civisibility/integrations/gotesting/testingB.go new file mode 100644 index 0000000000..0ebd7c8159 --- /dev/null +++ b/internal/civisibility/integrations/gotesting/testingB.go @@ -0,0 +1,336 @@ +// 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" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/utils" +) + +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/internal/civisibility/integrations/gotesting/testingT.go b/internal/civisibility/integrations/gotesting/testingT.go new file mode 100644 index 0000000000..2ac53c3fa8 --- /dev/null +++ b/internal/civisibility/integrations/gotesting/testingT.go @@ -0,0 +1,216 @@ +// 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" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/utils" +) + +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/internal/civisibility/integrations/gotesting/testing_test.go b/internal/civisibility/integrations/gotesting/testing_test.go new file mode 100644 index 0000000000..a2b7ac1f1b --- /dev/null +++ b/internal/civisibility/integrations/gotesting/testing_test.go @@ -0,0 +1,350 @@ +// 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" + + 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" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/integrations" + + "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) + 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/internal/civisibility/integrations/manual_api.go b/internal/civisibility/integrations/manual_api.go new file mode 100644 index 0000000000..276898ad1a --- /dev/null +++ b/internal/civisibility/integrations/manual_api.go @@ -0,0 +1,218 @@ +// 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" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/utils" +) + +// 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/internal/civisibility/integrations/manual_api_ddtest.go b/internal/civisibility/integrations/manual_api_ddtest.go new file mode 100644 index 0000000000..cdb01f4d4b --- /dev/null +++ b/internal/civisibility/integrations/manual_api_ddtest.go @@ -0,0 +1,157 @@ +// 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" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/utils" +) + +// 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/internal/civisibility/integrations/manual_api_ddtestmodule.go b/internal/civisibility/integrations/manual_api_ddtestmodule.go new file mode 100644 index 0000000000..9f8746f4e3 --- /dev/null +++ b/internal/civisibility/integrations/manual_api_ddtestmodule.go @@ -0,0 +1,140 @@ +// 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" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" +) + +// 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/internal/civisibility/integrations/manual_api_ddtestsession.go b/internal/civisibility/integrations/manual_api_ddtestsession.go new file mode 100644 index 0000000000..807cd835da --- /dev/null +++ b/internal/civisibility/integrations/manual_api_ddtestsession.go @@ -0,0 +1,170 @@ +// 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" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/utils" +) + +// 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/internal/civisibility/integrations/manual_api_ddtestsuite.go b/internal/civisibility/integrations/manual_api_ddtestsuite.go new file mode 100644 index 0000000000..9a1857dd21 --- /dev/null +++ b/internal/civisibility/integrations/manual_api_ddtestsuite.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 integrations + +import ( + "context" + "fmt" + "strings" + "time" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" +) + +// 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/internal/civisibility/integrations/manual_api_mocktracer_test.go b/internal/civisibility/integrations/manual_api_mocktracer_test.go new file mode 100644 index 0000000000..afb6d321e4 --- /dev/null +++ b/internal/civisibility/integrations/manual_api_mocktracer_test.go @@ -0,0 +1,277 @@ +// 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" + + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/ext" + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" + + "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/internal/civisibility/integrations/manual_api_test.go b/internal/civisibility/integrations/manual_api_test.go new file mode 100644 index 0000000000..2bb0a4ecb3 --- /dev/null +++ b/internal/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/internal/civisibility/utils/ci_providers.go b/internal/civisibility/utils/ci_providers.go new file mode 100644 index 0000000000..bb8b6940d5 --- /dev/null +++ b/internal/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" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/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/internal/civisibility/utils/ci_providers_test.go b/internal/civisibility/utils/ci_providers_test.go new file mode 100644 index 0000000000..b85acc1efc --- /dev/null +++ b/internal/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/internal/civisibility/utils/codeowners.go b/internal/civisibility/utils/codeowners.go new file mode 100644 index 0000000000..fa930d5af2 --- /dev/null +++ b/internal/civisibility/utils/codeowners.go @@ -0,0 +1,304 @@ +// 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" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" + + logger "gopkg.in/DataDog/dd-trace-go.v1/internal/log" +) + +// 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 + } + logger.Debug("Error parsing codeowners: %s", err) + } + } + } + + 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) { + logger.Warn("Error closing codeowners file: %s", err.Error()) + } + }() + + 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/internal/civisibility/utils/codeowners_test.go b/internal/civisibility/utils/codeowners_test.go new file mode 100644 index 0000000000..95852bec4a --- /dev/null +++ b/internal/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/internal/civisibility/utils/environmentTags.go b/internal/civisibility/utils/environmentTags.go new file mode 100644 index 0000000000..989ef7ef06 --- /dev/null +++ b/internal/civisibility/utils/environmentTags.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 utils + +import ( + "path/filepath" + "runtime" + "sync" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/civisibility/constants" + "gopkg.in/DataDog/dd-trace-go.v1/internal/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 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/internal/civisibility/utils/environmentTags_test.go b/internal/civisibility/utils/environmentTags_test.go new file mode 100644 index 0000000000..694a2b1217 --- /dev/null +++ b/internal/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" + + "gopkg.in/DataDog/dd-trace-go.v1/internal/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/internal/civisibility/utils/git.go b/internal/civisibility/utils/git.go new file mode 100644 index 0000000000..fb62a37567 --- /dev/null +++ b/internal/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/internal/civisibility/utils/git_test.go b/internal/civisibility/utils/git_test.go new file mode 100644 index 0000000000..fd91ebee11 --- /dev/null +++ b/internal/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/internal/civisibility/utils/home.go b/internal/civisibility/utils/home.go new file mode 100644 index 0000000000..8625010feb --- /dev/null +++ b/internal/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/internal/civisibility/utils/home_test.go b/internal/civisibility/utils/home_test.go new file mode 100644 index 0000000000..a87d8de8d2 --- /dev/null +++ b/internal/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/internal/civisibility/utils/names.go b/internal/civisibility/utils/names.go new file mode 100644 index 0000000000..1edcb2b972 --- /dev/null +++ b/internal/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/internal/civisibility/utils/names_test.go b/internal/civisibility/utils/names_test.go new file mode 100644 index 0000000000..fe98952392 --- /dev/null +++ b/internal/civisibility/utils/names_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 ( + "runtime" + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestGetModuleAndSuiteName(t *testing.T) { + pc, _, _, _ := runtime.Caller(0) // Get the program counter of this function + module, 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/internal/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITHUB b/internal/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITHUB new file mode 100644 index 0000000000..afbd198dcf --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITLAB b/internal/civisibility/utils/testdata/fixtures/codeowners/CODEOWNERS_GITLAB new file mode 100644 index 0000000000..d0831c780d --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/appveyor.json b/internal/civisibility/utils/testdata/fixtures/providers/appveyor.json new file mode 100644 index 0000000000..def208d711 --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/awscodepipeline.json b/internal/civisibility/utils/testdata/fixtures/providers/awscodepipeline.json new file mode 100644 index 0000000000..6f3071331f --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/azurepipelines.json b/internal/civisibility/utils/testdata/fixtures/providers/azurepipelines.json new file mode 100644 index 0000000000..d072c2731c --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/bitbucket.json b/internal/civisibility/utils/testdata/fixtures/providers/bitbucket.json new file mode 100644 index 0000000000..bbd8cd37ea --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/bitrise.json b/internal/civisibility/utils/testdata/fixtures/providers/bitrise.json new file mode 100644 index 0000000000..579ace5d2c --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/buddy.json b/internal/civisibility/utils/testdata/fixtures/providers/buddy.json new file mode 100644 index 0000000000..9c64a199d1 --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/buildkite.json b/internal/civisibility/utils/testdata/fixtures/providers/buildkite.json new file mode 100644 index 0000000000..86ff170deb --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/circleci.json b/internal/civisibility/utils/testdata/fixtures/providers/circleci.json new file mode 100644 index 0000000000..2a697af349 --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/codefresh.json b/internal/civisibility/utils/testdata/fixtures/providers/codefresh.json new file mode 100644 index 0000000000..d719df1059 --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/github.json b/internal/civisibility/utils/testdata/fixtures/providers/github.json new file mode 100644 index 0000000000..3dd5ac62d5 --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/gitlab.json b/internal/civisibility/utils/testdata/fixtures/providers/gitlab.json new file mode 100644 index 0000000000..24882c1e85 --- /dev/null +++ b/internal/civisibility/utils/testdata/fixtures/providers/gitlab.json @@ -0,0 +1,1047 @@ +[ + [ + { + "CI_COMMIT_AUTHOR": "John Doe ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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 ", + "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/internal/civisibility/utils/testdata/fixtures/providers/jenkins.json b/internal/civisibility/utils/testdata/fixtures/providers/jenkins.json new file mode 100644 index 0000000000..38d448747b --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/teamcity.json b/internal/civisibility/utils/testdata/fixtures/providers/teamcity.json new file mode 100644 index 0000000000..086c1c16de --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/travisci.json b/internal/civisibility/utils/testdata/fixtures/providers/travisci.json new file mode 100644 index 0000000000..2131938546 --- /dev/null +++ b/internal/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/internal/civisibility/utils/testdata/fixtures/providers/usersupplied.json b/internal/civisibility/utils/testdata/fixtures/providers/usersupplied.json new file mode 100644 index 0000000000..f07f4e3740 --- /dev/null +++ b/internal/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/" + } + ] +]