datadog-ci 1.6.0 → 1.7.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: f38fbb3ab8e2c8c1b058d7cbefb3ac30c59cd138fb2a69929d2c7309763b0c6e
4
- data.tar.gz: 862f8c10065b47fb8763701399de65cf0da652f36f0d301ba3b0cd4e1c12080c
3
+ metadata.gz: f4cb94bf0ef94f54289e38a26649d24eeaaca99ba91229f5ebb096c046bdc582
4
+ data.tar.gz: 9a88847a58121a1516b95fc1abac1fa89d4a1bab5c4593f932fc804410ee8a57
5
5
  SHA512:
6
- metadata.gz: 4be4dec3f2b5a11d0a822a3cd17d422eedab4262445dd42ba4d4efa96d4ee3f92049e5b37e1b6497928cf1fe980a847e4749538275092ca9fecd24700db3c62f
7
- data.tar.gz: 2ebcae9e728ffd517d2c4f583808ade0170cb705967b59710213633bc73d1cd5d529c2b22323f8f70d45f657a8d848e856560aacc53c3ec7aea6e3aff45110af
6
+ metadata.gz: 7830a7b52821efbea8e0f86e878588ef96718e845fd7a33575a4a5e92d350e26e36bb4b78f7ce78b58ff59654c62973c4eca58f21c63b958d44f4d8feff122cc
7
+ data.tar.gz: da8d30e84f9dea71eb498cccb6e03cb092e27e773008dc7533188020ee5cb59de774e4d2be32821e3fd82e28f4ccb426572695fd532dc7f7fff6479a392bca85
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [1.7.0] - 2024-09-25
4
+
5
+ ### Added
6
+ * Report total lines coverage percentage to Datadog ([#240][])
7
+ * add source location info to test suites ([#239][])
8
+ * Add pull_request extra tags for GitHub Actions ([#238][])
9
+
3
10
  ## [1.6.0] - 2024-09-20
4
11
 
5
12
 
@@ -332,7 +339,8 @@ Currently test suite level visibility is not used by our instrumentation: it wil
332
339
 
333
340
  - Ruby versions < 2.7 no longer supported ([#8][])
334
341
 
335
- [Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.6.0...main
342
+ [Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.7.0...main
343
+ [1.7.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.6.0...v1.7.0
336
344
  [1.6.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.5.0...v1.6.0
337
345
  [1.5.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.4.1...v1.5.0
338
346
  [1.4.1]: https://github.com/DataDog/datadog-ci-rb/compare/v1.4.0...v1.4.1
@@ -477,4 +485,7 @@ Currently test suite level visibility is not used by our instrumentation: it wil
477
485
  [#229]: https://github.com/DataDog/datadog-ci-rb/issues/229
478
486
  [#231]: https://github.com/DataDog/datadog-ci-rb/issues/231
479
487
  [#235]: https://github.com/DataDog/datadog-ci-rb/issues/235
480
- [#236]: https://github.com/DataDog/datadog-ci-rb/issues/236
488
+ [#236]: https://github.com/DataDog/datadog-ci-rb/issues/236
489
+ [#238]: https://github.com/DataDog/datadog-ci-rb/issues/238
490
+ [#239]: https://github.com/DataDog/datadog-ci-rb/issues/239
491
+ [#240]: https://github.com/DataDog/datadog-ci-rb/issues/240
@@ -70,7 +70,12 @@ module Datadog
70
70
  tags[CI::Ext::Test::TAG_PARAMETERS] = Utils::TestRun.test_parameters(arguments: parameters)
71
71
  end
72
72
 
73
- start_test_suite(test_suite_name) unless same_test_suite_as_current?(test_suite_name)
73
+ unless same_test_suite_as_current?(test_suite_name)
74
+ start_test_suite(
75
+ test_suite_name,
76
+ tags: test_suite_source_file_tags(event.test_case)
77
+ )
78
+ end
74
79
 
75
80
  test_span = test_visibility_component.trace_test(
76
81
  event.test_case.name,
@@ -146,10 +151,10 @@ module Datadog
146
151
  test_session.finish
147
152
  end
148
153
 
149
- def start_test_suite(test_suite_name)
154
+ def start_test_suite(test_suite_name, tags: {})
150
155
  finish_current_test_suite
151
156
 
152
- @current_test_suite = test_visibility_component.start_test_suite(test_suite_name)
157
+ @current_test_suite = test_visibility_component.start_test_suite(test_suite_name, tags: tags)
153
158
  end
154
159
 
155
160
  def finish_current_test_suite
@@ -201,6 +206,23 @@ module Datadog
201
206
  def test_visibility_component
202
207
  Datadog.send(:components).test_visibility
203
208
  end
209
+
210
+ def test_suite_source_file_tags(test_case)
211
+ if test_case.respond_to?(:parent_locations)
212
+ # supported in cucumber >= 9.0
213
+ source_file = test_case.parent_locations.file
214
+ line_number = test_case.parent_locations.line.to_s
215
+ else
216
+ # fallback for cucumber < 9.0
217
+ source_file = test_case.location.file
218
+ line_number = "1"
219
+ end
220
+
221
+ {
222
+ CI::Ext::Test::TAG_SOURCE_FILE => Git::LocalRepository.relative_to_root(source_file),
223
+ CI::Ext::Test::TAG_SOURCE_START => line_number.to_s
224
+ }
225
+ end
204
226
  end
205
227
  end
206
228
  end
@@ -6,7 +6,7 @@ module Datadog
6
6
  module Minitest
7
7
  module Helpers
8
8
  def self.test_suite_name(klass, method_name)
9
- source_location = extract_source_location_from_class(klass)
9
+ source_location = extract_source_location_from_class(klass)&.first
10
10
  # if we are in anonymous class, fallback to the method source location
11
11
  if source_location.nil?
12
12
  source_location, = klass.instance_method(method_name).source_location
@@ -23,11 +23,11 @@ module Datadog
23
23
  end
24
24
 
25
25
  def self.extract_source_location_from_class(klass)
26
- return nil if klass.nil? || klass.name.nil?
26
+ return [] if klass.nil? || klass.name.nil?
27
27
 
28
- klass.const_source_location(klass.name)&.first
28
+ klass.const_source_location(klass.name)
29
29
  rescue
30
- nil
30
+ []
31
31
  end
32
32
  end
33
33
  end
@@ -18,8 +18,21 @@ module Datadog
18
18
  return super if method.nil?
19
19
 
20
20
  test_suite_name = Helpers.test_suite_name(self, method)
21
-
22
- test_suite = test_visibility_component.start_test_suite(test_suite_name)
21
+ source_file, line_number = Helpers.extract_source_location_from_class(self)
22
+
23
+ test_suite_tags = if source_file
24
+ {
25
+ CI::Ext::Test::TAG_SOURCE_FILE => (Git::LocalRepository.relative_to_root(source_file) if source_file),
26
+ CI::Ext::Test::TAG_SOURCE_START => line_number&.to_s
27
+ }
28
+ else
29
+ {}
30
+ end
31
+
32
+ test_suite = test_visibility_component.start_test_suite(
33
+ test_suite_name,
34
+ tags: test_suite_tags
35
+ )
23
36
 
24
37
  results = super
25
38
  return results unless test_suite
@@ -21,7 +21,13 @@ module Datadog
21
21
  return super unless top_level?
22
22
 
23
23
  suite_name = "#{description} at #{file_path}"
24
- test_suite = test_visibility_component.start_test_suite(suite_name)
24
+ test_suite = test_visibility_component.start_test_suite(
25
+ suite_name,
26
+ tags: {
27
+ CI::Ext::Test::TAG_SOURCE_FILE => Git::LocalRepository.relative_to_root(metadata[:file_path]),
28
+ CI::Ext::Test::TAG_SOURCE_START => metadata[:line_number].to_s
29
+ }
30
+ )
25
31
 
26
32
  success = super
27
33
  return success unless test_suite
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "datadog/core"
4
+
5
+ require_relative "../ext"
6
+ require_relative "../../settings"
7
+
8
+ module Datadog
9
+ module CI
10
+ module Contrib
11
+ module Simplecov
12
+ module Configuration
13
+ # Custom settings for the Simplecov integration
14
+ # @public_api
15
+ class Settings < Datadog::CI::Contrib::Settings
16
+ option :enabled do |o|
17
+ o.type :bool
18
+ o.env Ext::ENV_ENABLED
19
+ o.default true
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Datadog
4
+ module CI
5
+ module Contrib
6
+ module Simplecov
7
+ # Simplecov integration constants
8
+ # @public_api
9
+ module Ext
10
+ ENV_ENABLED = "DD_CIVISIBILITY_SIMPLECOV_INSTRUMENTATION_ENABLED"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../integration"
4
+ require_relative "configuration/settings"
5
+ require_relative "patcher"
6
+
7
+ module Datadog
8
+ module CI
9
+ module Contrib
10
+ module Simplecov
11
+ # Description of Simplecov integration
12
+ class Integration
13
+ include Datadog::CI::Contrib::Integration
14
+
15
+ MINIMUM_VERSION = Gem::Version.new("0.18.0")
16
+
17
+ register_as :simplecov
18
+
19
+ def self.version
20
+ Gem.loaded_specs["simplecov"]&.version
21
+ end
22
+
23
+ def self.loaded?
24
+ !defined?(::SimpleCov).nil?
25
+ end
26
+
27
+ def self.compatible?
28
+ super && version >= MINIMUM_VERSION
29
+ end
30
+
31
+ # additional instrumentations for test helpers are auto instrumented on test session start
32
+ def auto_instrument?
33
+ true
34
+ end
35
+
36
+ def new_configuration
37
+ Configuration::Settings.new
38
+ end
39
+
40
+ def patcher
41
+ Patcher
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "datadog/tracing/contrib/patcher"
4
+
5
+ require_relative "result_extractor"
6
+
7
+ module Datadog
8
+ module CI
9
+ module Contrib
10
+ module Simplecov
11
+ # Patcher enables patching of 'SimpleCov' module.
12
+ module Patcher
13
+ include Datadog::Tracing::Contrib::Patcher
14
+
15
+ module_function
16
+
17
+ def target_version
18
+ Integration.version
19
+ end
20
+
21
+ def patch
22
+ ::SimpleCov.include(ResultExtractor)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "coverage"
4
+
5
+ module Datadog
6
+ module CI
7
+ module Contrib
8
+ module Simplecov
9
+ module ResultExtractor
10
+ def self.included(base)
11
+ base.singleton_class.prepend(ClassMethods)
12
+ end
13
+
14
+ module ClassMethods
15
+ def __dd_peek_result
16
+ unless datadog_configuration[:enabled]
17
+ Datadog.logger.debug("SimpleCov instrumentation is disabled")
18
+ return nil
19
+ end
20
+
21
+ result = ::SimpleCov::UselessResultsRemover.call(
22
+ ::SimpleCov::ResultAdapter.call(::Coverage.peek_result)
23
+ )
24
+
25
+ ::SimpleCov::Result.new(add_not_loaded_files(result))
26
+ end
27
+
28
+ def datadog_configuration
29
+ Datadog.configuration.ci[:simplecov]
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -49,6 +49,11 @@ module Datadog
49
49
  Git::TAG_COMMIT_SHA => @provider.git_commit_sha
50
50
  }
51
51
 
52
+ # set additional tags if provider needs them
53
+ @provider.additional_tags.each do |key, value|
54
+ @tags[key] = value
55
+ end
56
+
52
57
  # Normalize Git references and filter sensitive data
53
58
  normalize_git!
54
59
  # Expand ~
@@ -96,6 +96,10 @@ module Datadog
96
96
  def git_commit_sha
97
97
  end
98
98
 
99
+ def additional_tags
100
+ {}
101
+ end
102
+
99
103
  private
100
104
 
101
105
  def set_branch_and_tag
@@ -75,6 +75,30 @@ module Datadog
75
75
  }.reject { |_, v| v.nil? }.to_json
76
76
  end
77
77
 
78
+ def additional_tags
79
+ base_ref = env["GITHUB_BASE_REF"]
80
+ return {} if base_ref.nil? || base_ref.empty?
81
+
82
+ # @type var result: Hash[String, String]
83
+ result = {
84
+ Git::TAG_PULL_REQUEST_BASE_BRANCH => base_ref
85
+ }
86
+
87
+ event_path = env["GITHUB_EVENT_PATH"]
88
+ event_json = JSON.parse(File.read(event_path))
89
+
90
+ head_sha = event_json.dig("pull_request", "head", "sha")
91
+ result[Git::TAG_COMMIT_HEAD_SHA] = head_sha if head_sha
92
+
93
+ base_sha = event_json.dig("pull_request", "base", "sha")
94
+ result[Git::TAG_PULL_REQUEST_BASE_BRANCH_SHA] = base_sha if base_sha
95
+
96
+ result
97
+ rescue => e
98
+ Datadog.logger.error("Failed to extract additional tags from GitHub Actions: #{e}")
99
+ {}
100
+ end
101
+
78
102
  private
79
103
 
80
104
  def github_server_url
@@ -20,6 +20,11 @@ module Datadog
20
20
  TAG_COMMIT_MESSAGE = "git.commit.message"
21
21
  TAG_COMMIT_SHA = "git.commit.sha"
22
22
 
23
+ # additional tags that we use for github actions jobs with "pull_request" target
24
+ TAG_COMMIT_HEAD_SHA = "git.commit.head_sha"
25
+ TAG_PULL_REQUEST_BASE_BRANCH = "git.pull_request.base_branch"
26
+ TAG_PULL_REQUEST_BASE_BRANCH_SHA = "git.pull_request.base_branch_sha"
27
+
23
28
  ENV_REPOSITORY_URL = "DD_GIT_REPOSITORY_URL"
24
29
  ENV_COMMIT_SHA = "DD_GIT_COMMIT_SHA"
25
30
  ENV_BRANCH = "DD_GIT_BRANCH"
@@ -64,6 +64,9 @@ module Datadog
64
64
  TAG_EARLY_FLAKE_ENABLED = "test.early_flake.enabled" # true if early flake detection is enabled
65
65
  TAG_EARLY_FLAKE_ABORT_REASON = "test.early_flake.abort_reason" # reason why early flake detection was aborted
66
66
 
67
+ # Tags for total code coverage
68
+ TAG_CODE_COVERAGE_LINES_PCT = "test.code_coverage.lines_pct"
69
+
67
70
  # internal APM tag to mark a span as a test span
68
71
  TAG_SPAN_KIND = "span.kind"
69
72
  SPAN_KIND_TEST = "test"
@@ -190,6 +190,13 @@ module Datadog
190
190
  tracer_span.get_tag(Ext::Test::TAG_RUNTIME_VERSION)
191
191
  end
192
192
 
193
+ # Source file path where the test or test suite defined (relative to git repository root).
194
+ # @return [String] the source file path of the test
195
+ # @return [nil] if the source file path is not found
196
+ def source_file
197
+ get_tag(Ext::Test::TAG_SOURCE_FILE)
198
+ end
199
+
193
200
  def set_environment_runtime_tags
194
201
  tracer_span.set_tag(Ext::Test::TAG_OS_ARCHITECTURE, ::RbConfig::CONFIG["host_cpu"])
195
202
  tracer_span.set_tag(Ext::Test::TAG_OS_PLATFORM, ::RbConfig::CONFIG["host_os"])
@@ -57,13 +57,6 @@ module Datadog
57
57
  get_tag(Ext::Test::TAG_TEST_SESSION_ID)
58
58
  end
59
59
 
60
- # Source file path of the test relative to git repository root.
61
- # @return [String] the source file path of the test
62
- # @return [nil] if the source file path is not found
63
- def source_file
64
- get_tag(Ext::Test::TAG_SOURCE_FILE)
65
- end
66
-
67
60
  # Returns "true" if the test is skipped by the intelligent test runner.
68
61
  # @return [Boolean] true if the test is skipped by the intelligent test runner, false otherwise.
69
62
  def skipped_by_itr?
@@ -4,6 +4,7 @@ require "rbconfig"
4
4
 
5
5
  require_relative "context"
6
6
  require_relative "telemetry"
7
+ require_relative "total_coverage"
7
8
 
8
9
  require_relative "../codeowners/parser"
9
10
  require_relative "../contrib/contrib"
@@ -166,6 +167,8 @@ module Datadog
166
167
  end
167
168
 
168
169
  def on_test_suite_started(test_suite)
170
+ set_codeowners(test_suite)
171
+
169
172
  Telemetry.event_created(test_suite)
170
173
  end
171
174
 
@@ -186,6 +189,8 @@ module Datadog
186
189
  def on_test_session_finished(test_session)
187
190
  test_optimisation.write_test_session_tags(test_session)
188
191
 
192
+ TotalCoverage.extract_lines_pct(test_session)
193
+
189
194
  Telemetry.event_finished(test_session)
190
195
  end
191
196
 
@@ -223,10 +228,10 @@ module Datadog
223
228
  end
224
229
  end
225
230
 
226
- def set_codeowners(test)
227
- source = test.source_file
231
+ def set_codeowners(span)
232
+ source = span.source_file
228
233
  owners = @codeowners.list_owners(source) if source
229
- test.set_tag(Ext::Test::TAG_CODEOWNERS, owners) unless owners.nil?
234
+ span.set_tag(Ext::Test::TAG_CODEOWNERS, owners) unless owners.nil?
230
235
  end
231
236
 
232
237
  def fix_test_suite!(test)
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../ext/test"
4
+
5
+ module Datadog
6
+ module CI
7
+ module TestVisibility
8
+ module TotalCoverage
9
+ def self.extract_lines_pct(test_session)
10
+ unless defined?(::SimpleCov)
11
+ Datadog.logger.debug("SimpleCov is not loaded, skipping code coverage extraction")
12
+ return
13
+ end
14
+
15
+ unless ::SimpleCov.running
16
+ Datadog.logger.debug("SimpleCov is not running, skipping code coverage extraction")
17
+ return
18
+ end
19
+
20
+ unless ::SimpleCov.respond_to?(:__dd_peek_result)
21
+ Datadog.logger.debug("SimpleCov is not patched, skipping code coverage extraction")
22
+ return
23
+ end
24
+
25
+ result = ::SimpleCov.__dd_peek_result
26
+ unless result
27
+ Datadog.logger.debug("SimpleCov result is nil, skipping code coverage extraction")
28
+ return
29
+ end
30
+
31
+ test_session.set_tag(Ext::Test::TAG_CODE_COVERAGE_LINES_PCT, result.covered_percent)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -4,7 +4,7 @@ module Datadog
4
4
  module CI
5
5
  module VERSION
6
6
  MAJOR = 1
7
- MINOR = 6
7
+ MINOR = 7
8
8
  PATCH = 0
9
9
  PRE = nil
10
10
  BUILD = nil
data/lib/datadog/ci.rb CHANGED
@@ -410,6 +410,7 @@ require_relative "ci/contrib/cucumber/integration"
410
410
  require_relative "ci/contrib/rspec/integration"
411
411
  require_relative "ci/contrib/minitest/integration"
412
412
  require_relative "ci/contrib/selenium/integration"
413
+ require_relative "ci/contrib/simplecov/integration"
413
414
 
414
415
  # Configuration extensions
415
416
  require_relative "ci/configuration/extensions"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datadog-ci
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Datadog, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-20 00:00:00.000000000 Z
11
+ date: 2024-09-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: datadog
@@ -103,6 +103,11 @@ files:
103
103
  - lib/datadog/ci/contrib/selenium/patcher.rb
104
104
  - lib/datadog/ci/contrib/selenium/rum.rb
105
105
  - lib/datadog/ci/contrib/settings.rb
106
+ - lib/datadog/ci/contrib/simplecov/configuration/settings.rb
107
+ - lib/datadog/ci/contrib/simplecov/ext.rb
108
+ - lib/datadog/ci/contrib/simplecov/integration.rb
109
+ - lib/datadog/ci/contrib/simplecov/patcher.rb
110
+ - lib/datadog/ci/contrib/simplecov/result_extractor.rb
106
111
  - lib/datadog/ci/ext/app_types.rb
107
112
  - lib/datadog/ci/ext/environment.rb
108
113
  - lib/datadog/ci/ext/environment/extractor.rb
@@ -179,6 +184,7 @@ files:
179
184
  - lib/datadog/ci/test_visibility/store/global.rb
180
185
  - lib/datadog/ci/test_visibility/store/local.rb
181
186
  - lib/datadog/ci/test_visibility/telemetry.rb
187
+ - lib/datadog/ci/test_visibility/total_coverage.rb
182
188
  - lib/datadog/ci/test_visibility/transport.rb
183
189
  - lib/datadog/ci/transport/adapters/net.rb
184
190
  - lib/datadog/ci/transport/adapters/net_http_client.rb