diff --git a/.github/scripts/find_gem_version_bounds.rb b/.github/scripts/find_gem_version_bounds.rb new file mode 100644 index 00000000000..93daee54981 --- /dev/null +++ b/.github/scripts/find_gem_version_bounds.rb @@ -0,0 +1,149 @@ +require 'pathname' +require 'rubygems' +require 'json' +require 'bundler' + +lib = File.expand_path('lib', __dir__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'datadog' + +class GemfileProcessor + SPECIAL_CASES = { + "opensearch" => "OpenSearch" # special case because opensearch = OpenSearch not Opensearch + }.freeze + EXCLUDED_INTEGRATIONS = ["configuration", "propagation", "utils"].freeze + + def initialize(directory: 'gemfiles/', contrib_dir: 'lib/datadog/tracing/contrib/') + @directory = directory + @contrib_dir = contrib_dir + @min_gems = { 'ruby' => {}, 'jruby' => {} } + @max_gems = { 'ruby' => {}, 'jruby' => {} } + @integration_json_mapping = {} + end + + def process + parse_gemfiles + process_integrations + include_hardcoded_versions + write_output + end + + private + + + def parse_gemfiles(directory = 'gemfiles/') + gemfiles = Dir.glob(File.join(@directory, '*')) + gemfiles.each do |gemfile_name| + runtime = File.basename(gemfile_name).split('_').first # ruby or jruby + next unless %w[ruby jruby].include?(runtime) + # parse the gemfile + if gemfile_name.end_with?(".gemfile") + process_gemfile(gemfile_name, runtime) + elsif gemfile_name.end_with?('.gemfile.lock') + process_lockfile(gemfile_name, runtime) + end + end + + end + + def process_gemfile(gemfile_name, runtime) + begin + definition = Bundler::Definition.build(gemfile_name, nil, nil) + definition.dependencies.each do |dependency| + gem_name = dependency.name + version = dependency.requirement.to_s + update_gem_versions(runtime, gem_name, version) + end + rescue Bundler::GemfileError => e + puts "Error reading Gemfile: #{e.message}" + end + end + + def process_lockfile(gemfile_name, runtime) + lockfile_contents = File.read(gemfile_name) + parser = Bundler::LockfileParser.new(lockfile_contents) + parser.specs.each do |spec| + gem_name = spec.name + version = spec.version.to_s + update_gem_versions(runtime, gem_name, version) + end + end + + def update_gem_versions(runtime, gem_name, version) + return unless version_valid?(version) + + gem_version = Gem::Version.new(version) + + # Update minimum gems + if @min_gems[runtime][gem_name].nil? || gem_version < Gem::Version.new(@min_gems[runtime][gem_name]) + @min_gems[runtime][gem_name] = version + end + + # Update maximum gems + if @max_gems[runtime][gem_name].nil? || gem_version > Gem::Version.new(@max_gems[runtime][gem_name]) + @max_gems[runtime][gem_name] = version + end + end + + # Helper: Validate the version format + def version_valid?(version) + return false if version.nil? || version.strip.empty? + + Gem::Version.new(version) + true + rescue ArgumentError + false + end + + + def process_integrations + integrations = Datadog::Tracing::Contrib::REGISTRY.map(&:name).map(&:to_s) + integrations.each do |integration| + next if EXCLUDED_INTEGRATIONS.include?(integration) + + integration_name = resolve_integration_name(integration) + + @integration_json_mapping[integration] = [ + @min_gems['ruby'][integration_name], + @max_gems['ruby'][integration_name], + @min_gems['jruby'][integration_name], + @max_gems['jruby'][integration_name] + ] + end + end + + def include_hardcoded_versions + # `httpx` is maintained externally + @integration_json_mapping['httpx'] = [ + '0.11', # Min version Ruby + '0.11', # Max version Ruby + nil, # Min version JRuby + nil # Max version JRuby + ] + + # `makara` is part of `activerecord` + @integration_json_mapping['makara'] = [ + '0.3.5', # Min version Ruby + '0.3.5', # Max version Ruby + nil, # Min version JRuby + nil # Max version JRuby + ] + end + + def resolve_integration_name(integration) + mod_name = SPECIAL_CASES[integration] || integration.split('_').map(&:capitalize).join + module_name = "Datadog::Tracing::Contrib::#{mod_name}" + integration_module = Object.const_get(module_name)::Integration + integration_module.respond_to?(:gem_name) ? integration_module.gem_name : integration + rescue NameError, NoMethodError + puts "Fallback for #{integration}: module or gem_name not found." + integration + end + + def write_output + @integration_json_mapping = @integration_json_mapping.sort.to_h + File.write("gem_output.json", JSON.pretty_generate(@integration_json_mapping)) + end +end + +GemfileProcessor.new.process \ No newline at end of file diff --git a/.github/scripts/generate_table_versions.rb b/.github/scripts/generate_table_versions.rb new file mode 100644 index 00000000000..bbc8ef62dcd --- /dev/null +++ b/.github/scripts/generate_table_versions.rb @@ -0,0 +1,21 @@ +require 'json' + +input_file = 'gem_output.json' +output_file = 'integration_versions.md' + +data = JSON.parse(File.read(input_file)) + +comment = "# Integrations\n\n" +header = "| Integration | Ruby Min | Ruby Max | JRuby Min | JRuby Max |\n" +separator = "|-------------|----------|-----------|----------|----------|\n" +rows = data.map do |integration_name, versions| + ruby_min, ruby_max, jruby_min, jruby_max = versions.map { |v| v || "None" } + "| #{integration_name} | #{ruby_min} | #{ruby_max} | #{jruby_min} | #{jruby_max} |" +end + +File.open(output_file, 'w') do |file| + file.puts comment + file.puts header + file.puts separator + rows.each { |row| file.puts row } +end diff --git a/.github/workflows/generate-supported-versions.yml b/.github/workflows/generate-supported-versions.yml new file mode 100644 index 00000000000..ae64be78c6f --- /dev/null +++ b/.github/workflows/generate-supported-versions.yml @@ -0,0 +1,49 @@ +name: "Generate Supported Versions" + +on: + workflow_dispatch: + + +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-22.04 + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + bundler-cache: true # runs bundle install + ruby-version: "3.3" + + - name: Update latest + run: bundle exec ruby .github/scripts/find_gem_version_bounds.rb + + - name: Generate versions table + run: ruby .github/scripts/generate_table_versions.rb + + - run: git diff + + - name: Create Pull Request + id: cpr + uses: peter-evans/create-pull-request@v7 + with: + token: ${{ secrets.GHA_PAT }} + branch: auto-generate/update-supported-versions + title: '[🤖] Update Supported Versions' + base: master + labels: dev/internal, integrations + commit-message: "Test creating supported versions" + delete-branch: true + body: | + This is a PR to update the table for supported integration versions. + Workflow run: [Generate Supported Versions](https://github.com/DataDog/dd-trace-rb/actions/workflows/generate-supported-versions.yml) + This should be tied to tracer releases, or triggered manually. + diff --git a/lib/datadog/tracing/contrib/action_cable/integration.rb b/lib/datadog/tracing/contrib/action_cable/integration.rb index fa1086e74ac..1699238dd4c 100644 --- a/lib/datadog/tracing/contrib/action_cable/integration.rb +++ b/lib/datadog/tracing/contrib/action_cable/integration.rb @@ -17,6 +17,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :action_cable, auto_patch: false + def self.gem_name + 'actioncable' + end def self.version Gem.loaded_specs['actioncable'] && Gem.loaded_specs['actioncable'].version diff --git a/lib/datadog/tracing/contrib/action_mailer/integration.rb b/lib/datadog/tracing/contrib/action_mailer/integration.rb index fc9b4e5a980..07fef8095af 100644 --- a/lib/datadog/tracing/contrib/action_mailer/integration.rb +++ b/lib/datadog/tracing/contrib/action_mailer/integration.rb @@ -18,6 +18,10 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :action_mailer, auto_patch: false + def self.gem_name + 'actionmailer' + end + def self.version Gem.loaded_specs['actionmailer'] && Gem.loaded_specs['actionmailer'].version end diff --git a/lib/datadog/tracing/contrib/action_pack/integration.rb b/lib/datadog/tracing/contrib/action_pack/integration.rb index 881d9ce273c..c0efa9196dc 100644 --- a/lib/datadog/tracing/contrib/action_pack/integration.rb +++ b/lib/datadog/tracing/contrib/action_pack/integration.rb @@ -18,6 +18,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :action_pack, auto_patch: false + def self.gem_name + 'actionpack' + end def self.version Gem.loaded_specs['actionpack'] && Gem.loaded_specs['actionpack'].version diff --git a/lib/datadog/tracing/contrib/action_view/integration.rb b/lib/datadog/tracing/contrib/action_view/integration.rb index c56240be1f4..48dd75fe94c 100644 --- a/lib/datadog/tracing/contrib/action_view/integration.rb +++ b/lib/datadog/tracing/contrib/action_view/integration.rb @@ -18,6 +18,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :action_view, auto_patch: false + def self.gem_name + 'actionview' + end def self.version # ActionView is its own gem in Rails 4.1+ diff --git a/lib/datadog/tracing/contrib/active_job/integration.rb b/lib/datadog/tracing/contrib/active_job/integration.rb index 89718a647f2..73b912a894f 100644 --- a/lib/datadog/tracing/contrib/active_job/integration.rb +++ b/lib/datadog/tracing/contrib/active_job/integration.rb @@ -17,6 +17,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :active_job, auto_patch: false + def self.gem_name + 'activejob' + end def self.version Gem.loaded_specs['activejob'] && Gem.loaded_specs['activejob'].version diff --git a/lib/datadog/tracing/contrib/active_record/integration.rb b/lib/datadog/tracing/contrib/active_record/integration.rb index d5fd2294aae..f9ac1048453 100644 --- a/lib/datadog/tracing/contrib/active_record/integration.rb +++ b/lib/datadog/tracing/contrib/active_record/integration.rb @@ -22,6 +22,10 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :active_record, auto_patch: false + def self.gem_name + 'activerecord' + end + def self.version Gem.loaded_specs['activerecord'] && Gem.loaded_specs['activerecord'].version end diff --git a/lib/datadog/tracing/contrib/active_support/integration.rb b/lib/datadog/tracing/contrib/active_support/integration.rb index 9cf29c4b1c5..e335a6349a1 100644 --- a/lib/datadog/tracing/contrib/active_support/integration.rb +++ b/lib/datadog/tracing/contrib/active_support/integration.rb @@ -19,6 +19,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :active_support, auto_patch: false + def self.gem_name + 'activesupport' + end def self.version Gem.loaded_specs['activesupport'] && Gem.loaded_specs['activesupport'].version diff --git a/lib/datadog/tracing/contrib/aws/integration.rb b/lib/datadog/tracing/contrib/aws/integration.rb index b3a631bcb4a..167ea1be49a 100644 --- a/lib/datadog/tracing/contrib/aws/integration.rb +++ b/lib/datadog/tracing/contrib/aws/integration.rb @@ -16,6 +16,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :aws, auto_patch: true + def self.gem_name + 'aws-sdk-core' + end def self.version if Gem.loaded_specs['aws-sdk'] diff --git a/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb b/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb index 17ac1daf8ca..e82414273a3 100644 --- a/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb +++ b/lib/datadog/tracing/contrib/concurrent_ruby/integration.rb @@ -16,6 +16,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :concurrent_ruby + def self.gem_name + 'concurrent-ruby' + end def self.version Gem.loaded_specs['concurrent-ruby'] && Gem.loaded_specs['concurrent-ruby'].version diff --git a/lib/datadog/tracing/contrib/httprb/integration.rb b/lib/datadog/tracing/contrib/httprb/integration.rb index ba4dfd727dd..a5dd5fcc708 100644 --- a/lib/datadog/tracing/contrib/httprb/integration.rb +++ b/lib/datadog/tracing/contrib/httprb/integration.rb @@ -17,6 +17,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :httprb + def self.gem_name + 'http' + end def self.version Gem.loaded_specs['http'] && Gem.loaded_specs['http'].version diff --git a/lib/datadog/tracing/contrib/kafka/integration.rb b/lib/datadog/tracing/contrib/kafka/integration.rb index ce708f50123..d0e934cb06d 100644 --- a/lib/datadog/tracing/contrib/kafka/integration.rb +++ b/lib/datadog/tracing/contrib/kafka/integration.rb @@ -16,6 +16,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :kafka, auto_patch: false + def self.gem_name + 'ruby-kafka' + end def self.version Gem.loaded_specs['ruby-kafka'] && Gem.loaded_specs['ruby-kafka'].version diff --git a/lib/datadog/tracing/contrib/mongodb/integration.rb b/lib/datadog/tracing/contrib/mongodb/integration.rb index 4a31fedbb3d..e4ad350ea62 100644 --- a/lib/datadog/tracing/contrib/mongodb/integration.rb +++ b/lib/datadog/tracing/contrib/mongodb/integration.rb @@ -17,6 +17,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :mongo, auto_patch: true + def self.gem_name + 'mongo' + end def self.version Gem.loaded_specs['mongo'] && Gem.loaded_specs['mongo'].version diff --git a/lib/datadog/tracing/contrib/opensearch/integration.rb b/lib/datadog/tracing/contrib/opensearch/integration.rb index 0bf906cb07e..d0863e1efe8 100644 --- a/lib/datadog/tracing/contrib/opensearch/integration.rb +++ b/lib/datadog/tracing/contrib/opensearch/integration.rb @@ -16,6 +16,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :opensearch, auto_patch: true + def self.gem_name + 'opensearch-ruby' + end def self.version Gem.loaded_specs['opensearch-ruby'] \ diff --git a/lib/datadog/tracing/contrib/presto/integration.rb b/lib/datadog/tracing/contrib/presto/integration.rb index 63c55c9bdc4..7bfae3d42a2 100644 --- a/lib/datadog/tracing/contrib/presto/integration.rb +++ b/lib/datadog/tracing/contrib/presto/integration.rb @@ -16,6 +16,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :presto + def self.gem_name + 'presto-client' + end def self.version Gem.loaded_specs['presto-client'] && Gem.loaded_specs['presto-client'].version diff --git a/lib/datadog/tracing/contrib/rest_client/integration.rb b/lib/datadog/tracing/contrib/rest_client/integration.rb index 04ee5339733..d3dc0bf3c30 100644 --- a/lib/datadog/tracing/contrib/rest_client/integration.rb +++ b/lib/datadog/tracing/contrib/rest_client/integration.rb @@ -16,6 +16,9 @@ class Integration # @public_api Changing the integration name or integration options can cause breaking changes register_as :rest_client + def self.gem_name + 'rest-client' + end def self.version Gem.loaded_specs['rest-client'] && Gem.loaded_specs['rest-client'].version