datadog-ci 1.12.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -2
  3. data/lib/datadog/ci/configuration/components.rb +27 -15
  4. data/lib/datadog/ci/configuration/settings.rb +12 -0
  5. data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +5 -2
  6. data/lib/datadog/ci/contrib/minitest/test.rb +3 -3
  7. data/lib/datadog/ci/contrib/rspec/example.rb +3 -3
  8. data/lib/datadog/ci/ext/app_types.rb +1 -1
  9. data/lib/datadog/ci/ext/settings.rb +2 -0
  10. data/lib/datadog/ci/ext/telemetry.rb +17 -5
  11. data/lib/datadog/ci/ext/test.rb +42 -4
  12. data/lib/datadog/ci/ext/transport.rb +6 -0
  13. data/lib/datadog/ci/remote/component.rb +11 -1
  14. data/lib/datadog/ci/remote/library_settings.rb +31 -0
  15. data/lib/datadog/ci/remote/library_settings_client.rb +2 -1
  16. data/lib/datadog/ci/test.rb +67 -9
  17. data/lib/datadog/ci/test_management/component.rb +61 -0
  18. data/lib/datadog/ci/test_management/null_component.rb +25 -0
  19. data/lib/datadog/ci/test_management/tests_properties.rb +128 -0
  20. data/lib/datadog/ci/test_optimisation/component.rb +3 -3
  21. data/lib/datadog/ci/test_retries/component.rb +37 -7
  22. data/lib/datadog/ci/test_retries/driver/base.rb +5 -0
  23. data/lib/datadog/ci/test_retries/driver/retry_failed.rb +4 -0
  24. data/lib/datadog/ci/test_retries/driver/retry_flaky_fixed.rb +38 -0
  25. data/lib/datadog/ci/test_retries/driver/retry_new.rb +2 -7
  26. data/lib/datadog/ci/test_retries/strategy/retry_flaky_fixed.rb +43 -0
  27. data/lib/datadog/ci/test_retries/strategy/retry_new.rb +4 -46
  28. data/lib/datadog/ci/test_suite.rb +21 -2
  29. data/lib/datadog/ci/test_visibility/capabilities.rb +36 -0
  30. data/lib/datadog/ci/test_visibility/component.rb +70 -2
  31. data/lib/datadog/ci/test_visibility/context.rb +0 -4
  32. data/lib/datadog/ci/{test_retries/unique_tests_client.rb → test_visibility/known_tests.rb} +10 -10
  33. data/lib/datadog/ci/test_visibility/null_component.rb +3 -0
  34. data/lib/datadog/ci/test_visibility/serializers/factories/test_level.rb +1 -1
  35. data/lib/datadog/ci/test_visibility/telemetry.rb +11 -2
  36. data/lib/datadog/ci/test_visibility/transport.rb +15 -2
  37. data/lib/datadog/ci/version.rb +1 -1
  38. data/lib/datadog/ci.rb +13 -5
  39. metadata +11 -5
@@ -18,9 +18,10 @@ module Datadog
18
18
  module TestVisibility
19
19
  # Common behavior for CI tests
20
20
  class Component
21
- attr_reader :test_suite_level_visibility_enabled, :logical_test_session_name
21
+ attr_reader :test_suite_level_visibility_enabled, :logical_test_session_name, :known_tests, :known_tests_enabled
22
22
 
23
23
  def initialize(
24
+ known_tests_client:,
24
25
  test_suite_level_visibility_enabled: false,
25
26
  codeowners: Codeowners::Parser.new(Git::LocalRepository.root).parse,
26
27
  logical_test_session_name: nil
@@ -29,6 +30,21 @@ module Datadog
29
30
  @context = Context.new
30
31
  @codeowners = codeowners
31
32
  @logical_test_session_name = logical_test_session_name
33
+
34
+ # "Known tests" feature fetches a list of all tests known to Datadog for this repository
35
+ # and uses this list to determine if a test is new or not. New tests are marked with "test.is_new" tag.
36
+ @known_tests_enabled = false
37
+ @known_tests_client = known_tests_client
38
+ @known_tests = Set.new
39
+ end
40
+
41
+ def configure(library_configuration, test_session)
42
+ return unless test_suite_level_visibility_enabled
43
+
44
+ if library_configuration.known_tests_enabled?
45
+ @known_tests_enabled = true
46
+ fetch_known_tests(test_session)
47
+ end
32
48
  end
33
49
 
34
50
  def start_test_session(service: nil, tags: {}, total_tests_count: 0)
@@ -158,7 +174,8 @@ module Datadog
158
174
  # sets logical test session name if none provided by the user
159
175
  override_logical_test_session_name!(test_session) if logical_test_session_name.nil?
160
176
 
161
- # signal Remote::Component to configure the library
177
+ # Signal Remote::Component to configure the library.
178
+ # Note that it will call this component back (unfortunate circular dependency).
162
179
  remote.configure(test_session)
163
180
  end
164
181
 
@@ -182,6 +199,10 @@ module Datadog
182
199
 
183
200
  Telemetry.event_created(test)
184
201
 
202
+ mark_test_as_new(test) if new_test?(test)
203
+
204
+ test_management.tag_test_from_properties(test)
205
+
185
206
  test_optimisation.mark_if_skippable(test)
186
207
  test_optimisation.start_coverage(test)
187
208
  end
@@ -287,6 +308,49 @@ module Datadog
287
308
  end
288
309
  end
289
310
 
311
+ def new_test?(test_span)
312
+ return false unless @known_tests_enabled
313
+
314
+ test_id = Utils::TestRun.datadog_test_id(test_span.name, test_span.test_suite_name)
315
+ result = !@known_tests.include?(test_id)
316
+
317
+ if result
318
+ Datadog.logger.debug do
319
+ "#{test_id} is not found in the known tests set, it is a new test"
320
+ end
321
+ end
322
+
323
+ result
324
+ end
325
+
326
+ def fetch_known_tests(test_session)
327
+ @known_tests = @known_tests_client.fetch(test_session)
328
+
329
+ if @known_tests.empty?
330
+ @known_tests_enabled = false
331
+
332
+ # this adds unfortunate knowledge on EFD from Testvisibility, rethink this
333
+ test_session&.set_tag(Ext::Test::TAG_EARLY_FLAKE_ABORT_REASON, Ext::Test::EARLY_FLAKE_FAULTY)
334
+
335
+ Datadog.logger.warn("Empty set of tests known to Datadog")
336
+ end
337
+
338
+ # report how many known tests were found
339
+ Datadog.logger.debug do
340
+ "Found [#{@known_tests.size}] known tests"
341
+ end
342
+ Utils::Telemetry.distribution(
343
+ Ext::Telemetry::METRIC_KNOWN_TESTS_RESPONSE_TESTS,
344
+ @known_tests.size.to_f
345
+ )
346
+
347
+ @known_tests
348
+ end
349
+
350
+ def mark_test_as_new(test_span)
351
+ test_span.set_tag(Ext::Test::TAG_IS_NEW, "true")
352
+ end
353
+
290
354
  def test_optimisation
291
355
  Datadog.send(:components).test_optimisation
292
356
  end
@@ -302,6 +366,10 @@ module Datadog
302
366
  def remote
303
367
  Datadog.send(:components).ci_remote
304
368
  end
369
+
370
+ def test_management
371
+ Datadog.send(:components).test_management
372
+ end
305
373
  end
306
374
  end
307
375
  end
@@ -208,10 +208,6 @@ module Datadog
208
208
  ci_span.set_tags(@environment_tags)
209
209
 
210
210
  ci_span.set_metric(Ext::Test::METRIC_CPU_COUNT, Utils::TestRun.virtual_cpu_count)
211
- ci_span.set_tag(
212
- Ext::Test::TAG_USER_PROVIDED_TEST_SERVICE,
213
- Utils::Configuration.service_name_provided_by_user?.to_s
214
- )
215
211
  end
216
212
 
217
213
  # PROPAGATING CONTEXT FROM TOP-LEVEL TO THE LOWER LEVELS
@@ -10,9 +10,9 @@ require_relative "../utils/test_run"
10
10
 
11
11
  module Datadog
12
12
  module CI
13
- module TestRetries
14
- # fetch a list of unique known tests from the backend
15
- class UniqueTestsClient
13
+ module TestVisibility
14
+ # fetches and stores a list of known tests from the backend
15
+ class KnownTests
16
16
  class Response
17
17
  def initialize(http_response)
18
18
  @http_response = http_response
@@ -66,7 +66,7 @@ module Datadog
66
66
  @config_tags = config_tags
67
67
  end
68
68
 
69
- def fetch_unique_tests(test_session)
69
+ def fetch(test_session)
70
70
  api = @api
71
71
  return Set.new unless api
72
72
 
@@ -78,21 +78,21 @@ module Datadog
78
78
  payload: request_payload
79
79
  )
80
80
 
81
- Transport::Telemetry.api_requests(
82
- Ext::Telemetry::METRIC_EFD_UNIQUE_TESTS_REQUEST,
81
+ CI::Transport::Telemetry.api_requests(
82
+ Ext::Telemetry::METRIC_KNOWN_TESTS_REQUEST,
83
83
  1,
84
84
  compressed: http_response.request_compressed
85
85
  )
86
- Utils::Telemetry.distribution(Ext::Telemetry::METRIC_EFD_UNIQUE_TESTS_REQUEST_MS, http_response.duration_ms)
86
+ Utils::Telemetry.distribution(Ext::Telemetry::METRIC_KNOWN_TESTS_REQUEST_MS, http_response.duration_ms)
87
87
  Utils::Telemetry.distribution(
88
- Ext::Telemetry::METRIC_EFD_UNIQUE_TESTS_RESPONSE_BYTES,
88
+ Ext::Telemetry::METRIC_KNOWN_TESTS_RESPONSE_BYTES,
89
89
  http_response.response_size.to_f,
90
90
  {Ext::Telemetry::TAG_RESPONSE_COMPRESSED => http_response.gzipped_content?.to_s}
91
91
  )
92
92
 
93
93
  unless http_response.ok?
94
- Transport::Telemetry.api_requests_errors(
95
- Ext::Telemetry::METRIC_EFD_UNIQUE_TESTS_REQUEST_ERRORS,
94
+ CI::Transport::Telemetry.api_requests_errors(
95
+ Ext::Telemetry::METRIC_KNOWN_TESTS_REQUEST_ERRORS,
96
96
  1,
97
97
  error_type: http_response.telemetry_error_type,
98
98
  status_code: http_response.code
@@ -5,6 +5,9 @@ module Datadog
5
5
  module TestVisibility
6
6
  # Special test visibility component that does not record anything
7
7
  class NullComponent
8
+ def configure(_, _)
9
+ end
10
+
8
11
  def start_test_session(service: nil, tags: {}, total_tests_count: 0)
9
12
  skip_tracing
10
13
  end
@@ -9,7 +9,7 @@ module Datadog
9
9
  module Serializers
10
10
  module Factories
11
11
  # This factory takes care of creating msgpack serializers when test-level visibility is enabled
12
- # NOTE: citestcycle is a protocol Datadog uses to submit test execution tracing information to CI visibility
12
+ # NOTE: citestcycle is a protocol Datadog uses to submit test execution tracing information to Test Optimization
13
13
  # backend
14
14
  module TestLevel
15
15
  module_function
@@ -54,8 +54,11 @@ module Datadog
54
54
  # codeowner tag
55
55
  tags[Ext::Telemetry::TAG_HAS_CODEOWNER] = "true" if span.get_tag(Ext::Test::TAG_CODEOWNERS)
56
56
 
57
- # set is_retry tag if span represents a retried test
58
- tags[Ext::Telemetry::TAG_IS_RETRY] = "true" if span.get_tag(Ext::Test::TAG_IS_RETRY)
57
+ # set is_retry and retry_reason tags if span represents a retried test
58
+ if span.get_tag(Ext::Test::TAG_IS_RETRY)
59
+ tags[Ext::Telemetry::TAG_IS_RETRY] = "true"
60
+ tags[Ext::Telemetry::TAG_RETRY_REASON] = span.get_tag(Ext::Test::TAG_RETRY_REASON)
61
+ end
59
62
 
60
63
  # is_new
61
64
  tags[Ext::Telemetry::TAG_IS_NEW] = "true" if span.get_tag(Ext::Test::TAG_IS_NEW)
@@ -66,6 +69,12 @@ module Datadog
66
69
  tags[Ext::Telemetry::TAG_EARLY_FLAKE_DETECTION_ABORT_REASON] = early_flake_detection_abort_reason
67
70
  end
68
71
 
72
+ # test management tags
73
+ tags[Ext::Telemetry::TAG_IS_ATTEMPT_TO_FIX] = "true" if span.get_tag(Ext::Test::TAG_IS_ATTEMPT_TO_FIX)
74
+ tags[Ext::Telemetry::TAG_IS_QUARANTINED] = "true" if span.get_tag(Ext::Test::TAG_IS_QUARANTINED)
75
+ tags[Ext::Telemetry::TAG_IS_TEST_DISABLED] = "true" if span.get_tag(Ext::Test::TAG_IS_TEST_DISABLED)
76
+ tags[Ext::Telemetry::TAG_HAS_FAILED_ALL_RETRIES] = "true" if span.get_tag(Ext::Test::TAG_HAS_FAILED_ALL_RETRIES)
77
+
69
78
  tags
70
79
  end
71
80
 
@@ -4,12 +4,15 @@ require "datadog/core/environment/identity"
4
4
  require "datadog/core/telemetry/logging"
5
5
  require "datadog/core/utils/only_once"
6
6
 
7
+ require_relative "capabilities"
7
8
  require_relative "serializers/factories/test_level"
9
+
8
10
  require_relative "../ext/app_types"
9
11
  require_relative "../ext/telemetry"
10
12
  require_relative "../ext/transport"
11
13
  require_relative "../transport/event_platform_transport"
12
14
  require_relative "../transport/telemetry"
15
+ require_relative "../utils/configuration"
13
16
 
14
17
  module Datadog
15
18
  module CI
@@ -114,12 +117,22 @@ module Datadog
114
117
  packer.write("library_version")
115
118
  packer.write(Datadog::CI::VERSION::STRING)
116
119
 
120
+ library_capabilities_tags = Capabilities.tags
121
+
117
122
  Ext::AppTypes::CI_SPAN_TYPES.each do |ci_span_type|
118
123
  packer.write(ci_span_type)
119
- packer.write_map_header(1)
124
+ packer.write_map_header(2 + library_capabilities_tags.count)
120
125
 
121
- packer.write(Ext::Test::METADATA_TAG_TEST_SESSION_NAME)
126
+ packer.write(Ext::Test::TAG_TEST_SESSION_NAME)
122
127
  packer.write(test_visibility&.logical_test_session_name)
128
+
129
+ packer.write(Ext::Test::TAG_USER_PROVIDED_TEST_SERVICE)
130
+ packer.write(Utils::Configuration.service_name_provided_by_user?.to_s)
131
+
132
+ library_capabilities_tags.each do |tag, value|
133
+ packer.write(tag)
134
+ packer.write(value)
135
+ end
123
136
  end
124
137
 
125
138
  packer.write("events")
@@ -4,7 +4,7 @@ module Datadog
4
4
  module CI
5
5
  module VERSION
6
6
  MAJOR = 1
7
- MINOR = 12
7
+ MINOR = 13
8
8
  PATCH = 0
9
9
  PRE = nil
10
10
  BUILD = nil
data/lib/datadog/ci.rb CHANGED
@@ -10,7 +10,7 @@ require "datadog"
10
10
  require "datadog/core"
11
11
 
12
12
  module Datadog
13
- # Datadog CI visibility public API.
13
+ # Datadog Test Optimization public API.
14
14
  #
15
15
  # @public_api
16
16
  module CI
@@ -319,16 +319,16 @@ module Datadog
319
319
  # @return [Object] If a block is provided, returns the result of the block execution.
320
320
  # @return [Datadog::CI::Span] If no block is provided, returns the active,
321
321
  # unfinished {Datadog::CI::Span}.
322
- # @return [nil] if CI visibility is disabled
323
- # @raise [ReservedTypeError] if provided type is reserved for Datadog CI visibility
322
+ # @return [nil] if Test Optimization is disabled
323
+ # @raise [ReservedTypeError] if provided type is reserved for Datadog Test Optimization
324
324
  # @yield Optional block where newly created {Datadog::CI::Span} captures the execution.
325
325
  # @yieldparam [Datadog::CI::Span] ci_span the newly created and active [Datadog::CI::Span]
326
- # @yieldparam [nil] ci_span if CI visibility is disabled
326
+ # @yieldparam [nil] ci_span if Test Optimization is disabled
327
327
  def trace(span_name, type: "span", tags: {}, &block)
328
328
  if Ext::AppTypes::CI_SPAN_TYPES.include?(type)
329
329
  raise(
330
330
  ReservedTypeError,
331
- "Span type #{type} is reserved for Datadog CI visibility. " \
331
+ "Span type #{type} is reserved for Datadog Test Optimization. " \
332
332
  "Reserved types are: #{Ext::AppTypes::CI_SPAN_TYPES}"
333
333
  )
334
334
  end
@@ -401,6 +401,14 @@ module Datadog
401
401
  def test_optimisation
402
402
  components.test_optimisation
403
403
  end
404
+
405
+ def test_management
406
+ components.test_management
407
+ end
408
+
409
+ def test_retries
410
+ components.test_retries
411
+ end
404
412
  end
405
413
  end
406
414
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datadog-ci
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.12.0
4
+ version: 1.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-01-23 00:00:00.000000000 Z
10
+ date: 2025-02-25 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: datadog
@@ -38,7 +38,7 @@ dependencies:
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
40
  description: |2
41
- datadog-ci is a Datadog's CI visibility library for Ruby. It traces
41
+ datadog-ci is a Datadog's Test Optimization library for Ruby. It traces
42
42
  tests as they are being executed and brings developers visibility into
43
43
  their CI pipelines.
44
44
  email:
@@ -164,6 +164,9 @@ files:
164
164
  - lib/datadog/ci/remote/slow_test_retries.rb
165
165
  - lib/datadog/ci/span.rb
166
166
  - lib/datadog/ci/test.rb
167
+ - lib/datadog/ci/test_management/component.rb
168
+ - lib/datadog/ci/test_management/null_component.rb
169
+ - lib/datadog/ci/test_management/tests_properties.rb
167
170
  - lib/datadog/ci/test_module.rb
168
171
  - lib/datadog/ci/test_optimisation/component.rb
169
172
  - lib/datadog/ci/test_optimisation/coverage/ddcov.rb
@@ -179,18 +182,21 @@ files:
179
182
  - lib/datadog/ci/test_retries/driver/base.rb
180
183
  - lib/datadog/ci/test_retries/driver/no_retry.rb
181
184
  - lib/datadog/ci/test_retries/driver/retry_failed.rb
185
+ - lib/datadog/ci/test_retries/driver/retry_flaky_fixed.rb
182
186
  - lib/datadog/ci/test_retries/driver/retry_new.rb
183
187
  - lib/datadog/ci/test_retries/null_component.rb
184
188
  - lib/datadog/ci/test_retries/strategy/base.rb
185
189
  - lib/datadog/ci/test_retries/strategy/no_retry.rb
186
190
  - lib/datadog/ci/test_retries/strategy/retry_failed.rb
191
+ - lib/datadog/ci/test_retries/strategy/retry_flaky_fixed.rb
187
192
  - lib/datadog/ci/test_retries/strategy/retry_new.rb
188
- - lib/datadog/ci/test_retries/unique_tests_client.rb
189
193
  - lib/datadog/ci/test_session.rb
190
194
  - lib/datadog/ci/test_suite.rb
195
+ - lib/datadog/ci/test_visibility/capabilities.rb
191
196
  - lib/datadog/ci/test_visibility/component.rb
192
197
  - lib/datadog/ci/test_visibility/context.rb
193
198
  - lib/datadog/ci/test_visibility/flush.rb
199
+ - lib/datadog/ci/test_visibility/known_tests.rb
194
200
  - lib/datadog/ci/test_visibility/null_component.rb
195
201
  - lib/datadog/ci/test_visibility/null_transport.rb
196
202
  - lib/datadog/ci/test_visibility/serializers/base.rb
@@ -254,5 +260,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
254
260
  requirements: []
255
261
  rubygems_version: 3.6.2
256
262
  specification_version: 4
257
- summary: Datadog CI visibility for your ruby application
263
+ summary: Datadog Test Optimization for your ruby application
258
264
  test_files: []