datadog-ci 1.28.0 → 1.29.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e9b1bc98a924cc6601633633b9feb70bf48f79872266845b55505f1c5cd12837
4
- data.tar.gz: e3aa6b68b3d65959178370fb7c4f2ee26094efb8de1da9e7f22d0b35e0a07729
3
+ metadata.gz: ac5578878c72238512be9428c1414a047b03a854d97e71830328683bcff6677c
4
+ data.tar.gz: 6c91bfd2ed06cc0c87f8a4537ca32c100ee9e1f4f825aa10c05971ee320aa7cb
5
5
  SHA512:
6
- metadata.gz: c0562103f425826f918fd9c2c53ceacdb27e6e19280fd2e9ce47c67e1a7da631ab0b4c6dcf2fe28868aa166f5c5822b160a539f6f65ec462d5f7417c4d491a58
7
- data.tar.gz: 4dbf986a49b767d5bc579800bd89716ef24f3263017c40c3410026fc879a5774626bb84f572386eb7ed93298031ed680ccbf1e7ff280061ba2d5d3674ab5c8fe
6
+ metadata.gz: e0260d48fffd5ad1a63c9859afa4a14025325454437d54fd0fce6518afec538b091a3f49289042f93a0a4a515e03dfc80288a19b11c71dc965e01ce3c47b0824
7
+ data.tar.gz: b2097df46c9cfa6b197c237dc9e80ca022204f1950c91f35870965036a60f9c90be895220a82d4bda73f3a22e20ce30b9261308d349d4519a2eca78f21f897bf
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.29.0] - 2026-05-04
4
+
5
+ ### Added
6
+
7
+ * Add _dd.ci.library_configuration_error.* tags on backend errors ([#501][])
8
+ * Add cucumber 11 ([#499][])
9
+
10
+ ### Changed
11
+
12
+ * Make attempt_to_fix take precedence over quarantine and disabled ([#498][])
13
+ * Log error when Datadog API rejects request due to API key ([#500][])
14
+ * Enable static dependency tracking by default ([#493][])
15
+
16
+ ### Fixed
17
+
18
+ * Handle nil DDTest skippable tests data ([#491][])
19
+
20
+ ### Removed
21
+
22
+ * Remove JRuby from the CI matrix ([#494][])
23
+
3
24
  ## [1.28.0] - 2026-02-16
4
25
 
5
26
  ### Added
@@ -608,7 +629,8 @@ Currently test suite level visibility is not used by our instrumentation: it wil
608
629
 
609
630
  - Ruby versions < 2.7 no longer supported ([#8][])
610
631
 
611
- [Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.28.0...main
632
+ [Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.29.0...main
633
+ [1.29.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.28.0...v1.29.0
612
634
  [1.28.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.27.0...v1.28.0
613
635
  [1.27.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.26.0...v1.27.0
614
636
  [1.26.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.25.0...v1.26.0
@@ -862,4 +884,11 @@ Currently test suite level visibility is not used by our instrumentation: it wil
862
884
  [#463]: https://github.com/DataDog/datadog-ci-rb/issues/463
863
885
  [#464]: https://github.com/DataDog/datadog-ci-rb/issues/464
864
886
  [#468]: https://github.com/DataDog/datadog-ci-rb/issues/468
865
- [#473]: https://github.com/DataDog/datadog-ci-rb/issues/473
887
+ [#473]: https://github.com/DataDog/datadog-ci-rb/issues/473
888
+ [#491]: https://github.com/DataDog/datadog-ci-rb/issues/491
889
+ [#493]: https://github.com/DataDog/datadog-ci-rb/issues/493
890
+ [#494]: https://github.com/DataDog/datadog-ci-rb/issues/494
891
+ [#498]: https://github.com/DataDog/datadog-ci-rb/issues/498
892
+ [#499]: https://github.com/DataDog/datadog-ci-rb/issues/499
893
+ [#500]: https://github.com/DataDog/datadog-ci-rb/issues/500
894
+ [#501]: https://github.com/DataDog/datadog-ci-rb/issues/501
@@ -183,7 +183,7 @@ module Datadog
183
183
  option :tia_static_dependencies_tracking_enabled do |o|
184
184
  o.type :bool
185
185
  o.env CI::Ext::Settings::ENV_TIA_STATIC_DEPENDENCIES_TRACKING_ENABLED
186
- o.default false
186
+ o.default true
187
187
  end
188
188
 
189
189
  option :code_coverage_report_upload_enabled do |o|
@@ -39,8 +39,9 @@ module Datadog
39
39
  def begin_scenario(test_case)
40
40
  datadog_test = Datadog::CI.active_test
41
41
 
42
- # special case for cucumber-ruby: we skip quarantined tests, thus for cucumber quarantined is the same as disabled
43
- if datadog_test&.should_skip? || datadog_test&.quarantined?
42
+ # special case for cucumber-ruby: we skip quarantined tests, thus for cucumber quarantined is the same as disabled.
43
+ # attempt_to_fix takes precedence over quarantine: such tests must run (and be retried) even when quarantined.
44
+ if datadog_test&.should_skip? || (datadog_test&.quarantined? && !datadog_test.attempt_to_fix?)
44
45
  raise ::Cucumber::Core::Test::Result::Skipped, datadog_test.datadog_skip_reason
45
46
  end
46
47
 
@@ -93,6 +93,17 @@ module Datadog
93
93
  TAG_HAS_FAILED_ALL_RETRIES = "test.has_failed_all_retries" # true if test was retried and none of the retries passed
94
94
  TAG_ATTEMPT_TO_FIX_PASSED = "test.test_management.attempt_to_fix_passed" # true if test was marked as "attempted to fix" and all of the retries passed
95
95
 
96
+ # Hidden tags used to indicate that a communication error occurred with the backend
97
+ # during one of the library configuration requests. They are propagated to every CI event
98
+ # (test session, test module, test suite, test) so that the backend can detect when
99
+ # data may be incomplete because the library could not load configuration.
100
+ module LibraryConfigurationError
101
+ TAG_SETTINGS = "_dd.ci.library_configuration_error.settings"
102
+ TAG_SKIPPABLE_TESTS = "_dd.ci.library_configuration_error.skippable_tests"
103
+ TAG_KNOWN_TESTS = "_dd.ci.library_configuration_error.known_tests"
104
+ TAG_TEST_MANAGEMENT_TESTS = "_dd.ci.library_configuration_error.test_management_tests"
105
+ end
106
+
96
107
  # a set of tag indicating which capabilities (features) are supported by the library
97
108
  module LibraryCapabilities
98
109
  TAG_TEST_IMPACT_ANALYSIS = "_dd.library_capabilities.test_impact_analysis"
@@ -140,7 +151,14 @@ module Datadog
140
151
  METRIC_CPU_COUNT = "_dd.host.vcpu_count"
141
152
 
142
153
  # tags that are common for the whole session and can be inherited from the test session
143
- INHERITABLE_TAGS = [TAG_FRAMEWORK, TAG_FRAMEWORK_VERSION].freeze
154
+ INHERITABLE_TAGS = [
155
+ TAG_FRAMEWORK,
156
+ TAG_FRAMEWORK_VERSION,
157
+ LibraryConfigurationError::TAG_SETTINGS,
158
+ LibraryConfigurationError::TAG_SKIPPABLE_TESTS,
159
+ LibraryConfigurationError::TAG_KNOWN_TESTS,
160
+ LibraryConfigurationError::TAG_TEST_MANAGEMENT_TESTS
161
+ ].freeze
144
162
 
145
163
  # could be either "test" or "suite" depending on whether we skip individual tests or whole suites
146
164
  ITR_TEST_SKIPPING_MODE = "test" # we always skip tests (not suites) in Ruby
@@ -49,6 +49,10 @@ module Datadog
49
49
  error_type: http_response.telemetry_error_type,
50
50
  status_code: http_response.code
51
51
  )
52
+
53
+ # mark the test session so that all events emitted in this session are tagged
54
+ # with the hidden _dd.ci.library_configuration_error.settings tag
55
+ test_session.set_tag(Ext::Test::LibraryConfigurationError::TAG_SETTINGS, "true")
52
56
  end
53
57
 
54
58
  library_settings = LibrarySettings.from_http_response(http_response)
@@ -235,8 +235,8 @@ module Datadog
235
235
 
236
236
  # @internal
237
237
  def should_ignore_failures?
238
- return true if quarantined? || disabled?
239
238
  return false if attempt_to_fix?
239
+ return true if quarantined? || disabled?
240
240
 
241
241
  any_retry_passed?
242
242
  end
@@ -271,8 +271,9 @@ module Datadog
271
271
  # Skip status is always preserved
272
272
  return status if status == Ext::Test::Status::SKIP
273
273
 
274
- # For attempt_to_fix tests (not quarantined/disabled), any failure means the fix didn't work
275
- if attempt_to_fix? && !quarantined? && !disabled?
274
+ # attempt_to_fix takes precedence over quarantine/disabled flags:
275
+ # any failure across attempts means the fix didn't work.
276
+ if attempt_to_fix?
276
277
  return all_executions_passed? ? Ext::Test::Status::PASS : Ext::Test::Status::FAIL
277
278
  end
278
279
 
@@ -381,7 +381,7 @@ module Datadog
381
381
  # "data": [{"type": "test", "attributes": {"suite": "suite_name", "name": "test_name", "parameters": "{...}"}}]
382
382
  # }
383
383
  def transform_test_runner_data(skippable_tests_data)
384
- skippable_tests = skippable_tests_data.fetch("skippableTests", {})
384
+ skippable_tests = skippable_tests_data.fetch("skippableTests", {}) || {}
385
385
 
386
386
  # Pre-calculate array size for better memory allocation
387
387
  total_test_configs = skippable_tests.sum do |_, tests_hash|
@@ -111,6 +111,10 @@ module Datadog
111
111
  error_type: http_response.telemetry_error_type,
112
112
  status_code: http_response.code
113
113
  )
114
+
115
+ # mark the test session so that all events emitted in this session are tagged
116
+ # with the hidden _dd.ci.library_configuration_error.skippable_tests tag
117
+ test_session.set_tag(Ext::Test::LibraryConfigurationError::TAG_SKIPPABLE_TESTS, "true")
114
118
  end
115
119
 
116
120
  Response.from_http_response(http_response)
@@ -3,6 +3,7 @@
3
3
  require "json"
4
4
 
5
5
  require_relative "../ext/telemetry"
6
+ require_relative "../ext/test"
6
7
  require_relative "../ext/transport"
7
8
  require_relative "../transport/telemetry"
8
9
  require_relative "../utils/parsing"
@@ -112,6 +113,10 @@ module Datadog
112
113
  error_type: http_response.telemetry_error_type,
113
114
  status_code: http_response.code
114
115
  )
116
+
117
+ # mark the test session so that all events emitted in this session are tagged
118
+ # with the hidden _dd.ci.library_configuration_error.test_management_tests tag
119
+ test_session.set_tag(Ext::Test::LibraryConfigurationError::TAG_TEST_MANAGEMENT_TESTS, "true")
115
120
  end
116
121
 
117
122
  Response.from_http_response(http_response).tests
@@ -63,7 +63,12 @@ module Datadog
63
63
  # uses synchronized method #get_tag to get each tag value
64
64
  res = {}
65
65
  Ext::Test::INHERITABLE_TAGS.each do |tag|
66
- res[tag] = get_tag(tag)
66
+ value = get_tag(tag)
67
+ # skip tags that are not set on the session (e.g. library configuration error tags
68
+ # are only set when the corresponding backend request fails)
69
+ next if value.nil?
70
+
71
+ res[tag] = value
67
72
  end
68
73
  @inheritable_tags = res.freeze
69
74
  end
@@ -103,6 +103,10 @@ module Datadog
103
103
  response = fetch_page(api, test_session, page_state: page_state)
104
104
 
105
105
  unless response.ok?
106
+ # mark the test session so that all events emitted in this session are tagged
107
+ # with the hidden _dd.ci.library_configuration_error.known_tests tag
108
+ test_session.set_tag(Ext::Test::LibraryConfigurationError::TAG_KNOWN_TESTS, "true")
109
+
106
110
  Datadog.logger.debug(
107
111
  "Failed to fetch known tests page ##{page_number}, bailing out of known tests fetch. " \
108
112
  "Early flake detection will not work."
@@ -10,6 +10,18 @@ module Datadog
10
10
  class Agentless < Base
11
11
  attr_reader :api_key
12
12
 
13
+ # HTTP status codes returned by the Datadog backend when a request is
14
+ # rejected for authentication/authorization reasons (typically because
15
+ # of a missing or invalid DD_API_KEY).
16
+ AUTHENTICATION_ERROR_CODES = [401, 403].freeze
17
+
18
+ # Substring we look for in the response payload to confirm the
19
+ # rejection is API-key related rather than some other permission
20
+ # issue. Examples of payloads we want to match:
21
+ # {"errors":[{"status":"403","title":"Forbidden","detail":"API key is missing"}]}
22
+ # {"errors":[{"status":"403","title":"Forbidden","detail":"API key is invalid"}]}
23
+ API_KEY_ERROR_PAYLOAD_MARKER = "API key"
24
+
13
25
  def initialize(api_key:, citestcycle_url:, api_url:, citestcov_url:, logs_intake_url:, cicovreprt_url:)
14
26
  @api_key = api_key
15
27
  @citestcycle_http = build_http_client(citestcycle_url, compress: true)
@@ -17,6 +29,7 @@ module Datadog
17
29
  @citestcov_http = build_http_client(citestcov_url, compress: true)
18
30
  @logs_intake_http = build_http_client(logs_intake_url, compress: true)
19
31
  @cicovreprt_http = build_http_client(cicovreprt_url, compress: false)
32
+ @api_key_error_logged = false
20
33
  end
21
34
 
22
35
  def citestcycle_request(path:, payload:, headers: {}, verb: "post")
@@ -59,13 +72,42 @@ module Datadog
59
72
  private
60
73
 
61
74
  def perform_request(http_client, path:, payload:, headers:, verb:, accept_compressed_response: false)
62
- http_client.request(
75
+ response = http_client.request(
63
76
  path: path,
64
77
  payload: payload,
65
78
  headers: headers_with_default(headers),
66
79
  verb: verb,
67
80
  accept_compressed_response: accept_compressed_response
68
81
  )
82
+
83
+ log_api_key_error(response)
84
+
85
+ response
86
+ end
87
+
88
+ def log_api_key_error(response)
89
+ return if @api_key_error_logged
90
+ return unless AUTHENTICATION_ERROR_CODES.include?(response.code)
91
+ return unless response.payload.to_s.include?(API_KEY_ERROR_PAYLOAD_MARKER)
92
+
93
+ @api_key_error_logged = true
94
+
95
+ if api_key.nil? || api_key.strip.empty?
96
+ Datadog.logger.error do
97
+ "DATADOG CONFIGURATION - TEST OPTIMIZATION - ATTENTION - " \
98
+ "Datadog API rejected the request because DD_API_KEY is not set. " \
99
+ "Please set DD_API_KEY environment variable to a valid Datadog API key. " \
100
+ "Server response: #{response.payload}"
101
+ end
102
+ else
103
+ Datadog.logger.error do
104
+ "DATADOG CONFIGURATION - TEST OPTIMIZATION - ATTENTION - " \
105
+ "Datadog API rejected the request because the configured DD_API_KEY is invalid. " \
106
+ "Please verify that DD_API_KEY environment variable is set to a valid Datadog API key " \
107
+ "for the configured DD_SITE. " \
108
+ "Server response: #{response.payload}"
109
+ end
110
+ end
69
111
  end
70
112
 
71
113
  def build_http_client(url, compress:)
@@ -4,7 +4,7 @@ module Datadog
4
4
  module CI
5
5
  module VERSION
6
6
  MAJOR = 1
7
- MINOR = 28
7
+ MINOR = 29
8
8
  PATCH = 0
9
9
  PRE = nil
10
10
  BUILD = nil
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datadog-ci
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.28.0
4
+ version: 1.29.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.