datadog-ci 1.26.0 → 1.27.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 +32 -2
- data/lib/datadog/ci/code_coverage/component.rb +55 -0
- data/lib/datadog/ci/code_coverage/null_component.rb +24 -0
- data/lib/datadog/ci/code_coverage/transport.rb +66 -0
- data/lib/datadog/ci/configuration/components.rb +17 -1
- data/lib/datadog/ci/configuration/settings.rb +20 -2
- data/lib/datadog/ci/contrib/minitest/test.rb +1 -1
- data/lib/datadog/ci/contrib/rspec/example.rb +48 -8
- data/lib/datadog/ci/contrib/rspec/example_group.rb +63 -31
- data/lib/datadog/ci/contrib/simplecov/ext.rb +2 -0
- data/lib/datadog/ci/contrib/simplecov/patcher.rb +2 -0
- data/lib/datadog/ci/contrib/simplecov/report_uploader.rb +59 -0
- data/lib/datadog/ci/ext/environment/providers/github_actions.rb +65 -2
- data/lib/datadog/ci/ext/environment.rb +10 -0
- data/lib/datadog/ci/ext/settings.rb +2 -0
- data/lib/datadog/ci/ext/telemetry.rb +5 -0
- data/lib/datadog/ci/ext/test.rb +0 -5
- data/lib/datadog/ci/ext/transport.rb +4 -0
- data/lib/datadog/ci/git/cli.rb +59 -1
- data/lib/datadog/ci/remote/component.rb +6 -1
- data/lib/datadog/ci/remote/library_settings.rb +8 -0
- data/lib/datadog/ci/test.rb +27 -18
- data/lib/datadog/ci/test_management/component.rb +9 -0
- data/lib/datadog/ci/test_management/null_component.rb +8 -0
- data/lib/datadog/ci/test_optimisation/component.rb +168 -16
- data/lib/datadog/ci/test_optimisation/null_component.rb +19 -5
- data/lib/datadog/ci/test_suite.rb +16 -21
- data/lib/datadog/ci/test_visibility/component.rb +1 -2
- data/lib/datadog/ci/test_visibility/known_tests.rb +59 -6
- data/lib/datadog/ci/transport/api/agentless.rb +8 -1
- data/lib/datadog/ci/transport/api/base.rb +21 -0
- data/lib/datadog/ci/transport/api/builder.rb +5 -1
- data/lib/datadog/ci/transport/api/evp_proxy.rb +8 -0
- data/lib/datadog/ci/version.rb +1 -1
- metadata +5 -1
|
@@ -19,6 +19,10 @@ module Datadog
|
|
|
19
19
|
# counts how many times every test in this suite was executed with each status:
|
|
20
20
|
# { "MySuite.mytest.a:1" => { "pass" => 3, "fail" => 2 } }
|
|
21
21
|
@execution_stats_per_test = {}
|
|
22
|
+
|
|
23
|
+
# tracks final status for each test (the status that is reported after all retries):
|
|
24
|
+
# { "MySuite.mytest.a:1" => "pass" }
|
|
25
|
+
@final_statuses_per_test = {}
|
|
22
26
|
end
|
|
23
27
|
|
|
24
28
|
# Finishes this test suite.
|
|
@@ -42,6 +46,13 @@ module Datadog
|
|
|
42
46
|
end
|
|
43
47
|
end
|
|
44
48
|
|
|
49
|
+
# @internal
|
|
50
|
+
def record_test_final_status(test_id, final_status)
|
|
51
|
+
synchronize do
|
|
52
|
+
@final_statuses_per_test[test_id] = final_status
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
45
56
|
# @internal
|
|
46
57
|
def any_passed?
|
|
47
58
|
synchronize do
|
|
@@ -63,8 +74,7 @@ module Datadog
|
|
|
63
74
|
def all_executions_failed?(test_id)
|
|
64
75
|
synchronize do
|
|
65
76
|
stats = @execution_stats_per_test[test_id]
|
|
66
|
-
stats &&
|
|
67
|
-
stats[Ext::Test::Status::PASS] == 0
|
|
77
|
+
stats && stats[Ext::Test::Status::FAIL] > 0 && stats[Ext::Test::Status::PASS] == 0
|
|
68
78
|
end
|
|
69
79
|
end
|
|
70
80
|
|
|
@@ -72,8 +82,7 @@ module Datadog
|
|
|
72
82
|
def all_executions_passed?(test_id)
|
|
73
83
|
synchronize do
|
|
74
84
|
stats = @execution_stats_per_test[test_id]
|
|
75
|
-
stats && stats[Ext::Test::Status::PASS] > 0 && stats[Ext::Test::Status::FAIL] == 0
|
|
76
|
-
stats[Ext::Test::ExecutionStatsStatus::FAIL_IGNORED] == 0
|
|
85
|
+
stats && stats[Ext::Test::Status::PASS] > 0 && stats[Ext::Test::Status::FAIL] == 0
|
|
77
86
|
end
|
|
78
87
|
end
|
|
79
88
|
|
|
@@ -106,9 +115,9 @@ module Datadog
|
|
|
106
115
|
|
|
107
116
|
def set_status_from_stats!
|
|
108
117
|
synchronize do
|
|
109
|
-
# count how many tests
|
|
110
|
-
test_suite_stats = @
|
|
111
|
-
acc[
|
|
118
|
+
# count how many tests have each final status
|
|
119
|
+
test_suite_stats = @final_statuses_per_test.each_with_object(Hash.new(0)) do |(_test_id, final_status), acc|
|
|
120
|
+
acc[final_status] += 1
|
|
112
121
|
end
|
|
113
122
|
|
|
114
123
|
# test suite is considered failed if at least one test failed
|
|
@@ -123,20 +132,6 @@ module Datadog
|
|
|
123
132
|
end
|
|
124
133
|
end
|
|
125
134
|
end
|
|
126
|
-
|
|
127
|
-
def derive_test_status_from_execution_stats(test_execution_stats)
|
|
128
|
-
# test is passed if it passed at least once or it failed but fail was ignored
|
|
129
|
-
if test_execution_stats[Ext::Test::Status::PASS] > 0 ||
|
|
130
|
-
test_execution_stats[Ext::Test::ExecutionStatsStatus::FAIL_IGNORED] > 0
|
|
131
|
-
Ext::Test::Status::PASS
|
|
132
|
-
# if test was never passed, it is failed if it failed at least once
|
|
133
|
-
elsif test_execution_stats[Ext::Test::Status::FAIL] > 0
|
|
134
|
-
Ext::Test::Status::FAIL
|
|
135
|
-
# otherwise it is skipped
|
|
136
|
-
else
|
|
137
|
-
Ext::Test::Status::SKIP
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
135
|
end
|
|
141
136
|
end
|
|
142
137
|
end
|
|
@@ -302,7 +302,7 @@ module Datadog
|
|
|
302
302
|
test_management.tag_test_from_properties(test)
|
|
303
303
|
|
|
304
304
|
test_optimisation.mark_if_skippable(test)
|
|
305
|
-
test_optimisation.
|
|
305
|
+
test_optimisation.on_test_started(test)
|
|
306
306
|
|
|
307
307
|
test_retries.record_test_started(test)
|
|
308
308
|
end
|
|
@@ -328,7 +328,6 @@ module Datadog
|
|
|
328
328
|
end
|
|
329
329
|
|
|
330
330
|
def on_test_finished(test)
|
|
331
|
-
test_optimisation.stop_coverage(test)
|
|
332
331
|
test_optimisation.on_test_finished(test, maybe_remote_context)
|
|
333
332
|
|
|
334
333
|
validate_source_location(test)
|
|
@@ -45,6 +45,14 @@ module Datadog
|
|
|
45
45
|
res
|
|
46
46
|
end
|
|
47
47
|
|
|
48
|
+
def cursor
|
|
49
|
+
page_info.fetch("cursor", nil)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def has_next?
|
|
53
|
+
page_info.fetch("has_next", false)
|
|
54
|
+
end
|
|
55
|
+
|
|
48
56
|
private
|
|
49
57
|
|
|
50
58
|
def initialize(http_response, json)
|
|
@@ -66,6 +74,13 @@ module Datadog
|
|
|
66
74
|
@json = {}
|
|
67
75
|
end
|
|
68
76
|
end
|
|
77
|
+
|
|
78
|
+
def page_info
|
|
79
|
+
payload
|
|
80
|
+
.fetch("data", {})
|
|
81
|
+
.fetch("attributes", {})
|
|
82
|
+
.fetch("page_info", {})
|
|
83
|
+
end
|
|
69
84
|
end
|
|
70
85
|
|
|
71
86
|
def initialize(dd_env:, api: nil, config_tags: {})
|
|
@@ -78,8 +93,45 @@ module Datadog
|
|
|
78
93
|
api = @api
|
|
79
94
|
return Set.new unless api
|
|
80
95
|
|
|
81
|
-
|
|
82
|
-
|
|
96
|
+
result = Set.new
|
|
97
|
+
page_state = nil
|
|
98
|
+
page_number = 1
|
|
99
|
+
|
|
100
|
+
loop do
|
|
101
|
+
Datadog.logger.debug { "Fetching known tests page ##{page_number}#{" with cursor" if page_state}" }
|
|
102
|
+
|
|
103
|
+
response = fetch_page(api, test_session, page_state: page_state)
|
|
104
|
+
|
|
105
|
+
unless response.ok?
|
|
106
|
+
Datadog.logger.debug(
|
|
107
|
+
"Failed to fetch known tests page ##{page_number}, bailing out of known tests fetch. " \
|
|
108
|
+
"Early flake detection will not work."
|
|
109
|
+
)
|
|
110
|
+
return Set.new
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
page_tests = response.tests
|
|
114
|
+
result.merge(page_tests)
|
|
115
|
+
Datadog.logger.debug { "Received #{page_tests.size} known tests from page ##{page_number} (total so far: #{result.size})" }
|
|
116
|
+
|
|
117
|
+
unless response.has_next?
|
|
118
|
+
Datadog.logger.debug { "Stopping known tests fetch: no more pages after page ##{page_number}" }
|
|
119
|
+
break
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
page_state = response.cursor
|
|
123
|
+
page_number += 1
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
Datadog.logger.debug { "Finished fetching known tests: #{result.size} tests total from #{page_number} page(s)" }
|
|
127
|
+
result
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
def fetch_page(api, test_session, page_state: nil)
|
|
133
|
+
request_payload = payload(test_session, page_state: page_state)
|
|
134
|
+
Datadog.logger.debug { "Known tests request payload: #{request_payload}" }
|
|
83
135
|
|
|
84
136
|
http_response = api.api_request(
|
|
85
137
|
path: Ext::Transport::DD_API_UNIQUE_TESTS_PATH,
|
|
@@ -107,12 +159,12 @@ module Datadog
|
|
|
107
159
|
)
|
|
108
160
|
end
|
|
109
161
|
|
|
110
|
-
Response.from_http_response(http_response)
|
|
162
|
+
Response.from_http_response(http_response)
|
|
111
163
|
end
|
|
112
164
|
|
|
113
|
-
|
|
165
|
+
def payload(test_session, page_state: nil)
|
|
166
|
+
page_info = page_state ? {"page_state" => page_state} : {}
|
|
114
167
|
|
|
115
|
-
def payload(test_session)
|
|
116
168
|
{
|
|
117
169
|
"data" => {
|
|
118
170
|
"id" => Datadog::Core::Environment::Identity.id,
|
|
@@ -129,7 +181,8 @@ module Datadog
|
|
|
129
181
|
Ext::Test::TAG_RUNTIME_NAME => test_session.runtime_name,
|
|
130
182
|
Ext::Test::TAG_RUNTIME_VERSION => test_session.runtime_version,
|
|
131
183
|
"custom" => @config_tags
|
|
132
|
-
}
|
|
184
|
+
},
|
|
185
|
+
"page_info" => page_info
|
|
133
186
|
}
|
|
134
187
|
}
|
|
135
188
|
}.to_json
|
|
@@ -10,12 +10,13 @@ module Datadog
|
|
|
10
10
|
class Agentless < Base
|
|
11
11
|
attr_reader :api_key
|
|
12
12
|
|
|
13
|
-
def initialize(api_key:, citestcycle_url:, api_url:, citestcov_url:, logs_intake_url:)
|
|
13
|
+
def initialize(api_key:, citestcycle_url:, api_url:, citestcov_url:, logs_intake_url:, cicovreprt_url:)
|
|
14
14
|
@api_key = api_key
|
|
15
15
|
@citestcycle_http = build_http_client(citestcycle_url, compress: true)
|
|
16
16
|
@api_http = build_http_client(api_url, compress: false)
|
|
17
17
|
@citestcov_http = build_http_client(citestcov_url, compress: true)
|
|
18
18
|
@logs_intake_http = build_http_client(logs_intake_url, compress: true)
|
|
19
|
+
@cicovreprt_http = build_http_client(cicovreprt_url, compress: false)
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def citestcycle_request(path:, payload:, headers: {}, verb: "post")
|
|
@@ -49,6 +50,12 @@ module Datadog
|
|
|
49
50
|
perform_request(@logs_intake_http, path: path, payload: payload, headers: headers, verb: verb)
|
|
50
51
|
end
|
|
51
52
|
|
|
53
|
+
def cicovreprt_request(path:, event_payload:, compressed_coverage_report:, headers: {}, verb: "post")
|
|
54
|
+
super
|
|
55
|
+
|
|
56
|
+
perform_request(@cicovreprt_http, path: path, payload: @cicovreprt_payload, headers: headers, verb: verb)
|
|
57
|
+
end
|
|
58
|
+
|
|
52
59
|
private
|
|
53
60
|
|
|
54
61
|
def perform_request(http_client, path:, payload:, headers:, verb:, accept_compressed_response: false)
|
|
@@ -42,6 +42,27 @@ module Datadog
|
|
|
42
42
|
headers[Ext::Transport::HEADER_CONTENT_TYPE] ||= Ext::Transport::CONTENT_TYPE_JSON
|
|
43
43
|
end
|
|
44
44
|
|
|
45
|
+
def cicovreprt_request(path:, event_payload:, compressed_coverage_report:, headers: {}, verb: "post")
|
|
46
|
+
cicovreprt_request_boundary = ::SecureRandom.uuid
|
|
47
|
+
|
|
48
|
+
headers[Ext::Transport::HEADER_CONTENT_TYPE] ||=
|
|
49
|
+
"#{Ext::Transport::CONTENT_TYPE_MULTIPART_FORM_DATA}; boundary=#{cicovreprt_request_boundary}"
|
|
50
|
+
|
|
51
|
+
@cicovreprt_payload = [
|
|
52
|
+
"--#{cicovreprt_request_boundary}",
|
|
53
|
+
'Content-Disposition: form-data; name="event"; filename="event.json"',
|
|
54
|
+
"Content-Type: application/json",
|
|
55
|
+
"",
|
|
56
|
+
event_payload,
|
|
57
|
+
"--#{cicovreprt_request_boundary}",
|
|
58
|
+
'Content-Disposition: form-data; name="coverage"; filename="coverage.gz"',
|
|
59
|
+
"Content-Type: application/octet-stream",
|
|
60
|
+
"",
|
|
61
|
+
compressed_coverage_report,
|
|
62
|
+
"--#{cicovreprt_request_boundary}--"
|
|
63
|
+
].join("\r\n")
|
|
64
|
+
end
|
|
65
|
+
|
|
45
66
|
def headers_with_default(headers)
|
|
46
67
|
request_headers = default_headers
|
|
47
68
|
request_headers.merge!(headers)
|
|
@@ -30,12 +30,16 @@ module Datadog
|
|
|
30
30
|
logs_intake_url = settings.ci.agentless_url ||
|
|
31
31
|
"https://#{Ext::Transport::LOGS_INTAKE_HOST_PREFIX}.#{dd_site}:443"
|
|
32
32
|
|
|
33
|
+
cicovreprt_url = settings.ci.agentless_url ||
|
|
34
|
+
"https://#{Ext::Transport::CODE_COVERAGE_REPORT_INTAKE_HOST_PREFIX}.#{dd_site}:443"
|
|
35
|
+
|
|
33
36
|
Agentless.new(
|
|
34
37
|
api_key: settings.api_key,
|
|
35
38
|
citestcycle_url: citestcycle_url,
|
|
36
39
|
api_url: api_url,
|
|
37
40
|
citestcov_url: citestcov_url,
|
|
38
|
-
logs_intake_url: logs_intake_url
|
|
41
|
+
logs_intake_url: logs_intake_url,
|
|
42
|
+
cicovreprt_url: cicovreprt_url
|
|
39
43
|
)
|
|
40
44
|
end
|
|
41
45
|
|
|
@@ -50,6 +50,14 @@ module Datadog
|
|
|
50
50
|
raise NotImplementedError, "Logs intake is not supported in EVP proxy mode"
|
|
51
51
|
end
|
|
52
52
|
|
|
53
|
+
def cicovreprt_request(path:, event_payload:, compressed_coverage_report:, headers: {}, verb: "post")
|
|
54
|
+
super
|
|
55
|
+
|
|
56
|
+
headers[Ext::Transport::HEADER_EVP_SUBDOMAIN] = Ext::Transport::CODE_COVERAGE_REPORT_INTAKE_HOST_PREFIX
|
|
57
|
+
|
|
58
|
+
perform_request(@agent_intake_http, path: path, payload: @cicovreprt_payload, headers: headers, verb: verb)
|
|
59
|
+
end
|
|
60
|
+
|
|
53
61
|
private
|
|
54
62
|
|
|
55
63
|
def perform_request(http_client, path:, payload:, headers:, verb:)
|
data/lib/datadog/ci/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: datadog-ci
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.27.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Datadog, Inc.
|
|
@@ -92,6 +92,9 @@ files:
|
|
|
92
92
|
- lib/datadog/ci/cli/command/exec.rb
|
|
93
93
|
- lib/datadog/ci/cli/command/skippable_tests_percentage.rb
|
|
94
94
|
- lib/datadog/ci/cli/command/skippable_tests_percentage_estimate.rb
|
|
95
|
+
- lib/datadog/ci/code_coverage/component.rb
|
|
96
|
+
- lib/datadog/ci/code_coverage/null_component.rb
|
|
97
|
+
- lib/datadog/ci/code_coverage/transport.rb
|
|
95
98
|
- lib/datadog/ci/codeowners/matcher.rb
|
|
96
99
|
- lib/datadog/ci/codeowners/parser.rb
|
|
97
100
|
- lib/datadog/ci/codeowners/rule.rb
|
|
@@ -172,6 +175,7 @@ files:
|
|
|
172
175
|
- lib/datadog/ci/contrib/simplecov/ext.rb
|
|
173
176
|
- lib/datadog/ci/contrib/simplecov/integration.rb
|
|
174
177
|
- lib/datadog/ci/contrib/simplecov/patcher.rb
|
|
178
|
+
- lib/datadog/ci/contrib/simplecov/report_uploader.rb
|
|
175
179
|
- lib/datadog/ci/contrib/simplecov/result_extractor.rb
|
|
176
180
|
- lib/datadog/ci/ext/app_types.rb
|
|
177
181
|
- lib/datadog/ci/ext/dd_test.rb
|