datadog-ci 1.16.0 → 1.18.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 +4 -4
- data/CHANGELOG.md +37 -2
- data/ext/datadog_ci_native/ci.c +10 -0
- data/ext/{datadog_cov → datadog_ci_native}/datadog_cov.c +119 -147
- data/ext/datadog_ci_native/datadog_cov.h +3 -0
- data/ext/datadog_ci_native/datadog_source_code.c +28 -0
- data/ext/datadog_ci_native/datadog_source_code.h +3 -0
- data/ext/{datadog_cov → datadog_ci_native}/extconf.rb +1 -1
- data/lib/datadog/ci/codeowners/rule.rb +5 -0
- data/lib/datadog/ci/configuration/components.rb +17 -5
- data/lib/datadog/ci/configuration/settings.rb +6 -0
- data/lib/datadog/ci/contrib/knapsack/patcher.rb +1 -3
- data/lib/datadog/ci/contrib/knapsack/runner.rb +2 -0
- data/lib/datadog/ci/contrib/minitest/runner.rb +1 -0
- data/lib/datadog/ci/contrib/minitest/test.rb +24 -9
- data/lib/datadog/ci/contrib/parallel_tests/patcher.rb +1 -3
- data/lib/datadog/ci/contrib/patcher.rb +4 -0
- data/lib/datadog/ci/contrib/rspec/example.rb +14 -7
- data/lib/datadog/ci/contrib/rspec/helpers.rb +1 -3
- data/lib/datadog/ci/ext/environment/extractor.rb +4 -6
- data/lib/datadog/ci/ext/environment/providers/appveyor.rb +5 -0
- data/lib/datadog/ci/ext/environment/providers/base.rb +7 -2
- data/lib/datadog/ci/ext/environment/providers/bitbucket.rb +6 -0
- data/lib/datadog/ci/ext/environment/providers/bitrise.rb +7 -1
- data/lib/datadog/ci/ext/environment/providers/buddy.rb +5 -0
- data/lib/datadog/ci/ext/environment/providers/github_actions.rb +37 -18
- data/lib/datadog/ci/ext/environment/providers/gitlab.rb +13 -1
- data/lib/datadog/ci/ext/environment/providers/user_defined_tags.rb +12 -0
- data/lib/datadog/ci/ext/git.rb +3 -0
- data/lib/datadog/ci/ext/settings.rb +1 -0
- data/lib/datadog/ci/ext/telemetry.rb +3 -0
- data/lib/datadog/ci/ext/test.rb +5 -1
- data/lib/datadog/ci/ext/transport.rb +1 -0
- data/lib/datadog/ci/git/base_branch_sha_detection/base.rb +66 -0
- data/lib/datadog/ci/git/base_branch_sha_detection/branch_metric.rb +34 -0
- data/lib/datadog/ci/git/base_branch_sha_detection/guesser.rb +137 -0
- data/lib/datadog/ci/git/base_branch_sha_detection/merge_base_extractor.rb +29 -0
- data/lib/datadog/ci/git/base_branch_sha_detector.rb +63 -0
- data/lib/datadog/ci/git/cli.rb +56 -0
- data/lib/datadog/ci/git/local_repository.rb +131 -118
- data/lib/datadog/ci/git/telemetry.rb +14 -0
- data/lib/datadog/ci/git/tree_uploader.rb +10 -3
- data/lib/datadog/ci/impacted_tests_detection/component.rb +81 -0
- data/lib/datadog/ci/remote/component.rb +6 -1
- data/lib/datadog/ci/remote/library_settings.rb +8 -0
- data/lib/datadog/ci/span.rb +7 -0
- data/lib/datadog/ci/test.rb +6 -0
- data/lib/datadog/ci/test_management/tests_properties.rb +2 -1
- data/lib/datadog/ci/test_optimisation/component.rb +10 -6
- data/lib/datadog/ci/test_optimisation/coverage/ddcov.rb +1 -1
- data/lib/datadog/ci/test_retries/component.rb +8 -17
- data/lib/datadog/ci/test_retries/driver/{retry_new.rb → retry_flake_detection.rb} +1 -1
- data/lib/datadog/ci/test_retries/strategy/{retry_new.rb → retry_flake_detection.rb} +4 -4
- data/lib/datadog/ci/test_visibility/component.rb +6 -0
- data/lib/datadog/ci/test_visibility/telemetry.rb +3 -0
- data/lib/datadog/ci/utils/command.rb +116 -0
- data/lib/datadog/ci/utils/source_code.rb +31 -0
- data/lib/datadog/ci/version.rb +1 -1
- metadata +21 -8
@@ -4,6 +4,7 @@ require "datadog/core/telemetry/ext"
|
|
4
4
|
|
5
5
|
require_relative "../ext/settings"
|
6
6
|
require_relative "../git/tree_uploader"
|
7
|
+
require_relative "../impacted_tests_detection/component"
|
7
8
|
require_relative "../logs/component"
|
8
9
|
require_relative "../logs/transport"
|
9
10
|
require_relative "../remote/component"
|
@@ -36,7 +37,7 @@ module Datadog
|
|
36
37
|
# Adds CI behavior to Datadog trace components
|
37
38
|
module Components
|
38
39
|
attr_reader :test_visibility, :test_optimisation, :git_tree_upload_worker, :ci_remote, :test_retries,
|
39
|
-
:test_management, :agentless_logs_submission
|
40
|
+
:test_management, :agentless_logs_submission, :impacted_tests_detection
|
40
41
|
|
41
42
|
def initialize(settings)
|
42
43
|
@test_optimisation = nil
|
@@ -45,6 +46,7 @@ module Datadog
|
|
45
46
|
@ci_remote = nil
|
46
47
|
@test_retries = TestRetries::NullComponent.new
|
47
48
|
@test_management = TestManagement::NullComponent.new
|
49
|
+
@impacted_tests_detection = nil
|
48
50
|
|
49
51
|
# Activate CI mode if enabled
|
50
52
|
if settings.ci.enabled
|
@@ -138,6 +140,8 @@ module Datadog
|
|
138
140
|
)
|
139
141
|
|
140
142
|
@agentless_logs_submission = build_agentless_logs_component(settings, test_visibility_api)
|
143
|
+
|
144
|
+
@impacted_tests_detection = ImpactedTestsDetection::Component.new(enabled: settings.ci.impacted_tests_detection_enabled)
|
141
145
|
end
|
142
146
|
|
143
147
|
def build_test_optimisation(settings, test_visibility_api)
|
@@ -237,7 +241,7 @@ module Datadog
|
|
237
241
|
|
238
242
|
def build_git_upload_worker(settings, api)
|
239
243
|
if settings.ci.git_metadata_upload_enabled
|
240
|
-
git_tree_uploader = Git::TreeUploader.new(api: api)
|
244
|
+
git_tree_uploader = Git::TreeUploader.new(api: api, force_unshallow: settings.ci.impacted_tests_detection_enabled)
|
241
245
|
Worker.new do |repository_url|
|
242
246
|
git_tree_uploader.call(repository_url)
|
243
247
|
end
|
@@ -323,13 +327,21 @@ module Datadog
|
|
323
327
|
settings.telemetry.shutdown_timeout_seconds = 60.0
|
324
328
|
|
325
329
|
begin
|
326
|
-
require "datadog/core/
|
330
|
+
require "datadog/core/transport/http/adapters/net"
|
327
331
|
|
328
|
-
# patch gem's
|
329
|
-
Core::
|
332
|
+
# patch gem's core transport layer to use Net::HTTP instead of WebMock's Net::HTTP
|
333
|
+
Core::Transport::HTTP::Adapters::Net.include(CI::Transport::Adapters::TelemetryWebmockSafeAdapter)
|
330
334
|
rescue LoadError, StandardError => e
|
331
335
|
Datadog.logger.warn("Failed to patch Datadog gem's telemetry layer: #{e}")
|
332
336
|
end
|
337
|
+
|
338
|
+
# for compatibility with old telemetry transport
|
339
|
+
begin
|
340
|
+
require "datadog/core/telemetry/http/adapters/net"
|
341
|
+
Core::Telemetry::Http::Adapters::Net.include(CI::Transport::Adapters::TelemetryWebmockSafeAdapter)
|
342
|
+
rescue LoadError, StandardError => e
|
343
|
+
Datadog.logger.debug("The old telemetry transport layer is not available: #{e}")
|
344
|
+
end
|
333
345
|
end
|
334
346
|
|
335
347
|
# When timecop is present:
|
@@ -151,6 +151,12 @@ module Datadog
|
|
151
151
|
o.env CI::Ext::Settings::ENV_TEST_VISIBILITY_DRB_SERVER_URI
|
152
152
|
end
|
153
153
|
|
154
|
+
option :impacted_tests_detection_enabled do |o|
|
155
|
+
o.type :bool
|
156
|
+
o.env CI::Ext::Settings::ENV_IMPACTED_TESTS_DETECTION_ENABLED
|
157
|
+
o.default false
|
158
|
+
end
|
159
|
+
|
154
160
|
define_method(:instrument) do |integration_name, options = {}, &block|
|
155
161
|
return unless enabled
|
156
162
|
|
@@ -9,9 +9,7 @@ module Datadog
|
|
9
9
|
module Patcher
|
10
10
|
include Datadog::CI::Contrib::Patcher
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
def patch
|
12
|
+
def self.patch
|
15
13
|
if ::RSpec::Core::Runner.ancestors.include?(::KnapsackPro::Extensions::RSpecExtension::Runner)
|
16
14
|
# knapsack already patched rspec runner
|
17
15
|
require_relative "runner"
|
@@ -19,6 +19,7 @@ module Datadog
|
|
19
19
|
return super if ::RSpec.configuration.dry_run? && !datadog_configuration[:dry_run_enabled]
|
20
20
|
return super unless datadog_configuration[:enabled]
|
21
21
|
|
22
|
+
# @type var test_session: Datadog::CI::TestSession?
|
22
23
|
test_session = test_visibility_component.start_test_session(
|
23
24
|
tags: {
|
24
25
|
CI::Ext::Test::TAG_FRAMEWORK => CI::Contrib::RSpec::Ext::FRAMEWORK,
|
@@ -27,6 +28,7 @@ module Datadog
|
|
27
28
|
service: datadog_configuration[:service_name]
|
28
29
|
)
|
29
30
|
|
31
|
+
# @type var test_module: Datadog::CI::TestModule?
|
30
32
|
test_module = test_visibility_component.start_test_module(CI::Contrib::RSpec::Ext::FRAMEWORK)
|
31
33
|
|
32
34
|
result = super
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative "../../ext/test"
|
4
4
|
require_relative "../../git/local_repository"
|
5
|
+
require_relative "../../utils/source_code"
|
5
6
|
require_relative "../instrumentation"
|
6
7
|
require_relative "ext"
|
7
8
|
require_relative "helpers"
|
@@ -27,20 +28,33 @@ module Datadog
|
|
27
28
|
end
|
28
29
|
|
29
30
|
test_suite_name = Helpers.test_suite_name(self.class, name)
|
30
|
-
|
31
|
+
|
32
|
+
# @type var tags : Hash[String, String]
|
33
|
+
tags = {
|
34
|
+
CI::Ext::Test::TAG_FRAMEWORK => Ext::FRAMEWORK,
|
35
|
+
CI::Ext::Test::TAG_FRAMEWORK_VERSION => datadog_integration.version.to_s
|
36
|
+
}
|
37
|
+
|
38
|
+
# try to find out where test method starts and ends
|
39
|
+
test_method = method(name)
|
40
|
+
source_file, first_line_number = test_method.source_location
|
41
|
+
last_line_number = Utils::SourceCode.last_line(test_method)
|
42
|
+
|
43
|
+
tags[CI::Ext::Test::TAG_SOURCE_FILE] = Git::LocalRepository.relative_to_root(source_file) if source_file
|
44
|
+
tags[CI::Ext::Test::TAG_SOURCE_START] = first_line_number.to_s if first_line_number
|
45
|
+
tags[CI::Ext::Test::TAG_SOURCE_END] = last_line_number.to_s if last_line_number
|
31
46
|
|
32
47
|
test_span = test_visibility_component.trace_test(
|
33
48
|
name,
|
34
49
|
test_suite_name,
|
35
|
-
tags:
|
36
|
-
CI::Ext::Test::TAG_FRAMEWORK => Ext::FRAMEWORK,
|
37
|
-
CI::Ext::Test::TAG_FRAMEWORK_VERSION => datadog_integration.version.to_s,
|
38
|
-
CI::Ext::Test::TAG_SOURCE_FILE => Git::LocalRepository.relative_to_root(source_file),
|
39
|
-
CI::Ext::Test::TAG_SOURCE_START => line_number.to_s
|
40
|
-
},
|
50
|
+
tags: tags,
|
41
51
|
service: datadog_configuration[:service_name]
|
42
52
|
)
|
53
|
+
# Steep type checker doesn't know that we patched Minitest::Test class definition
|
54
|
+
#
|
55
|
+
# steep:ignore:start
|
43
56
|
test_span&.itr_unskippable! if self.class.dd_suite_unskippable? || self.class.dd_test_unskippable?(name)
|
57
|
+
# steep:ignore:end
|
44
58
|
skip(test_span&.datadog_skip_reason) if test_span&.should_skip?
|
45
59
|
end
|
46
60
|
|
@@ -100,9 +114,10 @@ module Datadog
|
|
100
114
|
end
|
101
115
|
|
102
116
|
def dd_test_unskippable?(test_name)
|
103
|
-
|
117
|
+
tests = @datadog_itr_unskippable_tests
|
118
|
+
return false unless tests
|
104
119
|
|
105
|
-
|
120
|
+
tests.include?(test_name)
|
106
121
|
end
|
107
122
|
end
|
108
123
|
end
|
@@ -29,9 +29,13 @@ module Datadog
|
|
29
29
|
return unless defined?(super)
|
30
30
|
|
31
31
|
patch_only_once.run do
|
32
|
+
# There is no way to tell Steep that we prepend these methods to modules that have .patch method
|
33
|
+
#
|
34
|
+
# steep:ignore:start
|
32
35
|
super.tap do
|
33
36
|
@patch_successful = true
|
34
37
|
end
|
38
|
+
# steep:ignore:end
|
35
39
|
rescue => e
|
36
40
|
on_patch_error(e)
|
37
41
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative "../../ext/test"
|
4
4
|
require_relative "../../git/local_repository"
|
5
|
+
require_relative "../../utils/source_code"
|
5
6
|
require_relative "../../utils/test_run"
|
6
7
|
require_relative "../instrumentation"
|
7
8
|
require_relative "ext"
|
@@ -26,17 +27,23 @@ module Datadog
|
|
26
27
|
# don't report test to RSpec::Core::Reporter until retries are done
|
27
28
|
@skip_reporting = true
|
28
29
|
|
30
|
+
# @type var tags : Hash[String, String]
|
31
|
+
tags = {
|
32
|
+
CI::Ext::Test::TAG_FRAMEWORK => Ext::FRAMEWORK,
|
33
|
+
CI::Ext::Test::TAG_FRAMEWORK_VERSION => datadog_integration.version.to_s,
|
34
|
+
CI::Ext::Test::TAG_SOURCE_FILE => Git::LocalRepository.relative_to_root(metadata[:file_path]),
|
35
|
+
CI::Ext::Test::TAG_SOURCE_START => metadata[:line_number].to_s,
|
36
|
+
CI::Ext::Test::TAG_PARAMETERS => datadog_test_parameters
|
37
|
+
}
|
38
|
+
|
39
|
+
end_line = Utils::SourceCode.last_line(@example_block)
|
40
|
+
tags[CI::Ext::Test::TAG_SOURCE_END] = end_line.to_s if end_line
|
41
|
+
|
29
42
|
test_retries_component.with_retries do
|
30
43
|
test_visibility_component.trace_test(
|
31
44
|
datadog_test_name,
|
32
45
|
datadog_test_suite_name,
|
33
|
-
tags:
|
34
|
-
CI::Ext::Test::TAG_FRAMEWORK => Ext::FRAMEWORK,
|
35
|
-
CI::Ext::Test::TAG_FRAMEWORK_VERSION => datadog_integration.version.to_s,
|
36
|
-
CI::Ext::Test::TAG_SOURCE_FILE => Git::LocalRepository.relative_to_root(metadata[:file_path]),
|
37
|
-
CI::Ext::Test::TAG_SOURCE_START => metadata[:line_number].to_s,
|
38
|
-
CI::Ext::Test::TAG_PARAMETERS => datadog_test_parameters
|
39
|
-
},
|
46
|
+
tags: tags,
|
40
47
|
service: datadog_configuration[:service_name]
|
41
48
|
) do |test_span|
|
42
49
|
test_span&.itr_unskippable! if datadog_unskippable?
|
@@ -46,14 +46,12 @@ module Datadog
|
|
46
46
|
Git::TAG_COMMIT_COMMITTER_EMAIL => @provider.git_commit_committer_email,
|
47
47
|
Git::TAG_COMMIT_COMMITTER_NAME => @provider.git_commit_committer_name,
|
48
48
|
Git::TAG_COMMIT_MESSAGE => @provider.git_commit_message,
|
49
|
-
Git::TAG_COMMIT_SHA => @provider.git_commit_sha
|
49
|
+
Git::TAG_COMMIT_SHA => @provider.git_commit_sha,
|
50
|
+
Git::TAG_PULL_REQUEST_BASE_BRANCH => @provider.git_pull_request_base_branch,
|
51
|
+
Git::TAG_PULL_REQUEST_BASE_BRANCH_SHA => @provider.git_pull_request_base_branch_sha,
|
52
|
+
Git::TAG_COMMIT_HEAD_SHA => @provider.git_commit_head_sha
|
50
53
|
}
|
51
54
|
|
52
|
-
# set additional tags if provider needs them
|
53
|
-
@provider.additional_tags.each do |key, value|
|
54
|
-
@tags[key] = value
|
55
|
-
end
|
56
|
-
|
57
55
|
# Normalize Git references and filter sensitive data
|
58
56
|
normalize_git!
|
59
57
|
# Expand ~
|
@@ -83,6 +83,11 @@ module Datadog
|
|
83
83
|
commit_message
|
84
84
|
end
|
85
85
|
|
86
|
+
def git_pull_request_base_branch
|
87
|
+
# from docs: build branch. For Pull Request commits it is base branch PR is merging into
|
88
|
+
env["APPVEYOR_REPO_BRANCH"]
|
89
|
+
end
|
90
|
+
|
86
91
|
private
|
87
92
|
|
88
93
|
def github_repo_provider?
|
@@ -59,6 +59,12 @@ module Datadog
|
|
59
59
|
env["BITBUCKET_TAG"]
|
60
60
|
end
|
61
61
|
|
62
|
+
def git_pull_request_base_branch
|
63
|
+
# from docs: The pull request destination branch (used in combination with BITBUCKET_BRANCH).
|
64
|
+
# Only available on a pull request triggered build
|
65
|
+
env["BITBUCKET_PR_DESTINATION_BRANCH"]
|
66
|
+
end
|
67
|
+
|
62
68
|
private
|
63
69
|
|
64
70
|
def url
|
@@ -47,7 +47,7 @@ module Datadog
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def git_branch
|
50
|
-
env["
|
50
|
+
env["BITRISEIO_PULL_REQUEST_HEAD_BRANCH"] || env["BITRISE_GIT_BRANCH"]
|
51
51
|
end
|
52
52
|
|
53
53
|
def git_tag
|
@@ -73,6 +73,12 @@ module Datadog
|
|
73
73
|
def git_commit_committer_email
|
74
74
|
env["GIT_CLONE_COMMIT_COMMITER_EMAIL"] || env["GIT_CLONE_COMMIT_COMMITER_NAME"]
|
75
75
|
end
|
76
|
+
|
77
|
+
def git_pull_request_base_branch
|
78
|
+
# from docs: Used only with builds triggered by pull requests: the destination/target branch of the pull request that triggered the build.
|
79
|
+
# For example, a pull request wants to merge the content of a branch into the branch main. In this case, this Env Var’s value is main.
|
80
|
+
env["BITRISEIO_GIT_BRANCH_DEST"]
|
81
|
+
end
|
76
82
|
end
|
77
83
|
end
|
78
84
|
end
|
@@ -65,6 +65,11 @@ module Datadog
|
|
65
65
|
def git_commit_committer_email
|
66
66
|
env["BUDDY_EXECUTION_REVISION_COMMITTER_EMAIL"]
|
67
67
|
end
|
68
|
+
|
69
|
+
def git_pull_request_base_branch
|
70
|
+
# from docs: The name of the Git BASE branch of the currently run Pull Request
|
71
|
+
env["BUDDY_RUN_PR_BASE_BRANCH"]
|
72
|
+
end
|
68
73
|
end
|
69
74
|
end
|
70
75
|
end
|
@@ -76,34 +76,53 @@ module Datadog
|
|
76
76
|
}.reject { |_, v| v.nil? }.to_json
|
77
77
|
end
|
78
78
|
|
79
|
-
def
|
80
|
-
|
81
|
-
return {} if base_ref.nil? || base_ref.empty?
|
79
|
+
def git_pull_request_base_branch
|
80
|
+
return nil if github_event_json.nil?
|
82
81
|
|
83
|
-
|
84
|
-
|
85
|
-
Git::TAG_PULL_REQUEST_BASE_BRANCH => base_ref
|
86
|
-
}
|
87
|
-
|
88
|
-
event_path = env["GITHUB_EVENT_PATH"]
|
89
|
-
event_json = JSON.parse(File.read(event_path))
|
82
|
+
env["GITHUB_BASE_REF"]
|
83
|
+
end
|
90
84
|
|
91
|
-
|
92
|
-
|
85
|
+
def git_pull_request_base_branch_sha
|
86
|
+
return nil if git_pull_request_base_branch.nil?
|
93
87
|
|
94
|
-
|
95
|
-
|
88
|
+
event_json = github_event_json
|
89
|
+
return nil if event_json.nil?
|
96
90
|
|
97
|
-
|
91
|
+
event_json.dig("pull_request", "base", "sha")
|
98
92
|
rescue => e
|
99
|
-
Datadog.logger.error("Failed to extract
|
100
|
-
Core::Telemetry::Logger.report(e, description: "Failed to extract
|
93
|
+
Datadog.logger.error("Failed to extract pull request base branch SHA from GitHub Actions: #{e}")
|
94
|
+
Core::Telemetry::Logger.report(e, description: "Failed to extract pull request base branch SHA from GitHub Actions")
|
95
|
+
nil
|
96
|
+
end
|
101
97
|
|
102
|
-
|
98
|
+
def git_commit_head_sha
|
99
|
+
return nil if git_pull_request_base_branch.nil?
|
100
|
+
|
101
|
+
event_json = github_event_json
|
102
|
+
return nil if event_json.nil?
|
103
|
+
|
104
|
+
event_json.dig("pull_request", "head", "sha")
|
105
|
+
rescue => e
|
106
|
+
Datadog.logger.error("Failed to extract commit head SHA from GitHub Actions: #{e}")
|
107
|
+
Core::Telemetry::Logger.report(e, description: "Failed to extract commit head SHA from GitHub Actions")
|
108
|
+
nil
|
103
109
|
end
|
104
110
|
|
105
111
|
private
|
106
112
|
|
113
|
+
def github_event_json
|
114
|
+
return @github_event_json if defined?(@github_event_json)
|
115
|
+
|
116
|
+
event_path = env["GITHUB_EVENT_PATH"]
|
117
|
+
return @github_event_json = nil if event_path.nil? || event_path.empty?
|
118
|
+
|
119
|
+
@github_event_json = JSON.parse(File.read(event_path))
|
120
|
+
rescue => e
|
121
|
+
Datadog.logger.error("Failed to parse GitHub event JSON: #{e}")
|
122
|
+
Core::Telemetry::Logger.report(e, description: "Failed to parse GitHub event JSON")
|
123
|
+
@github_event_json = nil
|
124
|
+
end
|
125
|
+
|
107
126
|
def github_server_url
|
108
127
|
return @github_server_url if defined?(@github_server_url)
|
109
128
|
|
@@ -100,13 +100,25 @@ module Datadog
|
|
100
100
|
}.to_json
|
101
101
|
end
|
102
102
|
|
103
|
+
def git_pull_request_base_branch
|
104
|
+
env["CI_MERGE_REQUEST_TARGET_BRANCH_NAME"]
|
105
|
+
end
|
106
|
+
|
107
|
+
def git_pull_request_base_branch_sha
|
108
|
+
env["CI_MERGE_REQUEST_TARGET_BRANCH_SHA"]
|
109
|
+
end
|
110
|
+
|
111
|
+
def git_commit_head_sha
|
112
|
+
env["CI_MERGE_REQUEST_SOURCE_BRANCH_SHA"]
|
113
|
+
end
|
114
|
+
|
103
115
|
private
|
104
116
|
|
105
117
|
def extract_name_email
|
106
118
|
return @name_email_tuple if defined?(@name_email_tuple)
|
107
119
|
|
108
120
|
name_and_email_string = env["CI_COMMIT_AUTHOR"]
|
109
|
-
if name_and_email_string
|
121
|
+
if name_and_email_string&.include?("<") && (match = /^([^<]*)<([^>]*)>$/.match(name_and_email_string))
|
110
122
|
name = match[1]
|
111
123
|
name = name.strip if name
|
112
124
|
email = match[2]
|
@@ -54,6 +54,18 @@ module Datadog
|
|
54
54
|
def git_commit_committer_date
|
55
55
|
env[Git::ENV_COMMIT_COMMITTER_DATE]
|
56
56
|
end
|
57
|
+
|
58
|
+
def git_pull_request_base_branch
|
59
|
+
env[Git::ENV_PULL_REQUEST_BASE_BRANCH]
|
60
|
+
end
|
61
|
+
|
62
|
+
def git_pull_request_base_branch_sha
|
63
|
+
env[Git::ENV_PULL_REQUEST_BASE_BRANCH_SHA]
|
64
|
+
end
|
65
|
+
|
66
|
+
def git_commit_head_sha
|
67
|
+
env[Git::ENV_COMMIT_HEAD_SHA]
|
68
|
+
end
|
57
69
|
end
|
58
70
|
end
|
59
71
|
end
|
data/lib/datadog/ci/ext/git.rb
CHANGED
@@ -36,6 +36,9 @@ module Datadog
|
|
36
36
|
ENV_COMMIT_COMMITTER_NAME = "DD_GIT_COMMIT_COMMITTER_NAME"
|
37
37
|
ENV_COMMIT_COMMITTER_EMAIL = "DD_GIT_COMMIT_COMMITTER_EMAIL"
|
38
38
|
ENV_COMMIT_COMMITTER_DATE = "DD_GIT_COMMIT_COMMITTER_DATE"
|
39
|
+
ENV_PULL_REQUEST_BASE_BRANCH = "DD_GIT_PULL_REQUEST_BASE_BRANCH"
|
40
|
+
ENV_PULL_REQUEST_BASE_BRANCH_SHA = "DD_GIT_PULL_REQUEST_BASE_BRANCH_SHA"
|
41
|
+
ENV_COMMIT_HEAD_SHA = "DD_GIT_COMMIT_HEAD_SHA"
|
39
42
|
end
|
40
43
|
end
|
41
44
|
end
|
@@ -25,6 +25,7 @@ module Datadog
|
|
25
25
|
ENV_TEST_VISIBILITY_DRB_SERVER_URI = "DD_TEST_VISIBILITY_DRB_SERVER_URI"
|
26
26
|
ENV_AGENTLESS_LOGS_SUBMISSION_ENABLED = "DD_AGENTLESS_LOG_SUBMISSION_ENABLED"
|
27
27
|
ENV_AGENTLESS_LOGS_SUBMISSION_URL = "DD_AGENTLESS_LOG_SUBMISSION_URL"
|
28
|
+
ENV_IMPACTED_TESTS_DETECTION_ENABLED = "DD_CIVISIBILITY_IMPACTED_TESTS_DETECTION_ENABLED"
|
28
29
|
|
29
30
|
# Source: https://docs.datadoghq.com/getting_started/site/
|
30
31
|
DD_SITE_ALLOWLIST = %w[
|
@@ -90,6 +90,7 @@ module Datadog
|
|
90
90
|
TAG_IS_QUARANTINED = "is_quarantined"
|
91
91
|
TAG_IS_TEST_DISABLED = "is_disabled"
|
92
92
|
TAG_HAS_FAILED_ALL_RETRIES = "has_failed_all_retries"
|
93
|
+
TAG_IS_MODIFIED = "is_modified"
|
93
94
|
# tags for git_requests.settings_response metric
|
94
95
|
TAG_COVERAGE_ENABLED = "coverage_enabled"
|
95
96
|
TAG_ITR_ENABLED = "itr_enabled"
|
@@ -138,6 +139,8 @@ module Datadog
|
|
138
139
|
GET_LOCAL_COMMITS = "get_local_commits"
|
139
140
|
GET_OBJECTS = "get_objects"
|
140
141
|
PACK_OBJECTS = "pack_objects"
|
142
|
+
DIFF = "diff"
|
143
|
+
BASE_COMMIT_SHA = "base_commit_sha"
|
141
144
|
end
|
142
145
|
|
143
146
|
module Provider
|
data/lib/datadog/ci/ext/test.rb
CHANGED
@@ -20,6 +20,7 @@ module Datadog
|
|
20
20
|
TAG_COMMAND = "test.command"
|
21
21
|
TAG_SOURCE_FILE = "test.source.file"
|
22
22
|
TAG_SOURCE_START = "test.source.start"
|
23
|
+
TAG_SOURCE_END = "test.source.end"
|
23
24
|
TAG_CODEOWNERS = "test.codeowners"
|
24
25
|
TAG_PARAMETERS = "test.parameters"
|
25
26
|
|
@@ -94,7 +95,7 @@ module Datadog
|
|
94
95
|
AUTO_TEST_RETRIES_VERSION = "1"
|
95
96
|
TEST_MANAGEMENT_QUARANTINE_VERSION = "1"
|
96
97
|
TEST_MANAGEMENT_DISABLE_VERSION = "1"
|
97
|
-
TEST_MANAGEMENT_ATTEMPT_TO_FIX_VERSION = "
|
98
|
+
TEST_MANAGEMENT_ATTEMPT_TO_FIX_VERSION = "4"
|
98
99
|
end
|
99
100
|
|
100
101
|
# Map of capabilities to their versions
|
@@ -108,6 +109,9 @@ module Datadog
|
|
108
109
|
}.freeze
|
109
110
|
end
|
110
111
|
|
112
|
+
# impacted tests detection
|
113
|
+
TAG_TEST_IS_MODIFIED = "test.is_modified"
|
114
|
+
|
111
115
|
# internal APM tag to mark a span as a test span
|
112
116
|
TAG_SPAN_KIND = "span.kind"
|
113
117
|
SPAN_KIND_TEST = "test"
|
@@ -48,6 +48,7 @@ module Datadog
|
|
48
48
|
DD_API_SETTINGS_RESPONSE_TEST_MANAGEMENT_KEY = "test_management"
|
49
49
|
DD_API_SETTINGS_RESPONSE_ATTEMPT_TO_FIX_RETRIES_KEY = "attempt_to_fix_retries"
|
50
50
|
DD_API_SETTINGS_RESPONSE_DEFAULT = {DD_API_SETTINGS_RESPONSE_ITR_ENABLED_KEY => false}.freeze
|
51
|
+
DD_API_SETTINGS_RESPONSE_IMPACTED_TESTS_ENABLED_KEY = "impacted_tests_enabled"
|
51
52
|
|
52
53
|
DD_API_GIT_SEARCH_COMMITS_PATH = "/api/v2/git/repository/search_commits"
|
53
54
|
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../cli"
|
4
|
+
|
5
|
+
module Datadog
|
6
|
+
module CI
|
7
|
+
module Git
|
8
|
+
module BaseBranchShaDetection
|
9
|
+
class Base
|
10
|
+
attr_reader :remote_name
|
11
|
+
attr_reader :source_branch
|
12
|
+
|
13
|
+
def initialize(remote_name, source_branch)
|
14
|
+
@remote_name = remote_name
|
15
|
+
@source_branch = source_branch
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
raise NotImplementedError, "Subclasses must implement #call"
|
20
|
+
end
|
21
|
+
|
22
|
+
protected
|
23
|
+
|
24
|
+
def merge_base_sha(branch, source_branch)
|
25
|
+
CLI.exec_git_command(["merge-base", branch, source_branch], timeout: CLI::LONG_TIMEOUT)&.strip
|
26
|
+
rescue CLI::GitCommandExecutionError => e
|
27
|
+
Datadog.logger.debug { "Merge base calculation failed for branches '#{branch}' and '#{source_branch}': #{e}" }
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def check_and_fetch_branch(branch, remote_name)
|
32
|
+
# @type var short_branch_name: String
|
33
|
+
short_branch_name = remove_remote_prefix(branch, remote_name)
|
34
|
+
|
35
|
+
# Check if branch already fetched from remote
|
36
|
+
CLI.exec_git_command(["show-ref", "--verify", "--quiet", "refs/remotes/#{remote_name}/#{short_branch_name}"])
|
37
|
+
Datadog.logger.debug { "Branch '#{remote_name}/#{short_branch_name}' already fetched from remote, skipping" }
|
38
|
+
rescue CLI::GitCommandExecutionError => e
|
39
|
+
Datadog.logger.debug { "Branch '#{remote_name}/#{short_branch_name}' doesn't exist locally, checking remote..." }
|
40
|
+
|
41
|
+
begin
|
42
|
+
remote_heads = CLI.exec_git_command(["ls-remote", "--heads", remote_name, short_branch_name])
|
43
|
+
if remote_heads.nil? || remote_heads.empty?
|
44
|
+
Datadog.logger.debug { "Branch '#{remote_name}/#{short_branch_name}' doesn't exist in remote" }
|
45
|
+
return
|
46
|
+
end
|
47
|
+
|
48
|
+
Datadog.logger.debug { "Branch '#{remote_name}/#{short_branch_name}' exists in remote, fetching" }
|
49
|
+
CLI.exec_git_command(["fetch", "--depth", "1", remote_name, short_branch_name], timeout: CLI::LONG_TIMEOUT)
|
50
|
+
rescue CLI::GitCommandExecutionError => e
|
51
|
+
Datadog.logger.debug { "Branch '#{remote_name}/#{short_branch_name}' couldn't be fetched from remote: #{e}" }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def remove_remote_prefix(branch_name, remote_name)
|
56
|
+
branch_name&.sub(/^#{Regexp.escape(remote_name)}\//, "")
|
57
|
+
end
|
58
|
+
|
59
|
+
def branches_equal?(branch_name, default_branch, remote_name)
|
60
|
+
remove_remote_prefix(branch_name, remote_name) == remove_remote_prefix(default_branch, remote_name)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module CI
|
5
|
+
module Git
|
6
|
+
module BaseBranchShaDetection
|
7
|
+
# Represents metrics for a git branch comparison
|
8
|
+
# including how far behind/ahead it is from a source branch
|
9
|
+
# and the common base commit SHA
|
10
|
+
class BranchMetric
|
11
|
+
attr_reader :branch_name, :behind, :ahead, :base_sha
|
12
|
+
|
13
|
+
def initialize(branch_name:, behind:, ahead:, base_sha:)
|
14
|
+
@branch_name = branch_name
|
15
|
+
@behind = behind
|
16
|
+
@ahead = ahead
|
17
|
+
@base_sha = base_sha
|
18
|
+
end
|
19
|
+
|
20
|
+
# Checks if the branch is up to date with the source branch
|
21
|
+
def up_to_date?
|
22
|
+
@behind == 0 && @ahead == 0
|
23
|
+
end
|
24
|
+
|
25
|
+
# Used for comparison when finding the best branch
|
26
|
+
# Lower divergence score is better
|
27
|
+
def divergence_score
|
28
|
+
@ahead
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|