datadog-ci 1.33.0 → 1.35.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -2
  3. data/lib/datadog/ci/configuration/components.rb +2 -0
  4. data/lib/datadog/ci/configuration/settings.rb +11 -0
  5. data/lib/datadog/ci/contrib/cucumber/formatter.rb +51 -1
  6. data/lib/datadog/ci/contrib/cucumber/instrumentation.rb +5 -0
  7. data/lib/datadog/ci/contrib/minitest/helpers.rb +54 -6
  8. data/lib/datadog/ci/contrib/minitest/runnable.rb +5 -0
  9. data/lib/datadog/ci/contrib/minitest/runnable_minitest_6.rb +5 -0
  10. data/lib/datadog/ci/contrib/minitest/test.rb +21 -27
  11. data/lib/datadog/ci/contrib/patcher.rb +0 -4
  12. data/lib/datadog/ci/contrib/rspec/anonymous_example_name.rb +515 -0
  13. data/lib/datadog/ci/contrib/rspec/example.rb +23 -5
  14. data/lib/datadog/ci/contrib/rspec/example_group.rb +11 -1
  15. data/lib/datadog/ci/contrib/simplecov/result_extractor.rb +9 -1
  16. data/lib/datadog/ci/ext/settings.rb +1 -0
  17. data/lib/datadog/ci/ext/telemetry.rb +1 -0
  18. data/lib/datadog/ci/ext/test.rb +8 -1
  19. data/lib/datadog/ci/remote/library_settings_client.rb +3 -2
  20. data/lib/datadog/ci/source_code/method_inspect.rb +0 -3
  21. data/lib/datadog/ci/test_discovery/component.rb +4 -0
  22. data/lib/datadog/ci/test_impact_analysis/component.rb +176 -59
  23. data/lib/datadog/ci/test_impact_analysis/coverage/event.rb +6 -4
  24. data/lib/datadog/ci/test_impact_analysis/null_component.rb +25 -2
  25. data/lib/datadog/ci/test_impact_analysis/skippable.rb +20 -5
  26. data/lib/datadog/ci/test_impact_analysis/skippable_percentage/estimator.rb +3 -3
  27. data/lib/datadog/ci/test_optimization_cache/readers/legacy.rb +1 -1
  28. data/lib/datadog/ci/test_suite.rb +20 -0
  29. data/lib/datadog/ci/test_tracing/component.rb +10 -0
  30. data/lib/datadog/ci/test_tracing/deprecated_total_coverage_metric.rb +11 -1
  31. data/lib/datadog/ci/utils/configuration.rb +12 -0
  32. data/lib/datadog/ci/utils/test_name.rb +112 -0
  33. data/lib/datadog/ci/version.rb +1 -1
  34. metadata +3 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6e5dd4e5fdf647baf9134e9fdcc5eb6d1215c48b08bf1905dd69e731a8ebe18e
4
- data.tar.gz: 851eadff7367e69480490eea9fb7e5e074c2099008de2537c07efba2de9366bf
3
+ metadata.gz: f1aa81f9987d876f143abe3a9b7721e1a5b35178c5c854dcdaf79b872c5a0c9e
4
+ data.tar.gz: 261ea8b1b233ac66c0ba7b417018fe477afb05a274ed6607b6bf3c235c58c3ff
5
5
  SHA512:
6
- metadata.gz: 22bedcab49da1df468ae63d217b160d7e3a976a5b722aaa012622564629d8760afc39165b47dc8be7454b406db8e073be46524d0de02fa2b6fd13a4e685713c8
7
- data.tar.gz: 9affe9fe044871139755c3fbbb0b28146bc07c2b36ed79627ad0db67ee254764fbca12bebafc9a317224bd275f9c60e5a978a3c526b8a11c0a4a92150152f104
6
+ metadata.gz: 05aed65898a6b065f99e6a9fd2aab34023fd3ac77aaacc4146d39d8a1e6653ad5cb6d970afc5e6d590a4ddf6eec3f54704089a5ea49d7bf2f614d27d1572ed91
7
+ data.tar.gz: f89daf423afed1bd0ac7c227a28c4a980b92916fcf03a078a9278eb880e059c9fb40f8dc3b0887645e8a806f2e879e70bf23eaaa7500cd3e6e6e2d4b518a449f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.35.0] - 2026-07-23
4
+
5
+ ## [1.34.0] - 2026-06-25
6
+
7
+ ### Added
8
+
9
+ * Add configurable TIA skipping mode ([#530][])
10
+
3
11
  ## [1.33.0] - 2026-06-19
4
12
 
5
13
  ### Added
@@ -659,7 +667,9 @@ Currently test suite level visibility is not used by our instrumentation: it wil
659
667
 
660
668
  - Ruby versions < 2.7 no longer supported ([#8][])
661
669
 
662
- [Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.33.0...main
670
+ [Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.35.0...main
671
+ [1.35.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.34.0...v1.35.0
672
+ [1.34.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.33.0...v1.34.0
663
673
  [1.33.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.32.0...v1.33.0
664
674
  [1.32.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.31.0...v1.32.0
665
675
  [1.31.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.30.0...v1.31.0
@@ -933,4 +943,5 @@ Currently test suite level visibility is not used by our instrumentation: it wil
933
943
  [#516]: https://github.com/DataDog/datadog-ci-rb/issues/516
934
944
  [#521]: https://github.com/DataDog/datadog-ci-rb/issues/521
935
945
  [#522]: https://github.com/DataDog/datadog-ci-rb/issues/522
936
- [#527]: https://github.com/DataDog/datadog-ci-rb/issues/527
946
+ [#527]: https://github.com/DataDog/datadog-ci-rb/issues/527
947
+ [#530]: https://github.com/DataDog/datadog-ci-rb/issues/530
@@ -210,6 +210,7 @@ module Datadog
210
210
  api: test_visibility_api,
211
211
  dd_env: settings.env,
212
212
  config_tags: custom_configuration(settings),
213
+ test_skipping_mode: settings.ci.tia_test_skipping_mode,
213
214
  coverage_writer: build_coverage_writer(settings, test_visibility_api),
214
215
  enabled: settings.ci.enabled && settings.ci.itr_enabled,
215
216
  bundle_location: settings.ci.itr_code_coverage_excluded_bundle_path,
@@ -293,6 +294,7 @@ module Datadog
293
294
  Remote::LibrarySettingsClient.new(
294
295
  api: api,
295
296
  dd_env: settings.env,
297
+ test_skipping_mode: settings.ci.tia_test_skipping_mode,
296
298
  config_tags: custom_configuration(settings)
297
299
  )
298
300
  end
@@ -2,8 +2,10 @@
2
2
 
3
3
  require_relative "../contrib/instrumentation"
4
4
  require_relative "../ext/settings"
5
+ require_relative "../ext/test"
5
6
  require_relative "../ext/test_optimization_cache"
6
7
  require_relative "../utils/bundle"
8
+ require_relative "../utils/configuration"
7
9
  require_relative "../utils/parsing"
8
10
 
9
11
  module Datadog
@@ -79,6 +81,15 @@ module Datadog
79
81
  o.default true
80
82
  end
81
83
 
84
+ option :tia_test_skipping_mode do |o|
85
+ o.type :string
86
+ o.env CI::Ext::Settings::ENV_TIA_TEST_SKIPPING_MODE
87
+ o.default CI::Ext::Test::TIATestSkippingMode::TEST
88
+ o.setter do |mode|
89
+ Utils::Configuration.normalize_tia_test_skipping_mode(mode)
90
+ end
91
+ end
92
+
82
93
  option :git_metadata_upload_enabled do |o|
83
94
  o.type :bool
84
95
  o.env CI::Ext::Settings::ENV_GIT_METADATA_UPLOAD_ENABLED
@@ -72,9 +72,14 @@ module Datadog
72
72
  end
73
73
 
74
74
  unless same_test_suite_as_current?(test_suite_name)
75
+ suite_tags = test_suite_source_file_tags(event.test_case)
76
+ if test_suite_unskippable?(event.test_case)
77
+ suite_tags[CI::Ext::Test::TAG_ITR_UNSKIPPABLE] = "true"
78
+ end
79
+
75
80
  start_test_suite(
76
81
  test_suite_name,
77
- tags: test_suite_source_file_tags(event.test_case)
82
+ tags: suite_tags
78
83
  )
79
84
  end
80
85
 
@@ -228,6 +233,51 @@ module Datadog
228
233
  CI::Ext::Test::TAG_SOURCE_START => line_number.to_s
229
234
  }
230
235
  end
236
+
237
+ def test_suite_unskippable?(test_case)
238
+ feature = if test_case.respond_to?(:feature)
239
+ test_case.feature
240
+ else
241
+ @ast_lookup&.gherkin_document(test_case.location.file)&.feature
242
+ end
243
+
244
+ node_has_unskippable_tag?(feature) || test_case_unskippable?(test_case)
245
+ rescue
246
+ test_case_unskippable?(test_case)
247
+ end
248
+
249
+ def node_has_unskippable_tag?(node)
250
+ return false if node.nil?
251
+ return true if node_tags_include_unskippable?(node)
252
+
253
+ if node.respond_to?(:scenario) && node.scenario
254
+ return true if node_has_unskippable_tag?(node.scenario)
255
+ end
256
+
257
+ if node.respond_to?(:rule) && node.rule
258
+ return true if node_has_unskippable_tag?(node.rule)
259
+ end
260
+
261
+ return false unless node.respond_to?(:children)
262
+
263
+ node.children.any? { |child| node_has_unskippable_tag?(child) }
264
+ end
265
+
266
+ def node_tags_include_unskippable?(node)
267
+ return false unless node.respond_to?(:tags)
268
+
269
+ node.tags.any? { |tag| tag.respond_to?(:name) && tag.name == datadog_itr_unskippable_tag }
270
+ end
271
+
272
+ def test_case_unskippable?(test_case)
273
+ test_case.match_tags?(datadog_itr_unskippable_tag)
274
+ rescue
275
+ false
276
+ end
277
+
278
+ def datadog_itr_unskippable_tag
279
+ "@#{CI::Ext::Test::ITR_UNSKIPPABLE_OPTION}"
280
+ end
231
281
  end
232
282
  end
233
283
  end
@@ -37,6 +37,11 @@ module Datadog
37
37
  end
38
38
 
39
39
  def begin_scenario(test_case)
40
+ active_suite = Datadog::CI.active_test_suite
41
+ if active_suite&.should_skip?
42
+ raise ::Cucumber::Core::Test::Result::Skipped, active_suite.datadog_skip_reason
43
+ end
44
+
40
45
  datadog_test = Datadog::CI.active_test
41
46
 
42
47
  # special case for cucumber-ruby: we skip quarantined tests, thus for cucumber quarantined is the same as disabled.
@@ -7,6 +7,31 @@ module Datadog
7
7
  module Contrib
8
8
  module Minitest
9
9
  module Helpers
10
+ module RunnableClassMethods
11
+ def datadog_itr_unskippable(*args)
12
+ if args.nil? || args.empty?
13
+ @datadog_itr_unskippable_suite = true
14
+ else
15
+ @datadog_itr_unskippable_tests = args
16
+ end
17
+ end
18
+
19
+ def dd_suite_unskippable?
20
+ @datadog_itr_unskippable_suite
21
+ end
22
+
23
+ def dd_test_unskippable?(test_name)
24
+ tests = @datadog_itr_unskippable_tests
25
+ return false unless tests
26
+
27
+ tests.include?(test_name)
28
+ end
29
+
30
+ def dd_any_unskippable?
31
+ dd_suite_unskippable? || !!@datadog_itr_unskippable_tests
32
+ end
33
+ end
34
+
10
35
  def self.start_test_suite(klass)
11
36
  method = klass.runnable_methods.first
12
37
  return nil if method.nil?
@@ -22,6 +47,9 @@ module Datadog
22
47
  else
23
48
  {}
24
49
  end
50
+ if klass.dd_any_unskippable?
51
+ test_suite_tags[CI::Ext::Test::TAG_ITR_UNSKIPPABLE] = "true"
52
+ end
25
53
 
26
54
  test_tracing_component = Datadog.send(:components).test_tracing
27
55
  test_suite = test_tracing_component.start_test_suite(
@@ -53,11 +81,19 @@ module Datadog
53
81
  end
54
82
 
55
83
  def self.extract_runnable_source_location(klass, method_name)
56
- source_location = extract_source_location_from_class(klass)
57
- if source_location.nil? || source_location.empty?
58
- return klass.instance_method(method_name).source_location
84
+ method_source_location = extract_source_location_from_method(klass, method_name)
85
+ klass_source_location = extract_source_location_from_class(klass)
86
+
87
+ if defined?(::Minitest::Spec) && klass&.ancestors&.include?(::Minitest::Spec)
88
+ method_source_location || klass_source_location
89
+ else
90
+ klass_source_location || method_source_location
59
91
  end
60
- source_location
92
+ end
93
+
94
+ def self.skip_test_suite(test_suite)
95
+ test_suite&.finish
96
+ []
61
97
  end
62
98
 
63
99
  def self.parallel?(klass)
@@ -69,9 +105,21 @@ module Datadog
69
105
  end
70
106
 
71
107
  def self.extract_source_location_from_class(klass)
72
- return [] if klass.nil? || klass.name.nil?
108
+ return nil if klass.nil? || klass.name.nil?
73
109
 
74
- SourceCode::ConstantResolver.safely_get_const_source_location(klass.name) || []
110
+ empty_source_location_to_nil(SourceCode::ConstantResolver.safely_get_const_source_location(klass.name))
111
+ end
112
+
113
+ def self.extract_source_location_from_method(klass, method_name)
114
+ return nil if klass.nil?
115
+ return nil if method_name.nil? || method_name.empty?
116
+
117
+ empty_source_location_to_nil(klass.instance_method(method_name).source_location)
118
+ end
119
+
120
+ def self.empty_source_location_to_nil(source_location)
121
+ return nil if source_location.nil? || source_location.empty?
122
+ source_location
75
123
  end
76
124
  end
77
125
  end
@@ -10,11 +10,16 @@ module Datadog
10
10
  end
11
11
 
12
12
  module ClassMethods
13
+ include Helpers::RunnableClassMethods
14
+
13
15
  def run(*args)
14
16
  return super unless datadog_configuration[:enabled]
15
17
  return super if Helpers.parallel?(self)
16
18
 
17
19
  test_suite = Helpers.start_test_suite(self)
20
+ if test_suite&.should_skip?
21
+ return Helpers.skip_test_suite(test_suite)
22
+ end
18
23
 
19
24
  results = super
20
25
  return results unless test_suite
@@ -10,11 +10,16 @@ module Datadog
10
10
  end
11
11
 
12
12
  module ClassMethods
13
+ include Helpers::RunnableClassMethods
14
+
13
15
  def run_suite(*args)
14
16
  return super unless datadog_configuration[:enabled]
15
17
  return super if Helpers.parallel?(self)
16
18
 
17
19
  test_suite = Helpers.start_test_suite(self)
20
+ if test_suite&.should_skip?
21
+ return Helpers.skip_test_suite(test_suite)
22
+ end
18
23
 
19
24
  results = super
20
25
  return results unless test_suite
@@ -37,6 +37,11 @@ module Datadog
37
37
 
38
38
  return run_without_datadog_reentry if datadog_run_reentered?
39
39
 
40
+ test_suite = start_datadog_test_suite_if_parallel
41
+ if test_suite&.should_skip?
42
+ return skip_datadog_suite(test_suite)
43
+ end
44
+
40
45
  test_span = start_datadog_test
41
46
  return skip_datadog_test(test_span) if test_span&.should_skip?
42
47
 
@@ -76,10 +81,6 @@ module Datadog
76
81
  end
77
82
 
78
83
  def start_datadog_test
79
- if Helpers.parallel?(self.class)
80
- Helpers.start_test_suite(self.class)
81
- end
82
-
83
84
  test_suite_name = Helpers.test_suite_name(self.class, name)
84
85
 
85
86
  # @type var tags : Hash[String, String]
@@ -103,15 +104,17 @@ module Datadog
103
104
  tags: tags,
104
105
  service: datadog_configuration[:service_name]
105
106
  )
106
- # Steep type checker doesn't know that we patched Minitest::Test class definition
107
- #
108
- # steep:ignore:start
109
107
  test_span&.itr_unskippable! if self.class.dd_suite_unskippable? || self.class.dd_test_unskippable?(name)
110
- # steep:ignore:end
111
108
 
112
109
  test_span
113
110
  end
114
111
 
112
+ def start_datadog_test_suite_if_parallel
113
+ return unless Helpers.parallel?(self.class)
114
+
115
+ Helpers.start_test_suite(self.class)
116
+ end
117
+
115
118
  def skip_datadog_test(test_span)
116
119
  time_it do
117
120
  capture_exceptions do
@@ -124,6 +127,16 @@ module Datadog
124
127
  ::Minitest::Result.from(self)
125
128
  end
126
129
 
130
+ def skip_datadog_suite(test_suite)
131
+ time_it do
132
+ capture_exceptions do
133
+ skip(test_suite.datadog_skip_reason)
134
+ end
135
+ end
136
+
137
+ ::Minitest::Result.from(self)
138
+ end
139
+
127
140
  def finish_with_result(span, result_code)
128
141
  return unless span
129
142
 
@@ -158,25 +171,6 @@ module Datadog
158
171
  ensure
159
172
  super
160
173
  end
161
-
162
- def datadog_itr_unskippable(*args)
163
- if args.nil? || args.empty?
164
- @datadog_itr_unskippable_suite = true
165
- else
166
- @datadog_itr_unskippable_tests = args
167
- end
168
- end
169
-
170
- def dd_suite_unskippable?
171
- @datadog_itr_unskippable_suite
172
- end
173
-
174
- def dd_test_unskippable?(test_name)
175
- tests = @datadog_itr_unskippable_tests
176
- return false unless tests
177
-
178
- tests.include?(test_name)
179
- end
180
174
  end
181
175
  end
182
176
  end
@@ -29,13 +29,9 @@ module Datadog
29
29
  return unless defined?(super)
30
30
 
31
31
  patch_only_once.run do
32
- # There is no way to tell Steep that we prepend these methods to modules that have .patch method
33
- #
34
- # steep:ignore:start
35
32
  super.tap do
36
33
  @patch_successful = true
37
34
  end
38
- # steep:ignore:end
39
35
  rescue => e
40
36
  on_patch_error(e)
41
37
  end