datadog-ci 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 262f238c451500ef744ff6f24ba5176a5e2064ef49d46055965bb5ed13bc70ea
4
- data.tar.gz: 3023b3bca603feb1a591ebf8e68c9339cc887aaea2466fda2596c3cb6a209295
3
+ metadata.gz: 85bb3584dc8081e8f6e8531d2a600e3948f4f9ea23d551dd99df0eab9daf2a3c
4
+ data.tar.gz: b6f686d49d3dbb1695c0f6e20b434b77648a118c32e1691f4a0f30b13d38dc62
5
5
  SHA512:
6
- metadata.gz: a15556f403d45a9b4384643028ca42971f40847f7cb0a090ae4776a2e4f1615931fe8612f5306f21578ed30d72e15d725d1fbe74f488752cbec01f30f74a67ed
7
- data.tar.gz: 02c7f199399babed0311149c2abca1a3cad27b705d92b139e0485351589707e24ee136ccebae5cef1361afdaabdfe5e2209fa97bd65edfeb947777156bd91c68
6
+ metadata.gz: 8e8ae716ea7c7408c59c11efa5a3bc30e0767a02c659e231f1cec2a02d5494d451c1e2b43c7c16240cbe7c9418f004ead32faa09b08c83bcd878156fa1062ae9
7
+ data.tar.gz: 15bdfb45e32660356fcc4c5726c4a640c78cfc2d5007807623f532a0b7c321f0b11a32d0a75f571a64ae263159d4b86b0a750dda5d7e8a5c33f9c110bf7682a0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.1] - 2023-12-11
4
+
5
+ ### Fixed
6
+
7
+ * do not collect environment tags when CI is not enabled ([#87][])
8
+
9
+ ### Changed
10
+
11
+ * Move private classes and modules deeper in module hierarchy ([#85][])
12
+ * update appraisal dependencies ([#84][])
13
+
3
14
  ## [0.5.0] - 2023-12-06
4
15
 
5
16
  ### Test suite level visibility
@@ -101,7 +112,8 @@ Currently test suite level visibility is not used by our instrumentation: it wil
101
112
 
102
113
  * Ruby versions < 2.7 no longer supported ([#8][])
103
114
 
104
- [Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v0.5.0...main
115
+ [Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v0.5.1...main
116
+ [0.5.1]: https://github.com/DataDog/datadog-ci-rb/compare/v0.5.0...v0.5.1
105
117
  [0.5.0]: https://github.com/DataDog/datadog-ci-rb/compare/v0.4.1...v0.5.0
106
118
  [0.4.1]: https://github.com/DataDog/datadog-ci-rb/compare/v0.4.0...v0.4.1
107
119
  [0.4.0]: https://github.com/DataDog/datadog-ci-rb/compare/v0.3.0...v0.4.0
@@ -138,4 +150,7 @@ Currently test suite level visibility is not used by our instrumentation: it wil
138
150
  [#79]: https://github.com/DataDog/datadog-ci-rb/issues/79
139
151
  [#80]: https://github.com/DataDog/datadog-ci-rb/issues/80
140
152
  [#81]: https://github.com/DataDog/datadog-ci-rb/issues/81
141
- [#82]: https://github.com/DataDog/datadog-ci-rb/issues/82
153
+ [#82]: https://github.com/DataDog/datadog-ci-rb/issues/82
154
+ [#84]: https://github.com/DataDog/datadog-ci-rb/issues/84
155
+ [#85]: https://github.com/DataDog/datadog-ci-rb/issues/85
156
+ [#87]: https://github.com/DataDog/datadog-ci-rb/issues/87
@@ -6,11 +6,11 @@ require "datadog/core/remote/negotiation"
6
6
  require_relative "../ext/transport"
7
7
  require_relative "../ext/settings"
8
8
  require_relative "../test_visibility/flush"
9
- require_relative "../test_visibility/transport"
9
+ require_relative "../test_visibility/recorder"
10
10
  require_relative "../test_visibility/serializers/factories/test_level"
11
11
  require_relative "../test_visibility/serializers/factories/test_suite_level"
12
+ require_relative "../test_visibility/transport"
12
13
  require_relative "../transport/api/builder"
13
- require_relative "../recorder"
14
14
 
15
15
  module Datadog
16
16
  module CI
@@ -23,7 +23,7 @@ module Datadog
23
23
  # Activate CI mode if enabled
24
24
  activate_ci!(settings) if settings.ci.enabled
25
25
 
26
- @ci_recorder = Recorder.new(
26
+ @ci_recorder = TestVisibility::Recorder.new(
27
27
  enabled: settings.ci.enabled,
28
28
  test_suite_level_visibility_enabled: settings.ci.experimental_test_suite_level_visibility_enabled
29
29
  )
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "datadog/core/configuration/settings"
4
+ require "datadog/core/configuration/components"
5
+
6
+ require_relative "settings"
7
+ require_relative "components"
8
+
9
+ module Datadog
10
+ module CI
11
+ module Configuration
12
+ # Extends Datadog tracing with CI features
13
+ module Extensions
14
+ def self.activate!
15
+ Core::Configuration::Settings.extend(CI::Configuration::Settings)
16
+ Core::Configuration::Components.prepend(CI::Configuration::Components)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module CI
5
+ module TestVisibility
6
+ module Context
7
+ # This context is shared between threads and represents the current test session and test module.
8
+ class Global
9
+ def initialize
10
+ # we are using Monitor instead of Mutex because it is reentrant
11
+ @mutex = Monitor.new
12
+
13
+ @test_session = nil
14
+ @test_module = nil
15
+ @test_suites = {}
16
+ end
17
+
18
+ def fetch_or_activate_test_suite(test_suite_name, &block)
19
+ @mutex.synchronize do
20
+ @test_suites[test_suite_name] ||= block.call
21
+ end
22
+ end
23
+
24
+ def fetch_or_activate_test_module(&block)
25
+ @mutex.synchronize do
26
+ @test_module ||= block.call
27
+ end
28
+ end
29
+
30
+ def fetch_or_activate_test_session(&block)
31
+ @mutex.synchronize do
32
+ @test_session ||= block.call
33
+ end
34
+ end
35
+
36
+ def active_test_module
37
+ @test_module
38
+ end
39
+
40
+ def active_test_session
41
+ @test_session
42
+ end
43
+
44
+ def active_test_suite(test_suite_name)
45
+ @mutex.synchronize { @test_suites[test_suite_name] }
46
+ end
47
+
48
+ def service
49
+ @mutex.synchronize do
50
+ # thank you RBS for this weirdness
51
+ test_session = @test_session
52
+ test_session.service if test_session
53
+ end
54
+ end
55
+
56
+ def inheritable_session_tags
57
+ @mutex.synchronize do
58
+ test_session = @test_session
59
+ if test_session
60
+ test_session.inheritable_tags
61
+ else
62
+ {}
63
+ end
64
+ end
65
+ end
66
+
67
+ def deactivate_test_session!
68
+ @mutex.synchronize { @test_session = nil }
69
+ end
70
+
71
+ def deactivate_test_module!
72
+ @mutex.synchronize { @test_module = nil }
73
+ end
74
+
75
+ def deactivate_test_suite!(test_suite_name)
76
+ @mutex.synchronize { @test_suites.delete(test_suite_name) }
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module CI
5
+ module TestVisibility
6
+ module Context
7
+ class Local
8
+ def initialize
9
+ @key = :datadog_ci_active_test
10
+
11
+ self.active_test = nil
12
+ end
13
+
14
+ def activate_test!(test)
15
+ raise "Nested tests are not supported. Currently active test: #{active_test}" unless active_test.nil?
16
+
17
+ if block_given?
18
+ begin
19
+ self.active_test = test
20
+ yield
21
+ ensure
22
+ self.active_test = nil
23
+ end
24
+ else
25
+ self.active_test = test
26
+ end
27
+ end
28
+
29
+ def deactivate_test!(test)
30
+ return if active_test.nil?
31
+
32
+ if active_test == test
33
+ self.active_test = nil
34
+ else
35
+ raise "Trying to deactivate test #{test}, but currently active test is #{active_test}"
36
+ end
37
+ end
38
+
39
+ def active_test
40
+ Thread.current[@key]
41
+ end
42
+
43
+ private
44
+
45
+ def active_test=(test)
46
+ Thread.current[@key] = test
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,293 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "datadog/tracing"
4
+ require "datadog/tracing/trace_digest"
5
+
6
+ require "rbconfig"
7
+
8
+ require_relative "context/global"
9
+ require_relative "context/local"
10
+
11
+ require_relative "../ext/app_types"
12
+ require_relative "../ext/test"
13
+ require_relative "../ext/environment"
14
+
15
+ require_relative "../span"
16
+ require_relative "../null_span"
17
+ require_relative "../test"
18
+ require_relative "../test_session"
19
+ require_relative "../test_module"
20
+ require_relative "../test_suite"
21
+
22
+ module Datadog
23
+ module CI
24
+ module TestVisibility
25
+ # Common behavior for CI tests
26
+ # Note: this class has too many responsibilities and should be split into multiple classes
27
+ class Recorder
28
+ attr_reader :environment_tags, :test_suite_level_visibility_enabled, :enabled
29
+
30
+ def initialize(enabled: true, test_suite_level_visibility_enabled: false)
31
+ @enabled = enabled
32
+ @test_suite_level_visibility_enabled = enabled && test_suite_level_visibility_enabled
33
+
34
+ @environment_tags = @enabled ? Ext::Environment.tags(ENV).freeze : {}
35
+ @local_context = Context::Local.new
36
+ @global_context = Context::Global.new
37
+ end
38
+
39
+ def start_test_session(service: nil, tags: {})
40
+ return skip_tracing unless test_suite_level_visibility_enabled
41
+
42
+ @global_context.fetch_or_activate_test_session do
43
+ tracer_span = start_datadog_tracer_span(
44
+ "test.session", build_span_options(service, Ext::AppTypes::TYPE_TEST_SESSION)
45
+ )
46
+ set_session_context(tags, tracer_span)
47
+
48
+ build_test_session(tracer_span, tags)
49
+ end
50
+ end
51
+
52
+ def start_test_module(test_module_name, service: nil, tags: {})
53
+ return skip_tracing unless test_suite_level_visibility_enabled
54
+
55
+ @global_context.fetch_or_activate_test_module do
56
+ set_inherited_globals(tags)
57
+ set_session_context(tags)
58
+
59
+ tracer_span = start_datadog_tracer_span(
60
+ test_module_name, build_span_options(service, Ext::AppTypes::TYPE_TEST_MODULE)
61
+ )
62
+ set_module_context(tags, tracer_span)
63
+
64
+ build_test_module(tracer_span, tags)
65
+ end
66
+ end
67
+
68
+ def start_test_suite(test_suite_name, service: nil, tags: {})
69
+ return skip_tracing unless test_suite_level_visibility_enabled
70
+
71
+ @global_context.fetch_or_activate_test_suite(test_suite_name) do
72
+ set_inherited_globals(tags)
73
+ set_session_context(tags)
74
+ set_module_context(tags)
75
+
76
+ tracer_span = start_datadog_tracer_span(
77
+ test_suite_name, build_span_options(service, Ext::AppTypes::TYPE_TEST_SUITE)
78
+ )
79
+ set_suite_context(tags, span: tracer_span)
80
+
81
+ build_test_suite(tracer_span, tags)
82
+ end
83
+ end
84
+
85
+ def trace_test(test_name, test_suite_name, service: nil, tags: {}, &block)
86
+ return skip_tracing(block) unless enabled
87
+
88
+ set_inherited_globals(tags)
89
+ set_session_context(tags)
90
+ set_module_context(tags)
91
+ set_suite_context(tags, name: test_suite_name)
92
+
93
+ tags[Ext::Test::TAG_NAME] = test_name
94
+
95
+ span_options = build_span_options(
96
+ service,
97
+ Ext::AppTypes::TYPE_TEST,
98
+ # :resource is needed for the agent APM protocol to work correctly (for older agent versions)
99
+ # :continue_from is required to start a new trace for each test
100
+ {resource: test_name, continue_from: Datadog::Tracing::TraceDigest.new}
101
+ )
102
+
103
+ if block
104
+ start_datadog_tracer_span(test_name, span_options) do |tracer_span|
105
+ test = build_test(tracer_span, tags)
106
+
107
+ @local_context.activate_test!(test) do
108
+ block.call(test)
109
+ end
110
+ end
111
+ else
112
+ tracer_span = start_datadog_tracer_span(test_name, span_options)
113
+
114
+ test = build_test(tracer_span, tags)
115
+ @local_context.activate_test!(test)
116
+ test
117
+ end
118
+ end
119
+
120
+ def trace(span_type, span_name, tags: {}, &block)
121
+ return skip_tracing(block) unless enabled
122
+
123
+ span_options = build_span_options(
124
+ nil, # service name is completely optional for custom spans
125
+ span_type,
126
+ {resource: span_name}
127
+ )
128
+
129
+ if block
130
+ start_datadog_tracer_span(span_name, span_options) do |tracer_span|
131
+ block.call(build_span(tracer_span, tags))
132
+ end
133
+ else
134
+ tracer_span = start_datadog_tracer_span(span_name, span_options)
135
+
136
+ build_span(tracer_span, tags)
137
+ end
138
+ end
139
+
140
+ def active_span
141
+ tracer_span = Datadog::Tracing.active_span
142
+ Span.new(tracer_span) if tracer_span
143
+ end
144
+
145
+ def active_test
146
+ @local_context.active_test
147
+ end
148
+
149
+ def active_test_session
150
+ @global_context.active_test_session
151
+ end
152
+
153
+ def active_test_module
154
+ @global_context.active_test_module
155
+ end
156
+
157
+ def active_test_suite(test_suite_name)
158
+ @global_context.active_test_suite(test_suite_name)
159
+ end
160
+
161
+ def deactivate_test(test)
162
+ @local_context.deactivate_test!(test)
163
+ end
164
+
165
+ def deactivate_test_session
166
+ @global_context.deactivate_test_session!
167
+ end
168
+
169
+ def deactivate_test_module
170
+ @global_context.deactivate_test_module!
171
+ end
172
+
173
+ def deactivate_test_suite(test_suite_name)
174
+ @global_context.deactivate_test_suite!(test_suite_name)
175
+ end
176
+
177
+ private
178
+
179
+ def skip_tracing(block = nil)
180
+ if block
181
+ block.call(null_span)
182
+ else
183
+ null_span
184
+ end
185
+ end
186
+
187
+ # Sets trace's origin to ciapp-test
188
+ def set_trace_origin(trace)
189
+ trace.origin = Ext::Test::CONTEXT_ORIGIN if trace
190
+ end
191
+
192
+ def build_test_session(tracer_span, tags)
193
+ test_session = TestSession.new(tracer_span)
194
+ set_initial_tags(test_session, tags)
195
+ test_session
196
+ end
197
+
198
+ def build_test_module(tracer_span, tags)
199
+ test_module = TestModule.new(tracer_span)
200
+ set_initial_tags(test_module, tags)
201
+ test_module
202
+ end
203
+
204
+ def build_test_suite(tracer_span, tags)
205
+ test_suite = TestSuite.new(tracer_span)
206
+ set_initial_tags(test_suite, tags)
207
+ test_suite
208
+ end
209
+
210
+ def build_test(tracer_span, tags)
211
+ test = Test.new(tracer_span)
212
+ set_initial_tags(test, tags)
213
+ test
214
+ end
215
+
216
+ def build_span(tracer_span, tags)
217
+ span = Span.new(tracer_span)
218
+ set_initial_tags(span, tags)
219
+ span
220
+ end
221
+
222
+ def build_span_options(service, span_type, other_options = {})
223
+ other_options[:service] = service || @global_context.service
224
+ other_options[:span_type] = span_type
225
+
226
+ other_options
227
+ end
228
+
229
+ def set_inherited_globals(tags)
230
+ # this code achieves the same as @global_context.inheritable_session_tags.merge(tags)
231
+ # but without allocating a new hash
232
+ @global_context.inheritable_session_tags.each do |key, value|
233
+ tags[key] = value unless tags.key?(key)
234
+ end
235
+ end
236
+
237
+ def set_initial_tags(ci_span, tags)
238
+ ci_span.set_default_tags
239
+ ci_span.set_environment_runtime_tags
240
+
241
+ ci_span.set_tags(tags)
242
+ ci_span.set_tags(environment_tags)
243
+ end
244
+
245
+ def set_session_context(tags, test_session = nil)
246
+ test_session ||= active_test_session
247
+ tags[Ext::Test::TAG_TEST_SESSION_ID] = test_session.id.to_s if test_session
248
+ end
249
+
250
+ def set_module_context(tags, test_module = nil)
251
+ test_module ||= active_test_module
252
+ if test_module
253
+ tags[Ext::Test::TAG_TEST_MODULE_ID] = test_module.id.to_s
254
+ tags[Ext::Test::TAG_MODULE] = test_module.name
255
+ end
256
+ end
257
+
258
+ def set_suite_context(tags, span: nil, name: nil)
259
+ return if span.nil? && name.nil?
260
+
261
+ test_suite = span || active_test_suite(name)
262
+
263
+ if test_suite
264
+ tags[Ext::Test::TAG_TEST_SUITE_ID] = test_suite.id.to_s
265
+ tags[Ext::Test::TAG_SUITE] = test_suite.name
266
+ else
267
+ tags[Ext::Test::TAG_SUITE] = name
268
+ end
269
+ end
270
+
271
+ def start_datadog_tracer_span(span_name, span_options, &block)
272
+ if block
273
+ Datadog::Tracing.trace(span_name, **span_options) do |tracer_span, trace|
274
+ set_trace_origin(trace)
275
+
276
+ yield tracer_span
277
+ end
278
+ else
279
+ tracer_span = Datadog::Tracing.trace(span_name, **span_options)
280
+ trace = Datadog::Tracing.active_trace
281
+ set_trace_origin(trace)
282
+
283
+ tracer_span
284
+ end
285
+ end
286
+
287
+ def null_span
288
+ @null_span ||= NullSpan.new
289
+ end
290
+ end
291
+ end
292
+ end
293
+ end
@@ -5,7 +5,7 @@ module Datadog
5
5
  module VERSION
6
6
  MAJOR = "0"
7
7
  MINOR = "5"
8
- PATCH = "0"
8
+ PATCH = "1"
9
9
  PRE = nil
10
10
  BUILD = nil
11
11
  # PRE and BUILD above are modified for dev gems during gem build GHA workflow
data/lib/datadog/ci.rb CHANGED
@@ -389,6 +389,6 @@ require_relative "ci/contrib/cucumber/integration"
389
389
  require_relative "ci/contrib/rspec/integration"
390
390
  require_relative "ci/contrib/minitest/integration"
391
391
 
392
- # Extensions
393
- require_relative "ci/extensions"
394
- Datadog::CI::Extensions.activate!
392
+ # Configuration extensions
393
+ require_relative "ci/configuration/extensions"
394
+ Datadog::CI::Configuration::Extensions.activate!
@@ -2,9 +2,9 @@ module Datadog
2
2
  module CI
3
3
  module Configuration
4
4
  module Components : Datadog::Core::Configuration::Components
5
- @ci_recorder: Datadog::CI::Recorder
5
+ @ci_recorder: Datadog::CI::TestVisibility::Recorder
6
6
 
7
- attr_reader ci_recorder: Datadog::CI::Recorder
7
+ attr_reader ci_recorder: Datadog::CI::TestVisibility::Recorder
8
8
 
9
9
  def initialize: (untyped settings) -> void
10
10
 
@@ -0,0 +1,9 @@
1
+ module Datadog
2
+ module CI
3
+ module Configuration
4
+ module Extensions
5
+ def self.activate!: () -> untyped
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,39 @@
1
+ module Datadog
2
+ module CI
3
+ module TestVisibility
4
+ module Context
5
+ class Global
6
+ @mutex: Thread::Mutex
7
+
8
+ @test_session: Datadog::CI::TestSession?
9
+ @test_module: Datadog::CI::TestModule?
10
+ @test_suites: Hash[String, Datadog::CI::TestSuite]
11
+
12
+ def initialize: () -> void
13
+
14
+ def fetch_or_activate_test_suite: (String test_suite_name) {() -> Datadog::CI::TestSuite} -> Datadog::CI::TestSuite
15
+
16
+ def fetch_or_activate_test_module: () {() -> Datadog::CI::TestModule} -> Datadog::CI::TestModule
17
+
18
+ def fetch_or_activate_test_session: () {() -> Datadog::CI::TestSession} -> Datadog::CI::TestSession
19
+
20
+ def active_test_session: () -> Datadog::CI::TestSession?
21
+
22
+ def active_test_suite: (String test_suite_name) -> Datadog::CI::TestSuite?
23
+
24
+ def service: () -> String?
25
+
26
+ def inheritable_session_tags: () -> Hash[untyped, untyped]
27
+
28
+ def active_test_module: () -> Datadog::CI::TestModule?
29
+
30
+ def deactivate_test_session!: () -> void
31
+
32
+ def deactivate_test_module!: () -> void
33
+
34
+ def deactivate_test_suite!: (String test_suite_name) -> void
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,23 @@
1
+ module Datadog
2
+ module CI
3
+ module TestVisibility
4
+ module Context
5
+ class Local
6
+ @key: Symbol
7
+
8
+ def initialize: () -> void
9
+
10
+ def activate_test!: (Datadog::CI::Test test) ?{ () -> untyped } -> void
11
+
12
+ def deactivate_test!: (Datadog::CI::Test test) -> void
13
+
14
+ def active_test: () -> Datadog::CI::Test?
15
+
16
+ private
17
+
18
+ def active_test=: (Datadog::CI::Test? test) -> untyped
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end