datadog-ci 1.8.1 → 1.9.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 (46) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -2
  3. data/README.md +6 -8
  4. data/exe/ddcirb +3 -1
  5. data/lib/datadog/ci/auto_instrument.rb +8 -0
  6. data/lib/datadog/ci/cli/cli.rb +5 -1
  7. data/lib/datadog/ci/cli/command/exec.rb +29 -0
  8. data/lib/datadog/ci/configuration/settings.rb +3 -21
  9. data/lib/datadog/ci/contrib/ciqueue/integration.rb +34 -0
  10. data/lib/datadog/ci/contrib/ciqueue/patcher.rb +23 -0
  11. data/lib/datadog/ci/contrib/cucumber/formatter.rb +10 -5
  12. data/lib/datadog/ci/contrib/cucumber/integration.rb +5 -14
  13. data/lib/datadog/ci/contrib/cucumber/patcher.rb +2 -6
  14. data/lib/datadog/ci/contrib/instrumentation.rb +173 -0
  15. data/lib/datadog/ci/contrib/integration.rb +101 -117
  16. data/lib/datadog/ci/contrib/knapsack/extension.rb +27 -0
  17. data/lib/datadog/ci/contrib/knapsack/integration.rb +36 -0
  18. data/lib/datadog/ci/contrib/knapsack/patcher.rb +29 -0
  19. data/lib/datadog/ci/contrib/knapsack/runner.rb +66 -0
  20. data/lib/datadog/ci/contrib/minitest/integration.rb +6 -14
  21. data/lib/datadog/ci/contrib/minitest/patcher.rb +1 -5
  22. data/lib/datadog/ci/contrib/minitest/runner.rb +6 -1
  23. data/lib/datadog/ci/contrib/minitest/test.rb +6 -1
  24. data/lib/datadog/ci/contrib/patcher.rb +62 -0
  25. data/lib/datadog/ci/contrib/rspec/example.rb +6 -1
  26. data/lib/datadog/ci/contrib/rspec/integration.rb +10 -13
  27. data/lib/datadog/ci/contrib/rspec/patcher.rb +2 -33
  28. data/lib/datadog/ci/contrib/rspec/runner.rb +6 -1
  29. data/lib/datadog/ci/contrib/selenium/capybara_driver.rb +1 -1
  30. data/lib/datadog/ci/contrib/selenium/driver.rb +1 -1
  31. data/lib/datadog/ci/contrib/selenium/integration.rb +6 -10
  32. data/lib/datadog/ci/contrib/selenium/navigation.rb +6 -2
  33. data/lib/datadog/ci/contrib/selenium/patcher.rb +2 -6
  34. data/lib/datadog/ci/contrib/selenium/rum.rb +0 -2
  35. data/lib/datadog/ci/contrib/simplecov/integration.rb +6 -10
  36. data/lib/datadog/ci/contrib/simplecov/patcher.rb +2 -6
  37. data/lib/datadog/ci/test_retries/strategy/retry_new.rb +1 -1
  38. data/lib/datadog/ci/test_visibility/component.rb +2 -2
  39. data/lib/datadog/ci/test_visibility/telemetry.rb +2 -1
  40. data/lib/datadog/ci/version.rb +2 -2
  41. data/lib/datadog/ci.rb +9 -1
  42. metadata +13 -7
  43. data/lib/datadog/ci/contrib/contrib.rb +0 -31
  44. data/lib/datadog/ci/contrib/rspec/knapsack_pro/extension.rb +0 -29
  45. data/lib/datadog/ci/contrib/rspec/knapsack_pro/patcher.rb +0 -26
  46. data/lib/datadog/ci/contrib/rspec/knapsack_pro/runner.rb +0 -62
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b5ee5645ee9ec6c07af9660a2742402d617965668a00e48764839d6b7ca8470e
4
- data.tar.gz: 878ee49cc336a2d8d37e718c09a6f17b949cc149c8f6a57162410ff2da9ccfea
3
+ metadata.gz: 7641cbd9b01a787badb9bab7d3aa74933c99b750b46609c9a625ed3f7daaa11c
4
+ data.tar.gz: 5489c728c4b6c91ff68b515796533801b02dfb623f506da23849e60e343063a3
5
5
  SHA512:
6
- metadata.gz: 4fc31d4106750af2d5cc556164459e24c6782e7ade09abf57b0a3a48320552aa74f9b42488a40df256b0562f304883249b86c44b16eef59cce00af4c15f00aa9
7
- data.tar.gz: fa12a596824ad2963bcd394c8a634db54300e4fab62f482c17bad0210a7a6b159a48355a070dd65a413412225ab5f570d125c547390d46f946a4fd6389244222
6
+ metadata.gz: 3d2f41e5eb8f58c3eecb72200658add1799109c7778fad032c6d58ec2fd462a37df7e1a47edfb6cf7390e037c4e30ada85dc95c71983c30629de38b2bf62fd35
7
+ data.tar.gz: 408151cc04150bcc8ec2a0933c0f6d38cd1362b5842e8b145960ba276431681893988cfd0c49ce47f519ae9e7451549b45503a6ea79f123cfd4cc3750cb2a8ea
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.9.0] - 2024-11-26
4
+
5
+ ### Added
6
+
7
+ * Auto instrumentation ([#259][])
8
+
3
9
  ## [1.8.1] - 2024-10-18
4
10
 
5
11
 
@@ -356,7 +362,8 @@ Currently test suite level visibility is not used by our instrumentation: it wil
356
362
 
357
363
  - Ruby versions < 2.7 no longer supported ([#8][])
358
364
 
359
- [Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.8.1...main
365
+ [Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.9.0...main
366
+ [1.9.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.8.1...v1.9.0
360
367
  [1.8.1]: https://github.com/DataDog/datadog-ci-rb/compare/v1.8.0...v1.8.1
361
368
  [1.8.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.7.0...v1.8.0
362
369
  [1.7.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.6.0...v1.7.0
@@ -513,4 +520,5 @@ Currently test suite level visibility is not used by our instrumentation: it wil
513
520
  [#243]: https://github.com/DataDog/datadog-ci-rb/issues/243
514
521
  [#244]: https://github.com/DataDog/datadog-ci-rb/issues/244
515
522
  [#248]: https://github.com/DataDog/datadog-ci-rb/issues/248
516
- [#250]: https://github.com/DataDog/datadog-ci-rb/issues/250
523
+ [#250]: https://github.com/DataDog/datadog-ci-rb/issues/250
524
+ [#259]: https://github.com/DataDog/datadog-ci-rb/issues/259
data/README.md CHANGED
@@ -1,9 +1,7 @@
1
- # Datadog Test Visibility for Ruby
1
+ # Datadog Test Optimization for Ruby
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/datadog-ci.svg)](https://badge.fury.io/rb/datadog-ci)
4
4
  [![YARD documentation](https://img.shields.io/badge/YARD-documentation-blue)](https://datadoghq.dev/datadog-ci-rb/)
5
- [![codecov](https://codecov.io/gh/DataDog/datadog-ci-rb/branch/main/graph/badge.svg)](https://app.codecov.io/gh/DataDog/datadog-ci-rb/branch/main)
6
- [![CircleCI](https://dl.circleci.com/status-badge/img/gh/DataDog/datadog-ci-rb/tree/main.svg?style=svg)](https://dl.circleci.com/status-badge/redirect/gh/DataDog/datadog-ci-rb/tree/main)
7
5
 
8
6
  Datadog's Ruby Library for instrumenting your tests.
9
7
  Learn more on our [official website](https://docs.datadoghq.com/tests/) and check out our [documentation for this library](https://docs.datadoghq.com/tests/setup/ruby/?tab=cloudciprovideragentless).
@@ -11,12 +9,12 @@ Learn more on our [official website](https://docs.datadoghq.com/tests/) and chec
11
9
  ## Features
12
10
 
13
11
  - [Test Visibility](https://docs.datadoghq.com/tests/) - collect metrics and results for your tests
14
- - [Intelligent test runner](https://docs.datadoghq.com/intelligent_test_runner/) - save time by selectively running only tests affected by code changes
15
- - [Auto test retries](https://docs.datadoghq.com/tests/auto_test_retries/?tab=ruby) - retrying failing tests up to N times to avoid failing your build due to flaky tests
16
- - [Early flake detection](https://docs.datadoghq.com/tests/early_flake_detection?tab=ruby) - Datadog’s test flakiness solution that identifies flakes early by running newly added tests multiple times
12
+ - [Test impact analysis](https://docs.datadoghq.com/tests/test_impact_analysis/) - save time by selectively running only tests affected by code changes
13
+ - [Flaky test management](https://docs.datadoghq.com/tests/flaky_test_management/) - track, alert, search your flaky tests in Datadog UI
14
+ - [Auto test retries](https://docs.datadoghq.com/tests/flaky_test_management/auto_test_retries/?tab=ruby) - retrying failing tests up to N times to avoid failing your build due to flaky tests
15
+ - [Early flake detection](https://docs.datadoghq.com/tests/flaky_test_management/early_flake_detection/?tab=ruby) - Datadog’s test flakiness solution that identifies flakes early by running newly added tests multiple times
17
16
  - [Search and manage CI tests](https://docs.datadoghq.com/tests/search/)
18
17
  - [Enhance developer workflows](https://docs.datadoghq.com/tests/developer_workflows)
19
- - [Flaky test management](https://docs.datadoghq.com/tests/guides/flaky_test_management/)
20
18
  - [Add custom measures to your tests](https://docs.datadoghq.com/tests/guides/add_custom_measures/?tab=ruby)
21
19
  - [Browser tests integration with Datadog RUM](https://docs.datadoghq.com/tests/browser_tests)
22
20
 
@@ -37,7 +35,7 @@ If you used [test visibility for Ruby](https://docs.datadoghq.com/tests/setup/ru
37
35
  ## Setup
38
36
 
39
37
  - [Test visibility setup](https://docs.datadoghq.com/tests/setup/ruby/?tab=cloudciprovideragentless)
40
- - [Intelligent test runner setup](https://docs.datadoghq.com/intelligent_test_runner/setup/ruby) (test visibility setup is required before setting up intelligent test runner)
38
+ - [Test impact analysis setup](https://docs.datadoghq.com/tests/test_impact_analysis/setup/ruby/?tab=cloudciprovideragentless) (test visibility setup is required before setting up test impact analysis)
41
39
 
42
40
  ## Contributing
43
41
 
data/exe/ddcirb CHANGED
@@ -2,4 +2,6 @@
2
2
 
3
3
  require "datadog/ci/cli/cli"
4
4
 
5
- Datadog::CI::CLI.exec(ARGV.first)
5
+ command = ARGV.shift
6
+
7
+ Datadog::CI::CLI.exec(command, ARGV)
@@ -0,0 +1,8 @@
1
+ require "datadog/ci"
2
+
3
+ if RUBY_ENGINE == "jruby"
4
+ Datadog.logger.error("Auto instrumentation is not supported on JRuby. Please use manual instrumentation instead.")
5
+ return
6
+ end
7
+
8
+ Datadog::CI::Contrib::Instrumentation.auto_instrument
@@ -1,14 +1,17 @@
1
1
  require "datadog"
2
2
  require "datadog/ci"
3
3
 
4
+ require_relative "command/exec"
4
5
  require_relative "command/skippable_tests_percentage"
5
6
  require_relative "command/skippable_tests_percentage_estimate"
6
7
 
7
8
  module Datadog
8
9
  module CI
9
10
  module CLI
10
- def self.exec(action)
11
+ def self.exec(action, args = [])
11
12
  case action
13
+ when "exec"
14
+ Command::Exec.new(args).exec
12
15
  when "skipped-tests", "skippable-tests"
13
16
  Command::SkippableTestsPercentage.new.exec
14
17
  when "skipped-tests-estimate", "skippable-tests-estimate"
@@ -17,6 +20,7 @@ module Datadog
17
20
  puts("Usage: bundle exec ddcirb [command] [options]. Available commands:")
18
21
  puts(" skippable-tests - calculates the exact percentage of skipped tests and prints it to stdout or file")
19
22
  puts(" skippable-tests-estimate - estimates the percentage of skipped tests and prints it to stdout or file")
23
+ puts(" exec YOUR_TEST_COMMAND - automatically instruments your test command with Datadog and executes it")
20
24
  end
21
25
  end
22
26
  end
@@ -0,0 +1,29 @@
1
+ require_relative "base"
2
+ require_relative "../../test_optimisation/skippable_percentage/estimator"
3
+
4
+ module Datadog
5
+ module CI
6
+ module CLI
7
+ module Command
8
+ class Exec < Base
9
+ def initialize(args)
10
+ super()
11
+
12
+ @args = args
13
+ end
14
+
15
+ def exec
16
+ rubyopts = [
17
+ "-rdatadog/ci/auto_instrument"
18
+ ]
19
+
20
+ existing_rubyopt = ENV["RUBYOPT"]
21
+ ENV["RUBYOPT"] = existing_rubyopt ? "#{existing_rubyopt} #{rubyopts.join(" ")}" : rubyopts.join(" ")
22
+
23
+ Kernel.exec(*@args)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../contrib/instrumentation"
3
4
  require_relative "../ext/settings"
4
5
  require_relative "../utils/bundle"
5
6
 
@@ -8,8 +9,6 @@ module Datadog
8
9
  module Configuration
9
10
  # Adds CI behavior to ddtrace settings
10
11
  module Settings
11
- InvalidIntegrationError = Class.new(StandardError)
12
-
13
12
  def self.extended(base)
14
13
  base = base.singleton_class unless base.is_a?(Class)
15
14
  add_settings!(base)
@@ -126,23 +125,11 @@ module Datadog
126
125
  define_method(:instrument) do |integration_name, options = {}, &block|
127
126
  return unless enabled
128
127
 
129
- integration = fetch_integration(integration_name)
130
- integration.configure(options, &block)
131
-
132
- return unless integration.enabled
133
-
134
- patch_results = integration.patch
135
- next if patch_results == true
136
-
137
- error_message = <<-ERROR
138
- Available?: #{patch_results[:available]}, Loaded?: #{patch_results[:loaded]},
139
- Compatible?: #{patch_results[:compatible]}, Patchable?: #{patch_results[:patchable]}"
140
- ERROR
141
- Datadog.logger.warn("Unable to patch #{integration_name} (#{error_message})")
128
+ Contrib::Instrumentation.instrument(integration_name, options, &block)
142
129
  end
143
130
 
144
131
  define_method(:[]) do |integration_name|
145
- fetch_integration(integration_name).configuration
132
+ Contrib::Instrumentation.fetch_integration(integration_name).configuration
146
133
  end
147
134
 
148
135
  option :trace_flush
@@ -151,11 +138,6 @@ module Datadog
151
138
  o.type :hash
152
139
  o.default({})
153
140
  end
154
-
155
- define_method(:fetch_integration) do |name|
156
- Datadog::CI::Contrib::Integration.registry[name] ||
157
- raise(InvalidIntegrationError, "'#{name}' is not a valid integration.")
158
- end
159
141
  end
160
142
  end
161
143
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../integration"
4
+ require_relative "patcher"
5
+
6
+ module Datadog
7
+ module CI
8
+ module Contrib
9
+ module Ciqueue
10
+ # ci-queue test runner instrumentation
11
+ # https://github.com/Shopify/ci-queue
12
+ class Integration < Contrib::Integration
13
+ MINIMUM_VERSION = Gem::Version.new("0.9.0")
14
+
15
+ def version
16
+ Gem.loaded_specs["ci-queue"]&.version
17
+ end
18
+
19
+ def loaded?
20
+ !defined?(::RSpec::Queue::Runner).nil?
21
+ end
22
+
23
+ def compatible?
24
+ super && version >= MINIMUM_VERSION
25
+ end
26
+
27
+ def patcher
28
+ Patcher
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../patcher"
4
+ require_relative "../rspec/runner"
5
+
6
+ module Datadog
7
+ module CI
8
+ module Contrib
9
+ module Ciqueue
10
+ # Patcher enables patching of 'rspec' module.
11
+ module Patcher
12
+ include Datadog::CI::Contrib::Patcher
13
+
14
+ module_function
15
+
16
+ def patch
17
+ ::RSpec::Queue::Runner.include(Contrib::RSpec::Runner)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -3,6 +3,7 @@
3
3
  require_relative "../../ext/test"
4
4
  require_relative "../../git/local_repository"
5
5
  require_relative "../../utils/test_run"
6
+ require_relative "../instrumentation"
6
7
  require_relative "ext"
7
8
 
8
9
  module Datadog
@@ -38,9 +39,9 @@ module Datadog
38
39
  test_visibility_component.start_test_session(
39
40
  tags: {
40
41
  CI::Ext::Test::TAG_FRAMEWORK => Ext::FRAMEWORK,
41
- CI::Ext::Test::TAG_FRAMEWORK_VERSION => CI::Contrib::Cucumber::Integration.version.to_s
42
+ CI::Ext::Test::TAG_FRAMEWORK_VERSION => datadog_integration.version.to_s
42
43
  },
43
- service: configuration[:service_name]
44
+ service: datadog_configuration[:service_name]
44
45
  )
45
46
  test_visibility_component.start_test_module(Ext::FRAMEWORK)
46
47
  end
@@ -61,7 +62,7 @@ module Datadog
61
62
  # @type var tags: Hash[String, String]
62
63
  tags = {
63
64
  CI::Ext::Test::TAG_FRAMEWORK => Ext::FRAMEWORK,
64
- CI::Ext::Test::TAG_FRAMEWORK_VERSION => CI::Contrib::Cucumber::Integration.version.to_s,
65
+ CI::Ext::Test::TAG_FRAMEWORK_VERSION => datadog_integration.version.to_s,
65
66
  CI::Ext::Test::TAG_SOURCE_FILE => Git::LocalRepository.relative_to_root(event.test_case.location.file),
66
67
  CI::Ext::Test::TAG_SOURCE_START => event.test_case.location.line.to_s
67
68
  }
@@ -81,7 +82,7 @@ module Datadog
81
82
  event.test_case.name,
82
83
  test_suite_name,
83
84
  tags: tags,
84
- service: configuration[:service_name]
85
+ service: datadog_configuration[:service_name]
85
86
  )
86
87
  if event.test_case.match_tags?("@#{CI::Ext::Test::ITR_UNSKIPPABLE_OPTION}")
87
88
  test_span&.itr_unskippable!
@@ -199,7 +200,11 @@ module Datadog
199
200
  end
200
201
  end
201
202
 
202
- def configuration
203
+ def datadog_integration
204
+ CI::Contrib::Instrumentation.fetch_integration(:cucumber)
205
+ end
206
+
207
+ def datadog_configuration
203
208
  Datadog.configuration.ci[:cucumber]
204
209
  end
205
210
 
@@ -9,30 +9,21 @@ module Datadog
9
9
  module Contrib
10
10
  module Cucumber
11
11
  # Description of Cucumber integration
12
- class Integration
13
- include Datadog::CI::Contrib::Integration
14
-
12
+ class Integration < Contrib::Integration
15
13
  MINIMUM_VERSION = Gem::Version.new("3.0.0")
16
14
 
17
- register_as :cucumber
18
-
19
- def self.version
15
+ def version
20
16
  Gem.loaded_specs["cucumber"]&.version
21
17
  end
22
18
 
23
- def self.loaded?
24
- !defined?(::Cucumber).nil? && !defined?(::Cucumber::Runtime).nil?
19
+ def loaded?
20
+ !defined?(::Cucumber).nil? && !defined?(::Cucumber::Runtime).nil? && !defined?(::Cucumber::Configuration).nil?
25
21
  end
26
22
 
27
- def self.compatible?
23
+ def compatible?
28
24
  super && version >= MINIMUM_VERSION
29
25
  end
30
26
 
31
- # test environments should not auto instrument test libraries
32
- def auto_instrument?
33
- false
34
- end
35
-
36
27
  def new_configuration
37
28
  Configuration::Settings.new
38
29
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "datadog/tracing/contrib/patcher"
3
+ require_relative "../patcher"
4
4
 
5
5
  require_relative "instrumentation"
6
6
 
@@ -10,14 +10,10 @@ module Datadog
10
10
  module Cucumber
11
11
  # Patches 'cucumber' gem.
12
12
  module Patcher
13
- include Datadog::Tracing::Contrib::Patcher
13
+ include Datadog::CI::Contrib::Patcher
14
14
 
15
15
  module_function
16
16
 
17
- def target_version
18
- Integration.version
19
- end
20
-
21
17
  def patch
22
18
  ::Cucumber::Runtime.include(Instrumentation)
23
19
  end
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "datadog/core/utils/only_once"
4
+
5
+ module Datadog
6
+ module CI
7
+ module Contrib
8
+ module Instrumentation
9
+ class InvalidIntegrationError < StandardError; end
10
+
11
+ @registry = {}
12
+ @auto_instrumented = false
13
+
14
+ def self.registry
15
+ @registry
16
+ end
17
+
18
+ def self.register_integration(integration_class)
19
+ @registry[integration_name(integration_class)] = integration_class.new
20
+ end
21
+
22
+ def self.auto_instrumented?
23
+ @auto_instrumented
24
+ end
25
+
26
+ # Auto instrumentation of all integrations.
27
+ #
28
+ # Registers a :script_compiled tracepoint to watch for new Ruby files being loaded.
29
+ # On every file load it checks if any of the integrations are patchable now.
30
+ # Only the integrations that are available in the environment are checked.
31
+ def self.auto_instrument
32
+ Datadog.logger.debug("Auto instrumenting all integrations...")
33
+ @auto_instrumented = true
34
+
35
+ auto_instrumented_integrations = fetch_auto_instrumented_integrations
36
+ if auto_instrumented_integrations.empty?
37
+ Datadog.logger.warn(
38
+ "Auto instrumentation was requested, but no available integrations were found. " \
39
+ "Tests will be run without Datadog instrumentation."
40
+ )
41
+ return
42
+ end
43
+
44
+ # note that `Kernel.require` might be called from a different thread, so
45
+ # there is a possibility of concurrent execution of this tracepoint
46
+ mutex = Mutex.new
47
+ script_compiled_tracepoint = TracePoint.new(:script_compiled) do |tp|
48
+ all_patched = true
49
+
50
+ mutex.synchronize do
51
+ auto_instrumented_integrations.each do |integration|
52
+ next if integration.patched?
53
+
54
+ all_patched = false
55
+ next unless integration.loaded?
56
+
57
+ auto_configure_datadog
58
+
59
+ Datadog.logger.debug("#{integration.class} is loaded")
60
+ patch_integration(integration)
61
+ end
62
+
63
+ if all_patched
64
+ Datadog.logger.debug("All expected integrations are patched, disabling the script_compiled tracepoint")
65
+
66
+ tp.disable
67
+ end
68
+ end
69
+ end
70
+ script_compiled_tracepoint.enable
71
+ end
72
+
73
+ # Manual instrumentation of a specific integration.
74
+ #
75
+ # This method is called when user has `c.ci.instrument :integration_name` in their code.
76
+ def self.instrument(integration_name, options = {}, &block)
77
+ integration = fetch_integration(integration_name)
78
+ # when manually instrumented, it might be configured via code
79
+ integration.configure(options, &block)
80
+
81
+ return unless integration.enabled
82
+
83
+ patch_integration(integration, with_dependencies: true)
84
+ end
85
+
86
+ # This method instruments all additional test libraries (ex: selenium-webdriver) that need to be instrumented
87
+ # later in the test suite run.
88
+ #
89
+ # It is intended to be called when test session starts to add additional capabilities to test visibility.
90
+ #
91
+ # This method does not automatically instrument test frameworks (ex: RSpec, Cucumber, etc), it requires
92
+ # test framework to be already instrumented.
93
+ def self.instrument_on_session_start
94
+ Datadog.logger.debug("Instrumenting all late instrumented integrations...")
95
+
96
+ @registry.each do |name, integration|
97
+ next unless integration.late_instrument?
98
+ next unless integration.enabled
99
+
100
+ Datadog.logger.debug "#{name} is allowed to be late instrumented"
101
+
102
+ patch_integration(integration)
103
+ end
104
+ end
105
+
106
+ def self.fetch_integration(name)
107
+ @registry[name] ||
108
+ raise(InvalidIntegrationError, "'#{name}' is not a valid integration.")
109
+ end
110
+
111
+ # take the parent module name and downcase it
112
+ # for example for Datadog::CI::Contrib::RSpec::Integration it will be :rspec
113
+ def self.integration_name(subclass)
114
+ result = subclass.name&.split("::")&.[](-2)&.downcase&.to_sym
115
+ raise "Integration name could not be derived for #{subclass}" if result.nil?
116
+ result
117
+ end
118
+
119
+ def self.patch_integration(integration, with_dependencies: false)
120
+ patch_results = integration.patch
121
+
122
+ if patch_results[:ok]
123
+ Datadog.logger.debug("#{integration.class} is patched")
124
+
125
+ return unless with_dependencies
126
+
127
+ # try to patch dependant integrations (for example knapsack that depends on rspec)
128
+ dependants = integration.dependants
129
+ .map { |name| fetch_integration(name) }
130
+ .filter { |integration| integration.patchable? }
131
+
132
+ Datadog.logger.debug("Found dependent integrations for #{integration.class}: #{dependants}")
133
+
134
+ dependants.each do |dependent_integration|
135
+ patch_integration(dependent_integration, with_dependencies: true)
136
+ end
137
+
138
+ else
139
+ Datadog.logger.debug("Attention: #{integration.class} is not patched (#{patch_results})")
140
+ end
141
+ end
142
+
143
+ def self.fetch_auto_instrumented_integrations
144
+ @registry.filter_map do |name, integration|
145
+ # ignore integrations that are not in the Gemfile or have incompatible versions
146
+ next unless integration.compatible?
147
+
148
+ # late instrumented integrations will be patched when the test session starts
149
+ next if integration.late_instrument?
150
+
151
+ Datadog.logger.debug("#{name} should be auto instrumented")
152
+ integration
153
+ end
154
+ end
155
+
156
+ def self.auto_configure_datadog
157
+ configure_once.run do
158
+ Datadog.logger.debug("Applying Datadog configuration in CI mode...")
159
+ Datadog.configure do |c|
160
+ c.ci.enabled = true
161
+ c.tracing.enabled = true
162
+ end
163
+ end
164
+ end
165
+
166
+ # This is not thread safe, it is synchronized by the caller in the tracepoint
167
+ def self.configure_once
168
+ @configure_once ||= Datadog::Core::Utils::OnlyOnce.new
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end