datadog-ci 1.6.0 → 1.7.0

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: 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