datadog-ci 1.37.0 → 1.38.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 +23 -2
- data/ext/datadog_ci_native/file_serialization.c +7 -48
- data/lib/datadog/ci/configuration/components.rb +5 -2
- data/lib/datadog/ci/contrib/minitest/ext.rb +4 -0
- data/lib/datadog/ci/contrib/minitest/test.rb +69 -4
- data/lib/datadog/ci/contrib/parallel_tests/cli.rb +3 -1
- data/lib/datadog/ci/contrib/rspec/example.rb +40 -3
- data/lib/datadog/ci/contrib/rspec/ext.rb +4 -0
- data/lib/datadog/ci/test_impact_analysis/component.rb +4 -7
- data/lib/datadog/ci/test_impact_analysis/coverage/files.rb +5 -5
- data/lib/datadog/ci/test_session.rb +6 -0
- data/lib/datadog/ci/test_tracing/component.rb +2 -4
- data/lib/datadog/ci/test_tracing/null_component.rb +1 -1
- data/lib/datadog/ci/utils/file_storage.rb +50 -10
- data/lib/datadog/ci/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4a6bdfb71fe5140388ad8b1ea7a086977ba9a4a69c86fdc1b465746f287ba2ec
|
|
4
|
+
data.tar.gz: 27615bb724035bc22617b7495cac97cac343fac08da9363fba31e7a4fc21a84e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9ae087aef544b7775bb9784fb64a27a836e3181076229cac5c814d4263ecb02432f723d9e136350aba020d6ad67820a8658edcce62fb3dcccea5d5efff999820
|
|
7
|
+
data.tar.gz: aca348e24dba29c5a98fe60fe7e89879257e1466bce6b179c9efd43c40e4f3f1cffb2641f1d35fd54adbd111ba0c1f957db144848702fb51b0bf567282508ace
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
## [Unreleased]
|
|
2
2
|
|
|
3
|
+
## [1.38.0] - 2026-08-28
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
* Trace RSpec and Minitest lifecycle steps ([#595][])
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
* Optimize TIA performance ([#589][])
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
* Isolate state between parallel_tests sessions ([#593][])
|
|
13
|
+
* Test Optimization telemetry shutdown order ([#586][])
|
|
14
|
+
|
|
15
|
+
### Removed
|
|
16
|
+
* Disable LiveDebugger's SymDB upload in CI mode ([#594][])
|
|
17
|
+
|
|
3
18
|
## [1.37.0] - 2026-08-19
|
|
4
19
|
|
|
5
20
|
## [1.36.1] - 2026-08-11
|
|
@@ -684,7 +699,8 @@ Currently test suite level visibility is not used by our instrumentation: it wil
|
|
|
684
699
|
|
|
685
700
|
- Ruby versions < 2.7 no longer supported ([#8][])
|
|
686
701
|
|
|
687
|
-
[Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.
|
|
702
|
+
[Unreleased]: https://github.com/DataDog/datadog-ci-rb/compare/v1.38.0...main
|
|
703
|
+
[1.38.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.37.0...v1.38.0
|
|
688
704
|
[1.37.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.36.1...v1.37.0
|
|
689
705
|
[1.36.1]: https://github.com/DataDog/datadog-ci-rb/compare/v1.36.0...v1.36.1
|
|
690
706
|
[1.36.0]: https://github.com/DataDog/datadog-ci-rb/compare/v1.35.0...v1.36.0
|
|
@@ -969,4 +985,9 @@ Currently test suite level visibility is not used by our instrumentation: it wil
|
|
|
969
985
|
[#554]: https://github.com/DataDog/datadog-ci-rb/issues/554
|
|
970
986
|
[#555]: https://github.com/DataDog/datadog-ci-rb/issues/555
|
|
971
987
|
[#566]: https://github.com/DataDog/datadog-ci-rb/issues/566
|
|
972
|
-
[#570]: https://github.com/DataDog/datadog-ci-rb/issues/570
|
|
988
|
+
[#570]: https://github.com/DataDog/datadog-ci-rb/issues/570
|
|
989
|
+
[#586]: https://github.com/DataDog/datadog-ci-rb/issues/586
|
|
990
|
+
[#589]: https://github.com/DataDog/datadog-ci-rb/issues/589
|
|
991
|
+
[#593]: https://github.com/DataDog/datadog-ci-rb/issues/593
|
|
992
|
+
[#594]: https://github.com/DataDog/datadog-ci-rb/issues/594
|
|
993
|
+
[#595]: https://github.com/DataDog/datadog-ci-rb/issues/595
|
|
@@ -13,11 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
struct packed_files_context {
|
|
15
15
|
VALUE root;
|
|
16
|
-
VALUE seen;
|
|
17
16
|
VALUE packed;
|
|
18
17
|
long root_len;
|
|
19
18
|
uint32_t files_count;
|
|
20
|
-
bool direct_absolute;
|
|
21
19
|
bool fast_path_supported;
|
|
22
20
|
};
|
|
23
21
|
|
|
@@ -92,8 +90,6 @@ static bool packed_files_append_entry(struct packed_files_context *context,
|
|
|
92
90
|
return false;
|
|
93
91
|
}
|
|
94
92
|
|
|
95
|
-
VALUE relative_file = file;
|
|
96
|
-
VALUE dedup_file = file;
|
|
97
93
|
long relative_len = RSTRING_LEN(file);
|
|
98
94
|
long relative_offset = 0;
|
|
99
95
|
const char *file_ptr = RSTRING_PTR(file);
|
|
@@ -117,15 +113,11 @@ static bool packed_files_append_entry(struct packed_files_context *context,
|
|
|
117
113
|
if (relative_len == 0) {
|
|
118
114
|
return true;
|
|
119
115
|
}
|
|
120
|
-
|
|
121
|
-
relative_offset = context->root_len + 1;
|
|
122
|
-
} else {
|
|
123
|
-
relative_file = rb_str_substr(file, context->root_len + 1, file_len);
|
|
124
|
-
dedup_file = relative_file;
|
|
125
|
-
}
|
|
116
|
+
relative_offset = context->root_len + 1;
|
|
126
117
|
} else if (!additional_file) {
|
|
127
|
-
//
|
|
128
|
-
//
|
|
118
|
+
// Production DDCov and static-dependency extraction only emit absolute
|
|
119
|
+
// paths after filtering them against the repository root. Keep the Ruby
|
|
120
|
+
// fallback for direct callers that violate this invariant.
|
|
129
121
|
context->fast_path_supported = false;
|
|
130
122
|
return false;
|
|
131
123
|
}
|
|
@@ -133,17 +125,12 @@ static bool packed_files_append_entry(struct packed_files_context *context,
|
|
|
133
125
|
if (relative_len == 0) {
|
|
134
126
|
return true;
|
|
135
127
|
}
|
|
136
|
-
if (rb_hash_lookup2(context->seen, dedup_file, Qundef) != Qundef) {
|
|
137
|
-
return true;
|
|
138
|
-
}
|
|
139
|
-
rb_hash_aset(context->seen, dedup_file, Qtrue);
|
|
140
128
|
|
|
141
|
-
int encoding_index = rb_enc_get_index(
|
|
129
|
+
int encoding_index = rb_enc_get_index(file);
|
|
142
130
|
bool binary = encoding_index == rb_ascii8bit_encindex();
|
|
143
131
|
if (!binary && encoding_index != rb_utf8_encindex() &&
|
|
144
132
|
encoding_index != rb_usascii_encindex() &&
|
|
145
|
-
(!rb_enc_str_asciicompat_p(
|
|
146
|
-
!string_bytes_are_ascii(relative_file))) {
|
|
133
|
+
(!rb_enc_str_asciicompat_p(file) || !string_bytes_are_ascii(file))) {
|
|
147
134
|
context->fast_path_supported = false;
|
|
148
135
|
return false;
|
|
149
136
|
}
|
|
@@ -159,9 +146,8 @@ static bool packed_files_append_entry(struct packed_files_context *context,
|
|
|
159
146
|
sizeof(filename_entry_prefix));
|
|
160
147
|
packed_files_append_string_header(context->packed, (uint32_t)relative_len,
|
|
161
148
|
binary);
|
|
162
|
-
const char *relative_ptr = RSTRING_PTR(
|
|
149
|
+
const char *relative_ptr = RSTRING_PTR(file) + relative_offset;
|
|
163
150
|
rb_str_cat(context->packed, relative_ptr, relative_len);
|
|
164
|
-
RB_GC_GUARD(relative_file);
|
|
165
151
|
context->files_count++;
|
|
166
152
|
return true;
|
|
167
153
|
}
|
|
@@ -174,24 +160,6 @@ static int pack_primary_file_i(VALUE file, VALUE _value,
|
|
|
174
160
|
: ST_STOP;
|
|
175
161
|
}
|
|
176
162
|
|
|
177
|
-
static bool packed_file_is_absolute(VALUE file, bool additional_file) {
|
|
178
|
-
if (file == Qnil && !additional_file) {
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
|
-
return RB_TYPE_P(file, T_STRING) && rb_obj_class(file) == rb_cString &&
|
|
182
|
-
RSTRING_LEN(file) > 0 && RSTRING_PTR(file)[0] == '/';
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
static int detect_absolute_primary_file_i(VALUE file, VALUE _value,
|
|
186
|
-
VALUE all_absolute_value) {
|
|
187
|
-
bool *all_absolute = (bool *)all_absolute_value;
|
|
188
|
-
if (!packed_file_is_absolute(file, false)) {
|
|
189
|
-
*all_absolute = false;
|
|
190
|
-
return ST_STOP;
|
|
191
|
-
}
|
|
192
|
-
return ST_CONTINUE;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
163
|
static void
|
|
196
164
|
packed_files_write_array_header(struct packed_files_context *context) {
|
|
197
165
|
unsigned char header[5];
|
|
@@ -233,22 +201,13 @@ static VALUE file_serialization_pack_files(VALUE module, VALUE primary_files,
|
|
|
233
201
|
return Qnil;
|
|
234
202
|
}
|
|
235
203
|
|
|
236
|
-
bool all_absolute = true;
|
|
237
|
-
rb_hash_foreach(primary_files, detect_absolute_primary_file_i,
|
|
238
|
-
(VALUE)&all_absolute);
|
|
239
204
|
long additional_files_len = RARRAY_LEN(additional_files);
|
|
240
|
-
for (long i = 0; all_absolute && i < additional_files_len; i++) {
|
|
241
|
-
all_absolute =
|
|
242
|
-
packed_file_is_absolute(rb_ary_entry(additional_files, i), true);
|
|
243
|
-
}
|
|
244
205
|
|
|
245
206
|
struct packed_files_context context = {
|
|
246
207
|
.root = root,
|
|
247
|
-
.seen = rb_hash_new(),
|
|
248
208
|
.packed = rb_str_buf_new(4096),
|
|
249
209
|
.root_len = RSTRING_LEN(root),
|
|
250
210
|
.files_count = 0,
|
|
251
|
-
.direct_absolute = all_absolute,
|
|
252
211
|
.fast_path_supported = true};
|
|
253
212
|
rb_str_resize(context.packed, 5);
|
|
254
213
|
|
|
@@ -72,8 +72,6 @@ module Datadog
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def shutdown!(replacement = nil)
|
|
75
|
-
super
|
|
76
|
-
|
|
77
75
|
@test_optimization_cache&.shutdown!
|
|
78
76
|
@test_tracing&.shutdown!
|
|
79
77
|
@test_impact_analysis&.shutdown!
|
|
@@ -81,9 +79,14 @@ module Datadog
|
|
|
81
79
|
@test_discovery&.shutdown!
|
|
82
80
|
@code_coverage&.shutdown!
|
|
83
81
|
@git_tree_upload_worker&.stop
|
|
82
|
+
ensure
|
|
83
|
+
super
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def activate_ci!(settings)
|
|
87
|
+
# Symbol Database upload is not supported in CI mode. Older datadog versions do not expose this setting.
|
|
88
|
+
settings.symbol_database.enabled = false if settings.respond_to?(:symbol_database)
|
|
89
|
+
|
|
87
90
|
unless settings.tracing.enabled
|
|
88
91
|
Datadog.logger.error(
|
|
89
92
|
"Test Optimization requires tracing to be enabled. Disabling Test Optimization. " \
|
|
@@ -48,18 +48,57 @@ module Datadog
|
|
|
48
48
|
super
|
|
49
49
|
end
|
|
50
50
|
|
|
51
|
-
def
|
|
51
|
+
def before_setup
|
|
52
52
|
test_span = _dd_test_tracing_component.active_test
|
|
53
53
|
return super unless test_span
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
@datadog_before_step_failures_count = failures.length
|
|
56
|
+
@datadog_before_step_span = _dd_test_tracing_component.trace(
|
|
57
|
+
Ext::BEFORE_STEP_SPAN_NAME,
|
|
58
|
+
type: Ext::STEP_SPAN_TYPE
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
super
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def after_setup
|
|
65
|
+
super
|
|
66
|
+
ensure
|
|
67
|
+
finish_datadog_before_step
|
|
68
|
+
end
|
|
56
69
|
|
|
57
|
-
|
|
58
|
-
|
|
70
|
+
def before_teardown
|
|
71
|
+
test_span = _dd_test_tracing_component.active_test
|
|
72
|
+
return super unless test_span
|
|
73
|
+
|
|
74
|
+
# Setup failures prevent Minitest from invoking after_setup, so
|
|
75
|
+
# close a still-active before span before teardown begins.
|
|
76
|
+
finish_datadog_before_step
|
|
77
|
+
|
|
78
|
+
@datadog_after_step_failures_count = failures.length
|
|
79
|
+
@datadog_after_step_span = _dd_test_tracing_component.trace(
|
|
80
|
+
Ext::AFTER_STEP_SPAN_NAME,
|
|
81
|
+
type: Ext::STEP_SPAN_TYPE
|
|
82
|
+
)
|
|
59
83
|
|
|
60
84
|
super
|
|
61
85
|
end
|
|
62
86
|
|
|
87
|
+
def after_teardown
|
|
88
|
+
test_span = _dd_test_tracing_component.active_test
|
|
89
|
+
return super unless test_span
|
|
90
|
+
|
|
91
|
+
begin
|
|
92
|
+
super
|
|
93
|
+
ensure
|
|
94
|
+
finish_datadog_after_step
|
|
95
|
+
finish_with_result(test_span, result_code)
|
|
96
|
+
|
|
97
|
+
# remove failures if failure can be ignored because of retries
|
|
98
|
+
self.failures = [] if test_span.should_ignore_failures?
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
63
102
|
private
|
|
64
103
|
|
|
65
104
|
def datadog_run_reentered?
|
|
@@ -115,6 +154,32 @@ module Datadog
|
|
|
115
154
|
Helpers.start_test_suite(self.class)
|
|
116
155
|
end
|
|
117
156
|
|
|
157
|
+
def finish_datadog_before_step
|
|
158
|
+
span = @datadog_before_step_span
|
|
159
|
+
return unless span
|
|
160
|
+
|
|
161
|
+
finish_datadog_step(span, @datadog_before_step_failures_count)
|
|
162
|
+
@datadog_before_step_span = nil
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def finish_datadog_after_step
|
|
166
|
+
span = @datadog_after_step_span
|
|
167
|
+
return unless span
|
|
168
|
+
|
|
169
|
+
finish_datadog_step(span, @datadog_after_step_failures_count)
|
|
170
|
+
@datadog_after_step_span = nil
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def finish_datadog_step(span, failures_count)
|
|
174
|
+
step_failure = failures[failures_count]
|
|
175
|
+
if step_failure && !step_failure.is_a?(::Minitest::Skip)
|
|
176
|
+
exception = step_failure.respond_to?(:error) ? step_failure.error : step_failure
|
|
177
|
+
span.failed!(exception: exception)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
span.finish
|
|
181
|
+
end
|
|
182
|
+
|
|
118
183
|
def skip_datadog_test(test_span)
|
|
119
184
|
time_it do
|
|
120
185
|
capture_exceptions do
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "../../ext/test"
|
|
4
|
+
require_relative "../../utils/file_storage"
|
|
4
5
|
require_relative "../rspec/ext"
|
|
5
6
|
|
|
6
7
|
module Datadog
|
|
@@ -17,7 +18,7 @@ module Datadog
|
|
|
17
18
|
# only rspec runner is supported for now
|
|
18
19
|
return super if @runner != ::ParallelTests::RSpec::Runner
|
|
19
20
|
|
|
20
|
-
|
|
21
|
+
Utils::FileStorage.with_new_namespace do |storage_namespace|
|
|
21
22
|
# Preserve activation explicitly for the RSpec worker commands.
|
|
22
23
|
# Starting the distributed parent session removes inherited
|
|
23
24
|
# activation before unrelated child processes can receive it.
|
|
@@ -35,6 +36,7 @@ module Datadog
|
|
|
35
36
|
|
|
36
37
|
options[:env] ||= {}
|
|
37
38
|
options[:env][CI::Ext::Settings::ENV_TEST_VISIBILITY_DRB_SERVER_URI] = test_tracing_component.context_service_uri
|
|
39
|
+
options[:env][Utils::FileStorage::ENV_NAMESPACE] = storage_namespace
|
|
38
40
|
options[:env]["RUBYOPT"] ||= worker_rubyopt if worker_rubyopt
|
|
39
41
|
|
|
40
42
|
super
|
|
@@ -26,8 +26,9 @@ module Datadog
|
|
|
26
26
|
# ============================================
|
|
27
27
|
|
|
28
28
|
def run(*args)
|
|
29
|
-
return super unless
|
|
30
|
-
|
|
29
|
+
return super unless datadog_tracing_enabled?
|
|
30
|
+
|
|
31
|
+
@datadog_test_tracing_component = test_tracing_component
|
|
31
32
|
|
|
32
33
|
test_suite_span = test_tracing_component.start_test_suite(datadog_test_suite_name) if ci_queue?
|
|
33
34
|
|
|
@@ -76,6 +77,8 @@ module Datadog
|
|
|
76
77
|
# after retries are done, we must report the test to RSpec
|
|
77
78
|
@skip_reporting = false
|
|
78
79
|
finish(reporter)
|
|
80
|
+
ensure
|
|
81
|
+
@datadog_test_tracing_component = nil
|
|
79
82
|
end
|
|
80
83
|
|
|
81
84
|
def finish(reporter)
|
|
@@ -193,6 +196,40 @@ module Datadog
|
|
|
193
196
|
# Run method helpers
|
|
194
197
|
# ============================================
|
|
195
198
|
|
|
199
|
+
def run_before_example
|
|
200
|
+
return super unless @datadog_test_tracing_component
|
|
201
|
+
|
|
202
|
+
skip_exception = nil
|
|
203
|
+
# @type var skip_exception: ::RSpec::Core::Pending::SkipDeclaredInExample?
|
|
204
|
+
result = test_tracing_component.trace(Ext::BEFORE_STEP_SPAN_NAME, type: Ext::STEP_SPAN_TYPE) do
|
|
205
|
+
super
|
|
206
|
+
rescue ::RSpec::Core::Pending::SkipDeclaredInExample => e
|
|
207
|
+
skip_exception = e
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
raise skip_exception if skip_exception
|
|
211
|
+
|
|
212
|
+
result
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def run_after_example
|
|
216
|
+
return super unless @datadog_test_tracing_component
|
|
217
|
+
|
|
218
|
+
exception_before_hooks = exception
|
|
219
|
+
|
|
220
|
+
test_tracing_component.trace(Ext::AFTER_STEP_SPAN_NAME, type: Ext::STEP_SPAN_TYPE) do |span|
|
|
221
|
+
result = super
|
|
222
|
+
span&.failed!(exception: exception) if exception && exception != exception_before_hooks
|
|
223
|
+
result
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def datadog_tracing_enabled?
|
|
228
|
+
Datadog.configuration.ci.enabled &&
|
|
229
|
+
datadog_configuration[:enabled] &&
|
|
230
|
+
(!::RSpec.configuration.dry_run? || datadog_configuration[:dry_run_enabled])
|
|
231
|
+
end
|
|
232
|
+
|
|
196
233
|
def build_test_tags
|
|
197
234
|
# @type var tags : Hash[String, String]
|
|
198
235
|
tags = {
|
|
@@ -376,7 +413,7 @@ module Datadog
|
|
|
376
413
|
end
|
|
377
414
|
|
|
378
415
|
def test_tracing_component
|
|
379
|
-
Datadog.send(:components).test_tracing
|
|
416
|
+
@datadog_test_tracing_component || Datadog.send(:components).test_tracing
|
|
380
417
|
end
|
|
381
418
|
|
|
382
419
|
def test_retries_component
|
|
@@ -13,6 +13,10 @@ module Datadog
|
|
|
13
13
|
ENV_ENABLED = "DD_TRACE_RSPEC_ENABLED"
|
|
14
14
|
ENV_DATADOG_FORMATTER_ENABLED = "DD_TRACE_RSPEC_DATADOG_FORMATTER_ENABLED"
|
|
15
15
|
|
|
16
|
+
STEP_SPAN_TYPE = "step"
|
|
17
|
+
BEFORE_STEP_SPAN_NAME = "before"
|
|
18
|
+
AFTER_STEP_SPAN_NAME = "after"
|
|
19
|
+
|
|
16
20
|
# Metadata keys
|
|
17
21
|
METADATA_DD_RETRIES = :dd_retries
|
|
18
22
|
METADATA_DD_RETRY_RESULTS = :dd_retry_results
|
|
@@ -459,13 +459,11 @@ module Datadog
|
|
|
459
459
|
def enrich_coverage_with_static_dependencies(coverage)
|
|
460
460
|
return unless @static_dependencies_tracking_enabled
|
|
461
461
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
static_dependencies_map.merge!(
|
|
462
|
+
coverage.keys.each do |file|
|
|
463
|
+
coverage.merge!(
|
|
465
464
|
Datadog::CI::SourceCode::StaticDependencies.fetch_static_dependencies(file)
|
|
466
465
|
)
|
|
467
466
|
end
|
|
468
|
-
coverage.merge!(static_dependencies_map)
|
|
469
467
|
end
|
|
470
468
|
|
|
471
469
|
def ensure_test_source_covered(test_source_file, coverage)
|
|
@@ -493,9 +491,8 @@ module Datadog
|
|
|
493
491
|
|
|
494
492
|
enrich_coverage_with_static_dependencies(coverage)
|
|
495
493
|
|
|
496
|
-
# Avoid normalizing
|
|
497
|
-
#
|
|
498
|
-
# an estimate and may count overlaps between the two collections.
|
|
494
|
+
# Avoid normalizing paths on the test thread. The writer serializes
|
|
495
|
+
# each entry and telemetry only needs the resulting entry count.
|
|
499
496
|
Telemetry.code_coverage_files(coverage.size + custom_impacted_files.size)
|
|
500
497
|
|
|
501
498
|
files = Coverage::Files.new(coverage, custom_impacted_files)
|
|
@@ -8,7 +8,7 @@ module Datadog
|
|
|
8
8
|
module TestImpactAnalysis
|
|
9
9
|
module Coverage
|
|
10
10
|
# Keeps native coverage and custom impacted files together and
|
|
11
|
-
# normalizes
|
|
11
|
+
# normalizes their paths for serialization.
|
|
12
12
|
#
|
|
13
13
|
# @internal
|
|
14
14
|
class Files
|
|
@@ -33,9 +33,9 @@ module Datadog
|
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
# Writes the complete MessagePack files array. The native fast path
|
|
36
|
-
# combines absolute-path classification, immutable-root slicing,
|
|
37
|
-
#
|
|
38
|
-
#
|
|
36
|
+
# combines absolute-path classification, immutable-root slicing, and
|
|
37
|
+
# filename-entry packing. Any shape it does not support falls back
|
|
38
|
+
# before writing bytes.
|
|
39
39
|
def write_to(packer)
|
|
40
40
|
if FileSerialization.respond_to?(:pack_files) &&
|
|
41
41
|
(packed_files = FileSerialization.pack_files(
|
|
@@ -85,7 +85,7 @@ module Datadog
|
|
|
85
85
|
end
|
|
86
86
|
files << relative_file unless relative_file.empty?
|
|
87
87
|
end
|
|
88
|
-
files
|
|
88
|
+
files
|
|
89
89
|
end
|
|
90
90
|
end
|
|
91
91
|
end
|
|
@@ -14,6 +14,12 @@ module Datadog
|
|
|
14
14
|
class TestSession < ConcurrentSpan
|
|
15
15
|
attr_accessor :estimated_total_tests_count, :distributed
|
|
16
16
|
|
|
17
|
+
def initialize(tracer_span)
|
|
18
|
+
super
|
|
19
|
+
|
|
20
|
+
@distributed = false
|
|
21
|
+
end
|
|
22
|
+
|
|
17
23
|
# Finishes the current test session.
|
|
18
24
|
# @return [void]
|
|
19
25
|
def finish
|
|
@@ -82,7 +82,7 @@ module Datadog
|
|
|
82
82
|
store_component_state if test_session.distributed
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
-
def start_test_session(service: nil, tags: {}, estimated_total_tests_count: 0, distributed:
|
|
85
|
+
def start_test_session(service: nil, tags: {}, estimated_total_tests_count: 0, distributed: nil, local_test_suites_mode: true)
|
|
86
86
|
return skip_tracing unless test_suite_level_visibility_enabled
|
|
87
87
|
|
|
88
88
|
@local_test_suites_mode = local_test_suites_mode
|
|
@@ -91,7 +91,7 @@ module Datadog
|
|
|
91
91
|
|
|
92
92
|
test_session = maybe_remote_context.start_test_session(service: service, tags: tags)
|
|
93
93
|
test_session.estimated_total_tests_count = estimated_total_tests_count
|
|
94
|
-
test_session.distributed = distributed
|
|
94
|
+
test_session.distributed = distributed unless distributed.nil?
|
|
95
95
|
|
|
96
96
|
on_test_session_started(test_session)
|
|
97
97
|
|
|
@@ -321,8 +321,6 @@ module Datadog
|
|
|
321
321
|
DeprecatedTotalCoverageMetric.extract_lines_pct(test_session)
|
|
322
322
|
|
|
323
323
|
Telemetry.event_finished(test_session)
|
|
324
|
-
|
|
325
|
-
Utils::FileStorage.cleanup
|
|
326
324
|
end
|
|
327
325
|
|
|
328
326
|
def on_test_module_finished(test_module)
|
|
@@ -9,7 +9,7 @@ module Datadog
|
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def start_test_session(
|
|
12
|
-
service: nil, tags: {}, estimated_total_tests_count: 0, distributed:
|
|
12
|
+
service: nil, tags: {}, estimated_total_tests_count: 0, distributed: nil, local_test_suites_mode: true
|
|
13
13
|
)
|
|
14
14
|
skip_tracing
|
|
15
15
|
end
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "fileutils"
|
|
4
|
-
require "
|
|
4
|
+
require "securerandom"
|
|
5
|
+
require "tmpdir"
|
|
5
6
|
|
|
6
7
|
module Datadog
|
|
7
8
|
module CI
|
|
@@ -9,18 +10,30 @@ module Datadog
|
|
|
9
10
|
# FileStorage module provides functionality for storing and retrieving arbitrary Ruby objects in a temp file
|
|
10
11
|
# to share them between processes.
|
|
11
12
|
module FileStorage
|
|
13
|
+
class MissingNamespaceError < StandardError; end
|
|
14
|
+
|
|
12
15
|
TEMP_DIR = File.join(Dir.tmpdir, "datadog-ci-storage")
|
|
16
|
+
ENV_NAMESPACE = "DD_CIVISIBILITY_PARALLEL_TESTS_RUN_ID"
|
|
13
17
|
|
|
14
18
|
def self.store(key, value)
|
|
15
19
|
ensure_temp_dir_exists
|
|
16
20
|
file_path = file_path_for(key)
|
|
21
|
+
temporary_path = File.join(storage_dir, "dd-ci-#{SecureRandom.uuid}.tmp")
|
|
17
22
|
|
|
18
|
-
File.
|
|
23
|
+
File.open(temporary_path, File::WRONLY | File::CREAT | File::EXCL, 0o600) do |file|
|
|
24
|
+
file.binmode
|
|
25
|
+
file.write(Marshal.dump(value))
|
|
26
|
+
file.flush
|
|
27
|
+
file.fsync
|
|
28
|
+
end
|
|
29
|
+
File.rename(temporary_path, file_path)
|
|
19
30
|
|
|
20
31
|
true
|
|
21
32
|
rescue => e
|
|
22
|
-
Datadog.logger.error("Failed to store data for key '#{key}': #{e.class}
|
|
33
|
+
Datadog.logger.error("Failed to store data for key '#{key}': #{e.class}")
|
|
23
34
|
false
|
|
35
|
+
ensure
|
|
36
|
+
FileUtils.rm_f(temporary_path) if temporary_path
|
|
24
37
|
end
|
|
25
38
|
|
|
26
39
|
def self.retrieve(key)
|
|
@@ -29,27 +42,54 @@ module Datadog
|
|
|
29
42
|
|
|
30
43
|
Marshal.load(File.binread(file_path))
|
|
31
44
|
rescue => e
|
|
32
|
-
Datadog.logger.error("Failed to retrieve data for key '#{key}': #{e.class}
|
|
45
|
+
Datadog.logger.error("Failed to retrieve data for key '#{key}': #{e.class}")
|
|
33
46
|
nil
|
|
34
47
|
end
|
|
35
48
|
|
|
36
|
-
def self.cleanup
|
|
37
|
-
|
|
49
|
+
def self.cleanup(namespace)
|
|
50
|
+
directory = storage_dir(namespace)
|
|
51
|
+
return false unless Dir.exist?(directory)
|
|
38
52
|
|
|
39
|
-
FileUtils.rm_rf(
|
|
53
|
+
FileUtils.rm_rf(directory)
|
|
40
54
|
true
|
|
41
55
|
rescue => e
|
|
42
|
-
Datadog.logger.error("Failed to cleanup storage directory: #{e.class}
|
|
56
|
+
Datadog.logger.error("Failed to cleanup storage directory: #{e.class}")
|
|
43
57
|
false
|
|
44
58
|
end
|
|
59
|
+
private_class_method :cleanup
|
|
60
|
+
|
|
61
|
+
def self.with_new_namespace
|
|
62
|
+
previous_namespace = ENV[ENV_NAMESPACE]
|
|
63
|
+
namespace = SecureRandom.uuid
|
|
64
|
+
ENV[ENV_NAMESPACE] = namespace
|
|
65
|
+
|
|
66
|
+
yield namespace
|
|
67
|
+
ensure
|
|
68
|
+
cleanup(namespace) if namespace
|
|
69
|
+
|
|
70
|
+
if previous_namespace
|
|
71
|
+
ENV[ENV_NAMESPACE] = previous_namespace
|
|
72
|
+
else
|
|
73
|
+
ENV.delete(ENV_NAMESPACE)
|
|
74
|
+
end
|
|
75
|
+
end
|
|
45
76
|
|
|
46
77
|
def self.ensure_temp_dir_exists
|
|
47
|
-
FileUtils.mkdir_p(
|
|
78
|
+
FileUtils.mkdir_p(storage_dir)
|
|
48
79
|
end
|
|
49
80
|
|
|
50
81
|
def self.file_path_for(key)
|
|
51
82
|
sanitized_key = key.to_s.gsub(/[^a-zA-Z0-9_-]/, "_")
|
|
52
|
-
File.join(
|
|
83
|
+
File.join(storage_dir, "dd-ci-#{sanitized_key}.dat")
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.storage_dir(namespace = ENV[ENV_NAMESPACE])
|
|
87
|
+
if namespace.nil? || namespace.empty?
|
|
88
|
+
raise MissingNamespaceError, "File storage namespace is not set"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
sanitized_namespace = namespace.gsub(/[^a-zA-Z0-9_-]/, "_")
|
|
92
|
+
File.join(TEMP_DIR, sanitized_namespace)
|
|
53
93
|
end
|
|
54
94
|
end
|
|
55
95
|
end
|
data/lib/datadog/ci/version.rb
CHANGED