diff --git a/bin/console b/bin/console deleted file mode 100755 index f29b0521..00000000 --- a/bin/console +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env ruby -# frozen_string_literal: true - -require "bundler/setup" -require "datadog/ci" - -# You can add fixtures and/or initialization code here to make experimenting -# with your gem easier. You can also use a different console, if you like. - -# (If you use this, don't forget to add pry to your Gemfile!) -# require "pry" -# Pry.start - -require "irb" -IRB.start(__FILE__) diff --git a/bin/ddcirb b/bin/ddcirb new file mode 100755 index 00000000..b2eab361 --- /dev/null +++ b/bin/ddcirb @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby + +rubyopts = [ + "-rdatadog/ci/auto_instrument" +] + +existing_rubyopt = ENV["RUBYOPT"] + +ENV["RUBYOPT"] = existing_rubyopt ? "#{existing_rubyopt} #{rubyopts.join(" ")}" : rubyopts.join(" ") + +::Kernel.exec(*ARGV) diff --git a/bin/setup b/bin/setup deleted file mode 100755 index dce67d86..00000000 --- a/bin/setup +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail -IFS=$'\n\t' -set -vx - -bundle install - -# Do any other automated setup that you need to do here diff --git a/datadog-ci.gemspec b/datadog-ci.gemspec index bdaa2d95..71712c51 100644 --- a/datadog-ci.gemspec +++ b/datadog-ci.gemspec @@ -22,6 +22,7 @@ Gem::Specification.new do |spec| spec.homepage = "https://github.com/DataDog/datadog-ci-rb" spec.license = "BSD-3-Clause" + spec.executables = ["ddcirb"] spec.metadata["allowed_push_host"] = "https://rubygems.org" spec.metadata["changelog_uri"] = "https://github.com/DataDog/datadog-ci-rb/blob/main/CHANGELOG.md" @@ -36,6 +37,7 @@ Gem::Specification.new do |spec| README.md ext/**/* lib/**/* + bin/**/* ]].select { |fn| File.file?(fn) } # We don't want directories, only files .reject { |fn| fn.end_with?(".so", ".bundle") } # Exclude local native binary artifacts diff --git a/lib/datadog/ci.rb b/lib/datadog/ci.rb index a5efc9f8..60152014 100644 --- a/lib/datadog/ci.rb +++ b/lib/datadog/ci.rb @@ -6,6 +6,7 @@ require_relative "ci/ext/app_types" require_relative "ci/ext/telemetry" +require "datadog" require "datadog/core" module Datadog diff --git a/lib/datadog/ci/auto_instrument.rb b/lib/datadog/ci/auto_instrument.rb new file mode 100644 index 00000000..da0b0148 --- /dev/null +++ b/lib/datadog/ci/auto_instrument.rb @@ -0,0 +1,4 @@ +require "datadog/ci" +require "datadog/ci/contrib/contrib" + +Datadog::CI::Contrib.auto_instrument! diff --git a/lib/datadog/ci/contrib/contrib.rb b/lib/datadog/ci/contrib/contrib.rb index 457a7e74..19378453 100644 --- a/lib/datadog/ci/contrib/contrib.rb +++ b/lib/datadog/ci/contrib/contrib.rb @@ -5,18 +5,83 @@ module Datadog module CI module Contrib + @@auto_instrumented_integrations = {} + + def self.auto_instrument! + Datadog.logger.debug("Auto instrumenting all integrations...") + + Integration.registry.each do |name, integration| + next unless integration.auto_instrument? + + Datadog.logger.debug "#{name} is allowed to be auto instrumented" + + if integration.class.loaded? + Datadog.logger.debug("#{name} is already loaded") + + configure_ci_with_framework(name) + else + Datadog.logger.debug("#{name} is not loaded yet") + + Datadog.logger.debug("Registering on require hook for #{name}...") + + @@auto_instrumented_integrations[name] = integration + end + end + + enable_trace_requires + end + + def self.enable_trace_requires + @@trp = TracePoint.new(:script_compiled) do |tp| + on_require(tp.instruction_sequence.path) + end + + @@trp.enable + end + + def self.disable_trace_requires + @@trp.disable + end + + def self.on_require(path) + Datadog.logger.debug { "Path: #{path}" } + @@auto_instrumented_integrations.each do |gem_name, integration| + if path.include?(gem_name.to_s) && integration.class.loaded? + Datadog.logger.debug { "Gem '#{gem_name}' loaded. Configuring integration." } + + Contrib.disable_trace_requires + + configure_ci_with_framework(gem_name) + end + end + rescue => e + Datadog.logger.debug do + "Failed to execute callback for gem: #{e.class.name} #{e.message} at #{Array(e.backtrace).join("\n")}" + end + end + + def self.configure_ci_with_framework(framework) + Datadog.logger.debug("Configuring CI with #{framework}...") + + Datadog.configure do |c| + c.tracing.enabled = true + c.ci.enabled = true + c.ci.instrument framework + end + end + # This method auto instruments all test libraries (ex: selenium-webdriver). # It is intended to be called when test session starts to add additional capabilities to test visibility. # # This method does not automatically instrument test frameworks (ex: RSpec, Cucumber, etc), it requires # test framework to be already instrumented. - def self.auto_instrument_on_session_start! - Datadog.logger.debug("Auto instrumenting all integrations...") + def self.instrument_on_session_start! + Datadog.logger.debug("Instrumenting additional libraries when session starts...") Integration.registry.each do |name, integration| - next unless integration.auto_instrument? + next unless integration.instrument_on_session_start? - Datadog.logger.debug "#{name} is allowed to be auto instrumented" + Datadog.logger.debug "#{name} is allowed to be instrumented when session starts" patch_results = integration.patch if patch_results == true diff --git a/lib/datadog/ci/contrib/cucumber/integration.rb b/lib/datadog/ci/contrib/cucumber/integration.rb index bbbcf82c..c11542e2 100644 --- a/lib/datadog/ci/contrib/cucumber/integration.rb +++ b/lib/datadog/ci/contrib/cucumber/integration.rb @@ -21,15 +21,18 @@ def self.version end def self.loaded? - !defined?(::Cucumber).nil? && !defined?(::Cucumber::Runtime).nil? + !defined?(::Cucumber).nil? && !defined?(::Cucumber::Runtime).nil? && !defined?(::Cucumber::Configuration).nil? end def self.compatible? super && version >= MINIMUM_VERSION end - # test environments should not auto instrument test libraries def auto_instrument? + true + end + + def instrument_on_session_start? false end diff --git a/lib/datadog/ci/contrib/minitest/integration.rb b/lib/datadog/ci/contrib/minitest/integration.rb index ce1cd89c..fb83b52c 100644 --- a/lib/datadog/ci/contrib/minitest/integration.rb +++ b/lib/datadog/ci/contrib/minitest/integration.rb @@ -21,15 +21,19 @@ def self.version end def self.loaded? - !defined?(::Minitest).nil? + !defined?(::Minitest).nil? && !defined?(::Minitest::Runnable).nil? && !defined?(::Minitest::Test).nil? && + !defined?(::Minitest::CompositeReporter).nil? end def self.compatible? super && version >= MINIMUM_VERSION end - # test environments should not auto instrument test libraries def auto_instrument? + true + end + + def instrument_on_session_start? false end diff --git a/lib/datadog/ci/contrib/rspec/integration.rb b/lib/datadog/ci/contrib/rspec/integration.rb index c0279c79..94d9aff4 100644 --- a/lib/datadog/ci/contrib/rspec/integration.rb +++ b/lib/datadog/ci/contrib/rspec/integration.rb @@ -22,15 +22,22 @@ def self.version def self.loaded? !defined?(::RSpec).nil? && !defined?(::RSpec::Core).nil? && - !defined?(::RSpec::Core::Example).nil? + !defined?(::RSpec::Core::Example).nil? && + !defined?(::RSpec::Core::Runner).nil? && + !defined?(::RSpec::Core::ExampleGroup).nil? end def self.compatible? super && version >= MINIMUM_VERSION end - # test environments should not auto instrument test libraries + # TODO: rename the following 2 methods: the difference is not about auto or on session start: + # the difference is that the first one is for test frameworks, the second one is for additional libraries def auto_instrument? + true + end + + def instrument_on_session_start? false end diff --git a/lib/datadog/ci/contrib/selenium/integration.rb b/lib/datadog/ci/contrib/selenium/integration.rb index 6d3e9bec..57713aad 100644 --- a/lib/datadog/ci/contrib/selenium/integration.rb +++ b/lib/datadog/ci/contrib/selenium/integration.rb @@ -29,8 +29,12 @@ def self.compatible? super && version >= MINIMUM_VERSION end - # additional instrumentations for test helpers are auto instrumented on test session start def auto_instrument? + false + end + + # additional instrumentations for test helpers are instrumented on test session start + def instrument_on_session_start? true end diff --git a/lib/datadog/ci/test_visibility/component.rb b/lib/datadog/ci/test_visibility/component.rb index 572ce78b..2a5b5380 100644 --- a/lib/datadog/ci/test_visibility/component.rb +++ b/lib/datadog/ci/test_visibility/component.rb @@ -146,7 +146,7 @@ def on_test_session_started(test_session) git_tree_upload_worker.perform(test_session.git_repository_url) # finds and instruments additional test libraries that we support (ex: selenium-webdriver) - Contrib.auto_instrument_on_session_start! + Contrib.instrument_on_session_start! # sends internal telemetry events Telemetry.test_session_started(test_session) diff --git a/sig/datadog/ci/contrib/selenium/integration.rbs b/sig/datadog/ci/contrib/selenium/integration.rbs index 15a9b87a..9c04ea59 100644 --- a/sig/datadog/ci/contrib/selenium/integration.rbs +++ b/sig/datadog/ci/contrib/selenium/integration.rbs @@ -14,7 +14,7 @@ module Datadog def self.compatible?: () -> bool - def auto_instrument?: () -> true + def auto_instrument?: () -> bool def new_configuration: () -> untyped diff --git a/spec/datadog/ci/configuration/settings_spec.rb b/spec/datadog/ci/configuration/settings_spec.rb index b70eae46..2ee655ee 100644 --- a/spec/datadog/ci/configuration/settings_spec.rb +++ b/spec/datadog/ci/configuration/settings_spec.rb @@ -38,6 +38,10 @@ def self.auto_instrument? false end + def instrument_on_session_start? + false + end + def patcher Patcher end diff --git a/spec/datadog/ci/contrib/cucumber/integration_spec.rb b/spec/datadog/ci/contrib/cucumber/integration_spec.rb index de70ee02..32cc415e 100644 --- a/spec/datadog/ci/contrib/cucumber/integration_spec.rb +++ b/spec/datadog/ci/contrib/cucumber/integration_spec.rb @@ -57,7 +57,7 @@ describe "#auto_instrument?" do subject(:auto_instrument?) { integration.auto_instrument? } - it { is_expected.to be(false) } + it { is_expected.to be(true) } end describe "#configuration" do diff --git a/spec/datadog/ci/contrib/minitest/integration_spec.rb b/spec/datadog/ci/contrib/minitest/integration_spec.rb index 105f17da..5663b486 100644 --- a/spec/datadog/ci/contrib/minitest/integration_spec.rb +++ b/spec/datadog/ci/contrib/minitest/integration_spec.rb @@ -49,7 +49,7 @@ describe "#auto_instrument?" do subject(:auto_instrument?) { integration.auto_instrument? } - it { is_expected.to be(false) } + it { is_expected.to be(true) } end describe "#configuration" do diff --git a/spec/datadog/ci/contrib/rspec/integration_spec.rb b/spec/datadog/ci/contrib/rspec/integration_spec.rb index b387a13a..9ea21ea3 100644 --- a/spec/datadog/ci/contrib/rspec/integration_spec.rb +++ b/spec/datadog/ci/contrib/rspec/integration_spec.rb @@ -49,7 +49,7 @@ describe "#auto_instrument?" do subject(:auto_instrument?) { integration.auto_instrument? } - it { is_expected.to be(false) } + it { is_expected.to be(true) } end describe "#configuration" do diff --git a/spec/datadog/ci/release_gem_spec.rb b/spec/datadog/ci/release_gem_spec.rb index 5708c587..8567fbe6 100644 --- a/spec/datadog/ci/release_gem_spec.rb +++ b/spec/datadog/ci/release_gem_spec.rb @@ -40,7 +40,6 @@ |\.circleci |\.github |\.vscode - | bin |gemfiles |integration |tasks