knapsack_pro 10.0.1 → 11.0.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/lib/knapsack_pro/adapters/base_adapter.rb +6 -6
- data/lib/knapsack_pro/adapters/minitest_adapter.rb +3 -3
- data/lib/knapsack_pro/adapters/rspec_adapter.rb +3 -3
- data/lib/knapsack_pro/client/api/v1/build_distributions.rb +0 -29
- data/lib/knapsack_pro/client/api/v2/queues.rb +1 -3
- data/lib/knapsack_pro/client/connection.rb +25 -12
- data/lib/knapsack_pro/commands.rb +8 -8
- data/lib/knapsack_pro/config/ci/app_veyor.rb +0 -8
- data/lib/knapsack_pro/config/ci/base.rb +0 -7
- data/lib/knapsack_pro/config/ci/buildkite.rb +1 -9
- data/lib/knapsack_pro/config/ci/circle.rb +0 -8
- data/lib/knapsack_pro/config/ci/cirrus_ci.rb +0 -8
- data/lib/knapsack_pro/config/ci/codefresh.rb +0 -8
- data/lib/knapsack_pro/config/ci/codeship.rb +0 -8
- data/lib/knapsack_pro/config/ci/github_actions.rb +2 -10
- data/lib/knapsack_pro/config/ci/gitlab_ci.rb +0 -9
- data/lib/knapsack_pro/config/ci/heroku.rb +1 -9
- data/lib/knapsack_pro/config/ci/semaphore.rb +0 -8
- data/lib/knapsack_pro/config/ci/semaphore2.rb +0 -8
- data/lib/knapsack_pro/config/ci/travis.rb +0 -8
- data/lib/knapsack_pro/config/env.rb +12 -42
- data/lib/knapsack_pro/crypto/branch_encryptor.rb +4 -5
- data/lib/knapsack_pro/extensions/rspec_extension.rb +1 -1
- data/lib/knapsack_pro/queue_allocator.rb +33 -88
- data/lib/knapsack_pro/regular_allocator.rb +41 -20
- data/lib/knapsack_pro/report.rb +37 -8
- data/lib/knapsack_pro/repository_adapters/git_adapter.rb +10 -2
- data/lib/knapsack_pro/runners/cucumber_runner.rb +2 -1
- data/lib/knapsack_pro/runners/minitest_runner.rb +2 -1
- data/lib/knapsack_pro/runners/queue/base_runner.rb +17 -8
- data/lib/knapsack_pro/runners/queue/cucumber_runner.rb +1 -1
- data/lib/knapsack_pro/runners/queue/minitest_runner.rb +1 -1
- data/lib/knapsack_pro/runners/queue/rspec_runner.rb +3 -3
- data/lib/knapsack_pro/runners/rspec_runner.rb +2 -1
- data/lib/knapsack_pro/runners/spinach_runner.rb +2 -1
- data/lib/knapsack_pro/runners/test_unit_runner.rb +2 -1
- data/lib/knapsack_pro/tracker.rb +2 -4
- data/lib/knapsack_pro/urls.rb +4 -10
- data/lib/knapsack_pro/version.rb +1 -1
- data/lib/tasks/queue/rspec.rake +5 -1
- metadata +2 -3
- data/lib/knapsack_pro/client/api/v1/queues.rb +0 -44
|
@@ -20,12 +20,6 @@ module KnapsackPro
|
|
|
20
20
|
@ci_node_index ||= (env_for('KNAPSACK_PRO_CI_NODE_INDEX', :node_index) || 0).to_i
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
def ci_node_build_id
|
|
24
|
-
env_name = 'KNAPSACK_PRO_CI_NODE_BUILD_ID'
|
|
25
|
-
env_for(env_name, :node_build_id) ||
|
|
26
|
-
raise("Missing environment variable #{env_name}. Read more at #{KnapsackPro::Urls::KNAPSACK_PRO_CI_NODE_BUILD_ID}")
|
|
27
|
-
end
|
|
28
|
-
|
|
29
23
|
def ci_node_retry_count
|
|
30
24
|
(env_for('KNAPSACK_PRO_CI_NODE_RETRY_COUNT', :node_retry_count) || 0).to_i
|
|
31
25
|
end
|
|
@@ -95,12 +89,12 @@ module KnapsackPro
|
|
|
95
89
|
ENV['KNAPSACK_PRO_REPOSITORY_ADAPTER']
|
|
96
90
|
end
|
|
97
91
|
|
|
98
|
-
def
|
|
99
|
-
ENV['
|
|
92
|
+
def single_batch_mode?
|
|
93
|
+
ENV['KNAPSACK_PRO_SINGLE_BATCH_MODE_ENABLED'] == 'true'
|
|
100
94
|
end
|
|
101
95
|
|
|
102
|
-
def
|
|
103
|
-
ENV['
|
|
96
|
+
def normal_mode?
|
|
97
|
+
ENV['KNAPSACK_PRO_NORMAL_MODE_ENABLED'] == 'true'
|
|
104
98
|
end
|
|
105
99
|
|
|
106
100
|
def queue_id
|
|
@@ -143,35 +137,8 @@ module KnapsackPro
|
|
|
143
137
|
ENV.fetch('KNAPSACK_PRO_ENDPOINT', 'https://api.knapsackpro.com')
|
|
144
138
|
end
|
|
145
139
|
|
|
146
|
-
def
|
|
147
|
-
ENV.fetch('
|
|
148
|
-
end
|
|
149
|
-
|
|
150
|
-
def fixed_test_suite_split?
|
|
151
|
-
fixed_test_suite_split.to_s == 'true'
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
def fixed_queue_split
|
|
155
|
-
@fixed_queue_split ||= begin
|
|
156
|
-
env_name = 'KNAPSACK_PRO_FIXED_QUEUE_SPLIT'
|
|
157
|
-
computed = env_for(env_name, :fixed_queue_split).to_s
|
|
158
|
-
|
|
159
|
-
if !ENV.key?(env_name)
|
|
160
|
-
KnapsackPro.logger.info("#{env_name} is not set. Using default value: #{computed}. Learn more at #{KnapsackPro::Urls::FIXED_QUEUE_SPLIT}")
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
computed
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
def fixed_queue_split_?
|
|
168
|
-
return @fixed_queue_split_ if defined?(@fixed_queue_split_)
|
|
169
|
-
|
|
170
|
-
@fixed_queue_split_ = env_for('KNAPSACK_PRO_FIXED_QUEUE_SPLIT', :fixed_queue_split).to_s == "true"
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
def fixed_queue_split?
|
|
174
|
-
fixed_queue_split.to_s == 'true'
|
|
140
|
+
def single_batch?
|
|
141
|
+
ENV.fetch('KNAPSACK_PRO_SINGLE_BATCH', 'false') == 'true'
|
|
175
142
|
end
|
|
176
143
|
|
|
177
144
|
def cucumber_queue_prefix
|
|
@@ -269,8 +236,11 @@ module KnapsackPro
|
|
|
269
236
|
def test_queue_id
|
|
270
237
|
knapsack_env_name = 'KNAPSACK_PRO_TEST_QUEUE_ID'
|
|
271
238
|
knapsack_env_value = ENV[knapsack_env_name]
|
|
239
|
+
if knapsack_env_value == '' || knapsack_env_value&.match?(/[^a-z0-9\-_]+/i)
|
|
240
|
+
raise "Invalid test_queue_id `#{knapsack_env_value}`: Only ASCII letters, numbers, hyphens (-), and underscores (_) are allowed."
|
|
241
|
+
end
|
|
272
242
|
|
|
273
|
-
ci_env_value = detected_ci.new.test_queue_id
|
|
243
|
+
ci_env_value = detected_ci.new.test_queue_id&.gsub(/[^a-z0-9\-_]+/i, "-")
|
|
274
244
|
|
|
275
245
|
if !knapsack_env_value.nil? && !ci_env_value.nil? && knapsack_env_value != ci_env_value.to_s
|
|
276
246
|
warn("You have set the environment variable #{knapsack_env_name} to #{knapsack_env_value} which could be automatically determined from the CI environment as #{ci_env_value}.")
|
|
@@ -279,8 +249,8 @@ module KnapsackPro
|
|
|
279
249
|
id = knapsack_env_value != nil ? knapsack_env_value : ci_env_value
|
|
280
250
|
return id unless id.nil?
|
|
281
251
|
|
|
282
|
-
triplet = [ci_node_total, branch, commit_hash]
|
|
283
|
-
return triplet.join('
|
|
252
|
+
triplet = [ci_node_total, KnapsackPro::Crypto::BranchEncryptor.call(branch), commit_hash]
|
|
253
|
+
return triplet.join('_').gsub(/[^a-z0-9\-_]+/i, "-") if triplet.all?
|
|
284
254
|
|
|
285
255
|
raise("Missing test_queue_id. See: #{KnapsackPro::Urls::KNAPSACK_PRO_TEST_QUEUE_ID}")
|
|
286
256
|
end
|
|
@@ -22,11 +22,10 @@ module KnapsackPro
|
|
|
22
22
|
]
|
|
23
23
|
|
|
24
24
|
def self.call(branch)
|
|
25
|
-
if
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
end
|
|
25
|
+
return branch if branch.nil?
|
|
26
|
+
return branch unless KnapsackPro::Config::Env.branch_encrypted?
|
|
27
|
+
|
|
28
|
+
new(branch).call
|
|
30
29
|
end
|
|
31
30
|
|
|
32
31
|
def initialize(branch)
|
|
@@ -77,7 +77,7 @@ module KnapsackPro
|
|
|
77
77
|
# Based on:
|
|
78
78
|
# https://github.com/rspec/rspec-core/blob/f8c8880dabd8f0544a6f91d8d4c857c1bd8df903/lib/rspec/core/runner.rb#L113
|
|
79
79
|
#
|
|
80
|
-
# Ignore `configuration.fail_if_no_examples
|
|
80
|
+
# Ignore `configuration.fail_if_no_examples`:
|
|
81
81
|
# * a late CI node, started after all tests were executed by other nodes, is expected to receive an empty batch
|
|
82
82
|
# * a batch could contain tests with no examples (e.g. commented out)
|
|
83
83
|
#
|
|
@@ -14,7 +14,7 @@ module KnapsackPro
|
|
|
14
14
|
|
|
15
15
|
def queue_exists?
|
|
16
16
|
raise "Connection failed. Please report this as a bug: #{KnapsackPro::Urls::SUPPORT}" if connection_failed?
|
|
17
|
-
return false if connection.api_code == KnapsackPro::Client::API::
|
|
17
|
+
return false if connection.api_code == KnapsackPro::Client::API::V2::Queues::CODE_ATTEMPT_CONNECT_TO_QUEUE_FAILED
|
|
18
18
|
|
|
19
19
|
true
|
|
20
20
|
end
|
|
@@ -24,11 +24,11 @@ module KnapsackPro
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def test_files
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
27
|
+
paths.map { |path| { "path" => path, "time_execution" => nil } }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def paths
|
|
31
|
+
response.fetch('paths')
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def id
|
|
@@ -47,31 +47,31 @@ module KnapsackPro
|
|
|
47
47
|
@repository_adapter = args.fetch(:repository_adapter)
|
|
48
48
|
@fallback_mode = false
|
|
49
49
|
@batch_index = -1
|
|
50
|
-
@batch_id = nil
|
|
51
50
|
@node_uuid = SecureRandom.uuid
|
|
51
|
+
@current_batch = nil
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
def test_file_paths(can_initialize_queue, executed_test_files,
|
|
54
|
+
def test_file_paths(can_initialize_queue, executed_test_files, time_tracker: nil)
|
|
55
55
|
@batch_index += 1
|
|
56
56
|
return [] if @fallback_mode
|
|
57
57
|
|
|
58
|
-
batch = pull_tests_from_queue(can_initialize_queue,
|
|
58
|
+
batch = pull_tests_from_queue(can_initialize_queue, time_tracker: time_tracker)
|
|
59
59
|
|
|
60
60
|
return switch_to_fallback_mode(executed_test_files: executed_test_files) if batch.connection_failed?
|
|
61
61
|
return normalize_test_files(batch.test_files) if batch.queue_exists?
|
|
62
62
|
|
|
63
63
|
test_files_result = test_suite.calculate_test_files
|
|
64
64
|
|
|
65
|
-
return try_initializing_queue(test_files_result.test_files
|
|
65
|
+
return try_initializing_queue(test_files_result.test_files.map { |test| test.fetch("path") }, time_tracker: time_tracker) if test_files_result.quick?
|
|
66
66
|
|
|
67
67
|
# The tests to run were found slowly. By that time, the queue could have already been initialized by another CI node.
|
|
68
68
|
# Attempt to pull tests from the queue to avoid the attempt to initialize the queue unnecessarily (queue initialization is an expensive request with a big test files payload).
|
|
69
|
-
batch = pull_tests_from_queue(can_initialize_queue,
|
|
69
|
+
batch = pull_tests_from_queue(can_initialize_queue, time_tracker: time_tracker)
|
|
70
70
|
|
|
71
71
|
return switch_to_fallback_mode(executed_test_files: executed_test_files) if batch.connection_failed?
|
|
72
72
|
return normalize_test_files(batch.test_files) if batch.queue_exists?
|
|
73
73
|
|
|
74
|
-
try_initializing_queue(test_files_result.test_files
|
|
74
|
+
try_initializing_queue(test_files_result.test_files.map { |test| test.fetch("path") }, time_tracker: time_tracker)
|
|
75
75
|
end
|
|
76
76
|
|
|
77
77
|
private
|
|
@@ -87,38 +87,28 @@ module KnapsackPro
|
|
|
87
87
|
KnapsackPro::TestFilePresenter.paths(decrypted_test_files)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
raise 'Test files are required when initializing a new queue.' if test_files.nil?
|
|
93
|
-
test_files = KnapsackPro::Crypto::Encryptor.call(test_files)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
KnapsackPro::Client::API::V1::Queues.queue(
|
|
97
|
-
can_initialize_queue: can_initialize_queue,
|
|
98
|
-
attempt_connect_to_queue: attempt_connect_to_queue,
|
|
99
|
-
commit_hash: repository_adapter.commit_hash,
|
|
100
|
-
branch: encrypted_branch,
|
|
101
|
-
node_total: ci_node_total,
|
|
102
|
-
node_index: ci_node_index,
|
|
103
|
-
test_files: test_files,
|
|
104
|
-
batch_uuid: batch_uuid
|
|
105
|
-
)
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def build_action_v2(can_initialize_queue:, attempt_connect_to_queue:, time_tracker:, paths: nil)
|
|
90
|
+
# time_tracker is nil for non-RSpec queue runners
|
|
91
|
+
def build_action(can_initialize_queue:, attempt_connect_to_queue:, time_tracker:, paths: nil)
|
|
109
92
|
if can_initialize_queue && !attempt_connect_to_queue
|
|
110
93
|
raise 'Test files are required when initializing a new queue.' if paths.nil?
|
|
111
94
|
paths = KnapsackPro::Crypto::Encryptor.paths(paths)
|
|
112
95
|
end
|
|
113
96
|
|
|
97
|
+
failed_paths =
|
|
98
|
+
if time_tracker
|
|
99
|
+
KnapsackPro::Crypto::Encryptor.paths(time_tracker.current_batch_failed_paths)
|
|
100
|
+
elsif @batch_index.positive?
|
|
101
|
+
KnapsackPro::Crypto::Encryptor.paths(@current_batch.paths)
|
|
102
|
+
end
|
|
103
|
+
|
|
114
104
|
KnapsackPro::Client::API::V2::Queues.queue(
|
|
115
105
|
attempt_connect_to_queue: attempt_connect_to_queue,
|
|
116
|
-
batch_id: @
|
|
106
|
+
batch_id: @current_batch&.id,
|
|
117
107
|
batch_index: @batch_index,
|
|
118
108
|
branch: encrypted_branch,
|
|
119
109
|
can_initialize_queue: can_initialize_queue,
|
|
120
110
|
commit_hash: repository_adapter.commit_hash,
|
|
121
|
-
failed_paths:
|
|
111
|
+
failed_paths: failed_paths,
|
|
122
112
|
node_index: ci_node_index,
|
|
123
113
|
node_total: ci_node_total,
|
|
124
114
|
node_uuid: @node_uuid,
|
|
@@ -126,64 +116,22 @@ module KnapsackPro
|
|
|
126
116
|
)
|
|
127
117
|
end
|
|
128
118
|
|
|
129
|
-
def pull_tests_from_queue(can_initialize_queue,
|
|
130
|
-
|
|
131
|
-
pull_tests_from_queue_v1(can_initialize_queue, batch_uuid)
|
|
132
|
-
else
|
|
133
|
-
pull_tests_from_queue_v2(can_initialize_queue, time_tracker)
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def pull_tests_from_queue_v1(can_initialize_queue, batch_uuid)
|
|
138
|
-
action = build_action_v1(can_initialize_queue: can_initialize_queue, attempt_connect_to_queue: can_initialize_queue, batch_uuid: batch_uuid)
|
|
119
|
+
def pull_tests_from_queue(can_initialize_queue, time_tracker: nil)
|
|
120
|
+
action = build_action(can_initialize_queue: can_initialize_queue, attempt_connect_to_queue: can_initialize_queue, time_tracker: time_tracker)
|
|
139
121
|
connection = KnapsackPro::Client::Connection.new(action)
|
|
140
122
|
response = connection.call
|
|
141
|
-
Batch.new(connection, response)
|
|
123
|
+
@current_batch = Batch.new(connection, response)
|
|
142
124
|
end
|
|
143
125
|
|
|
144
|
-
def
|
|
145
|
-
action =
|
|
126
|
+
def initialize_queue(paths, time_tracker: nil)
|
|
127
|
+
action = build_action(can_initialize_queue: true, attempt_connect_to_queue: false, paths: paths, time_tracker: time_tracker)
|
|
146
128
|
connection = KnapsackPro::Client::Connection.new(action)
|
|
147
129
|
response = connection.call
|
|
148
|
-
Batch.new(connection, response)
|
|
149
|
-
@batch_id = batch.id unless batch.connection_failed?
|
|
150
|
-
end
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
def initialize_queue_v1(tests_to_run, batch_uuid)
|
|
154
|
-
action = build_action_v1(can_initialize_queue: true, attempt_connect_to_queue: false, batch_uuid: batch_uuid, test_files: tests_to_run)
|
|
155
|
-
connection = KnapsackPro::Client::Connection.new(action)
|
|
156
|
-
response = connection.call
|
|
157
|
-
Batch.new(connection, response)
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
def initialize_queue_v2(paths, time_tracker)
|
|
161
|
-
action = build_action_v2(can_initialize_queue: true, attempt_connect_to_queue: false, paths: paths, time_tracker: time_tracker)
|
|
162
|
-
connection = KnapsackPro::Client::Connection.new(action)
|
|
163
|
-
response = connection.call
|
|
164
|
-
Batch.new(connection, response).tap do |batch|
|
|
165
|
-
@batch_id = batch.id unless batch.connection_failed?
|
|
166
|
-
end
|
|
130
|
+
@current_batch = Batch.new(connection, response)
|
|
167
131
|
end
|
|
168
132
|
|
|
169
|
-
def try_initializing_queue(
|
|
170
|
-
|
|
171
|
-
try_initializing_queue_v1(tests, batch_uuid)
|
|
172
|
-
else
|
|
173
|
-
paths = tests.map { |test| test.fetch("path") }
|
|
174
|
-
try_initializing_queue_v2(paths, time_tracker)
|
|
175
|
-
end
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def try_initializing_queue_v1(tests, batch_uuid)
|
|
179
|
-
result = initialize_queue_v1(tests, batch_uuid)
|
|
180
|
-
return switch_to_fallback_mode(executed_test_files: []) if result.connection_failed?
|
|
181
|
-
|
|
182
|
-
normalize_test_files(result.test_files)
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
def try_initializing_queue_v2(paths, time_tracker)
|
|
186
|
-
result = initialize_queue_v2(paths, time_tracker)
|
|
133
|
+
def try_initializing_queue(paths, time_tracker: nil)
|
|
134
|
+
result = initialize_queue(paths, time_tracker: time_tracker)
|
|
187
135
|
return switch_to_fallback_mode(executed_test_files: []) if result.connection_failed?
|
|
188
136
|
|
|
189
137
|
normalize_test_files(result.test_files)
|
|
@@ -193,14 +141,11 @@ module KnapsackPro
|
|
|
193
141
|
@fallback_mode = true
|
|
194
142
|
|
|
195
143
|
if !KnapsackPro::Config::Env.fallback_mode_enabled?
|
|
196
|
-
message = "Fallback Mode was disabled with KNAPSACK_PRO_FALLBACK_MODE_ENABLED=false. Please restart this CI node to retry tests. Most likely Fallback Mode was disabled due to #{KnapsackPro::Urls::
|
|
144
|
+
message = "Fallback Mode was disabled with KNAPSACK_PRO_FALLBACK_MODE_ENABLED=false. Please restart this CI node to retry tests. Most likely Fallback Mode was disabled due to #{KnapsackPro::Urls::NORMAL_MODE__CONNECTION_ERROR_WITH_FALLBACK_ENABLED_FALSE}"
|
|
197
145
|
KnapsackPro.logger.error(message)
|
|
198
146
|
raise FallbackModeError.new(message)
|
|
199
147
|
elsif KnapsackPro::Config::Env.ci_node_retry_count > 0
|
|
200
|
-
message = "knapsack_pro gem could not connect to Knapsack Pro API and the Fallback Mode cannot be used this time. Running tests in Fallback Mode are not allowed for retried parallel CI node to avoid running the wrong set of tests. Please manually retry this parallel job on your CI server then knapsack_pro gem will try to connect to Knapsack Pro API again and will run a correct set of tests for this CI node. Learn more #{KnapsackPro::Urls::
|
|
201
|
-
unless KnapsackPro::Config::Env.fixed_queue_split?
|
|
202
|
-
message += " Please ensure you have set KNAPSACK_PRO_FIXED_QUEUE_SPLIT=true to allow Knapsack Pro API remember the recorded CI node tests so when you retry failed tests on the CI node then the same set of tests will be executed. See more #{KnapsackPro::Urls::FIXED_QUEUE_SPLIT}"
|
|
203
|
-
end
|
|
148
|
+
message = "knapsack_pro gem could not connect to Knapsack Pro API and the Fallback Mode cannot be used this time. Running tests in Fallback Mode are not allowed for retried parallel CI node to avoid running the wrong set of tests. Please manually retry this parallel job on your CI server then knapsack_pro gem will try to connect to Knapsack Pro API again and will run a correct set of tests for this CI node. Learn more #{KnapsackPro::Urls::NORMAL_MODE__CONNECTION_ERROR_WITH_FALLBACK_ENABLED_TRUE_AND_POSITIVE_RETRY_COUNT}"
|
|
204
149
|
KnapsackPro.logger.error(message)
|
|
205
150
|
raise FallbackModeError.new(message)
|
|
206
151
|
else
|
|
@@ -12,7 +12,7 @@ module KnapsackPro
|
|
|
12
12
|
|
|
13
13
|
def exists?
|
|
14
14
|
raise "Connection failed. Please report this as a bug: #{KnapsackPro::Urls::SUPPORT}" if connection_failed?
|
|
15
|
-
return false if connection.api_code == KnapsackPro::Client::API::
|
|
15
|
+
return false if connection.api_code == KnapsackPro::Client::API::V2::Queues::CODE_ATTEMPT_CONNECT_TO_QUEUE_FAILED
|
|
16
16
|
|
|
17
17
|
true
|
|
18
18
|
end
|
|
@@ -22,7 +22,11 @@ module KnapsackPro
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def test_files
|
|
25
|
-
response.fetch('
|
|
25
|
+
response.fetch('paths').map { |path| { "path" => path, "time_execution" => nil } }
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def id
|
|
29
|
+
response.fetch('batch_id', nil)
|
|
26
30
|
end
|
|
27
31
|
|
|
28
32
|
private
|
|
@@ -74,33 +78,53 @@ module KnapsackPro
|
|
|
74
78
|
end
|
|
75
79
|
|
|
76
80
|
def build_action(cache_read_attempt:, test_files: nil)
|
|
77
|
-
|
|
81
|
+
if cache_read_attempt # connect
|
|
82
|
+
KnapsackPro::Client::API::V2::Queues.queue(
|
|
83
|
+
attempt_connect_to_queue: true,
|
|
84
|
+
batch_index: 0,
|
|
85
|
+
branch: encrypted_branch,
|
|
86
|
+
can_initialize_queue: true,
|
|
87
|
+
commit_hash: repository_adapter.commit_hash,
|
|
88
|
+
node_index: ci_node_index,
|
|
89
|
+
node_total: ci_node_total,
|
|
90
|
+
node_uuid: ENV.fetch('KNAPSACK_PRO_NODE_UUID'),
|
|
91
|
+
single_batch: true
|
|
92
|
+
)
|
|
93
|
+
else # initialize
|
|
78
94
|
raise 'Test files are required when initializing a new test suite split.' if test_files.nil?
|
|
79
|
-
|
|
95
|
+
paths = test_files.map { |test_file| test_file.fetch("path") }
|
|
96
|
+
|
|
97
|
+
KnapsackPro::Client::API::V2::Queues.queue(
|
|
98
|
+
attempt_connect_to_queue: false,
|
|
99
|
+
batch_index: 0,
|
|
100
|
+
branch: encrypted_branch,
|
|
101
|
+
can_initialize_queue: true,
|
|
102
|
+
commit_hash: repository_adapter.commit_hash,
|
|
103
|
+
node_index: ci_node_index,
|
|
104
|
+
node_total: ci_node_total,
|
|
105
|
+
node_uuid: ENV.fetch('KNAPSACK_PRO_NODE_UUID'),
|
|
106
|
+
paths: KnapsackPro::Crypto::Encryptor.paths(paths),
|
|
107
|
+
single_batch: true
|
|
108
|
+
)
|
|
80
109
|
end
|
|
81
|
-
|
|
82
|
-
KnapsackPro::Client::API::V1::BuildDistributions.subset(
|
|
83
|
-
cache_read_attempt: cache_read_attempt,
|
|
84
|
-
commit_hash: repository_adapter.commit_hash,
|
|
85
|
-
branch: encrypted_branch,
|
|
86
|
-
node_total: ci_node_total,
|
|
87
|
-
node_index: ci_node_index,
|
|
88
|
-
test_files: test_files,
|
|
89
|
-
)
|
|
90
110
|
end
|
|
91
111
|
|
|
92
112
|
def pull_tests
|
|
93
113
|
action = build_action(cache_read_attempt: true)
|
|
94
114
|
connection = KnapsackPro::Client::Connection.new(action)
|
|
95
115
|
response = connection.call
|
|
96
|
-
Split.new(connection, response)
|
|
116
|
+
Split.new(connection, response).tap do |batch|
|
|
117
|
+
ENV['KNAPSACK_PRO_BATCH_ID'] = batch.id.to_s unless batch.connection_failed?
|
|
118
|
+
end
|
|
97
119
|
end
|
|
98
120
|
|
|
99
121
|
def initialize_test_suite_split(tests_to_run)
|
|
100
122
|
action = build_action(cache_read_attempt: false, test_files: tests_to_run)
|
|
101
123
|
connection = KnapsackPro::Client::Connection.new(action)
|
|
102
124
|
response = connection.call
|
|
103
|
-
Split.new(connection, response)
|
|
125
|
+
Split.new(connection, response).tap do |batch|
|
|
126
|
+
ENV['KNAPSACK_PRO_BATCH_ID'] = batch.id.to_s unless batch.connection_failed?
|
|
127
|
+
end
|
|
104
128
|
end
|
|
105
129
|
|
|
106
130
|
def try_initializing_test_suite_split(tests)
|
|
@@ -113,15 +137,12 @@ module KnapsackPro
|
|
|
113
137
|
|
|
114
138
|
def switch_to_fallback_mode
|
|
115
139
|
if !KnapsackPro::Config::Env.fallback_mode_enabled?
|
|
116
|
-
message = "Fallback Mode was disabled with KNAPSACK_PRO_FALLBACK_MODE_ENABLED=false. Please restart this CI node to retry tests. Most likely Fallback Mode was disabled due to #{KnapsackPro::Urls::
|
|
140
|
+
message = "Fallback Mode was disabled with KNAPSACK_PRO_FALLBACK_MODE_ENABLED=false. Please restart this CI node to retry tests. Most likely Fallback Mode was disabled due to #{KnapsackPro::Urls::SINGLE_BATCH_MODE__CONNECTION_ERROR_WITH_FALLBACK_ENABLED_FALSE}"
|
|
117
141
|
KnapsackPro.logger.error(message)
|
|
118
142
|
exit_code = KnapsackPro::Config::Env.fallback_mode_error_exit_code
|
|
119
143
|
Kernel.exit(exit_code)
|
|
120
144
|
elsif KnapsackPro::Config::Env.ci_node_retry_count > 0
|
|
121
|
-
message = "knapsack_pro gem could not connect to Knapsack Pro API and the Fallback Mode cannot be used this time. Running tests in Fallback Mode are not allowed for retried parallel CI node to avoid running the wrong set of tests. Please manually retry this parallel job on your CI server then knapsack_pro gem will try to connect to Knapsack Pro API again and will run a correct set of tests for this CI node. Learn more #{KnapsackPro::Urls::
|
|
122
|
-
unless KnapsackPro::Config::Env.fixed_test_suite_split?
|
|
123
|
-
message += " Please ensure you have set KNAPSACK_PRO_FIXED_TEST_SUITE_SPLIT=true to allow Knapsack Pro API remember the recorded CI node tests so when you retry failed tests on the CI node then the same set of tests will be executed. See more #{KnapsackPro::Urls::FIXED_TEST_SUITE_SPLIT}"
|
|
124
|
-
end
|
|
145
|
+
message = "knapsack_pro gem could not connect to Knapsack Pro API and the Fallback Mode cannot be used this time. Running tests in Fallback Mode are not allowed for retried parallel CI node to avoid running the wrong set of tests. Please manually retry this parallel job on your CI server then knapsack_pro gem will try to connect to Knapsack Pro API again and will run a correct set of tests for this CI node. Learn more #{KnapsackPro::Urls::SINGLE_BATCH_MODE__CONNECTION_ERROR_WITH_FALLBACK_ENABLED_TRUE_AND_POSITIVE_RETRY_COUNT}"
|
|
125
146
|
KnapsackPro.logger.error(message)
|
|
126
147
|
exit_code = KnapsackPro::Config::Env.fallback_mode_error_exit_code
|
|
127
148
|
Kernel.exit(exit_code)
|
data/lib/knapsack_pro/report.rb
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
module KnapsackPro
|
|
4
4
|
class Report
|
|
5
|
-
def self.save(
|
|
6
|
-
if
|
|
7
|
-
|
|
5
|
+
def self.save(test_files = nil)
|
|
6
|
+
if test_files.nil?
|
|
7
|
+
test_files = KnapsackPro.tracker.to_a
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
if
|
|
11
|
-
KnapsackPro.logger.
|
|
12
|
-
KnapsackPro.logger.debug("When you use knapsack_pro Regular Mode, the reason for no tests executing might be a very narrow tests list. Most likely, you run only tests with a specified tag, and there were fewer test files with the tag than parallel CI nodes.")
|
|
10
|
+
if test_files.empty?
|
|
11
|
+
KnapsackPro.logger.info("No tests were executed because the test queue is empty.")
|
|
13
12
|
end
|
|
14
13
|
|
|
15
|
-
|
|
14
|
+
acknowledge_single_batch(test_files)
|
|
15
|
+
create_build_subset(test_files)
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def self.save_subset_queue_to_file
|
|
@@ -59,6 +59,35 @@ module KnapsackPro
|
|
|
59
59
|
create_build_subset(test_files)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
|
+
# TODO: Add support for Retry-only-Failures
|
|
63
|
+
def self.acknowledge_single_batch(test_files)
|
|
64
|
+
repository_adapter = KnapsackPro::RepositoryAdapterInitiator.call
|
|
65
|
+
paths = KnapsackPro::Utils.unsymbolize(test_files).map { |test_file| test_file.fetch('path') }
|
|
66
|
+
failed_paths = KnapsackPro::Crypto::Encryptor.paths(paths)
|
|
67
|
+
action = KnapsackPro::Client::API::V2::Queues.queue(
|
|
68
|
+
attempt_connect_to_queue: false,
|
|
69
|
+
batch_id: ENV['KNAPSACK_PRO_BATCH_ID'], # nil in Fallback Mode
|
|
70
|
+
batch_index: 1,
|
|
71
|
+
branch: KnapsackPro::Crypto::BranchEncryptor.call(repository_adapter.branch),
|
|
72
|
+
can_initialize_queue: false,
|
|
73
|
+
commit_hash: repository_adapter.commit_hash,
|
|
74
|
+
failed_paths: failed_paths,
|
|
75
|
+
node_total: KnapsackPro::Config::Env.ci_node_total,
|
|
76
|
+
node_index: KnapsackPro::Config::Env.ci_node_index,
|
|
77
|
+
node_uuid: ENV.fetch('KNAPSACK_PRO_NODE_UUID'),
|
|
78
|
+
single_batch: true
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
connection = KnapsackPro::Client::Connection.new(action)
|
|
82
|
+
response = connection.call
|
|
83
|
+
if connection.success?
|
|
84
|
+
raise ArgumentError.new(response) if connection.errors?
|
|
85
|
+
KnapsackPro.logger.debug('Acknowledged single batch.')
|
|
86
|
+
else
|
|
87
|
+
KnapsackPro.logger.warn('Single batch was not acknowledged due to connection problems.')
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
62
91
|
def self.create_build_subset(test_files)
|
|
63
92
|
repository_adapter = KnapsackPro::RepositoryAdapterInitiator.call
|
|
64
93
|
test_files = KnapsackPro::Utils.unsymbolize(test_files)
|
|
@@ -77,7 +106,7 @@ module KnapsackPro
|
|
|
77
106
|
raise ArgumentError.new(response) if connection.errors?
|
|
78
107
|
KnapsackPro.logger.debug('Saved time execution report on Knapsack Pro API server.')
|
|
79
108
|
else
|
|
80
|
-
KnapsackPro.logger.warn('Time execution report was not saved on Knapsack Pro API server due to connection
|
|
109
|
+
KnapsackPro.logger.warn('Time execution report was not saved on Knapsack Pro API server due to connection problems.')
|
|
81
110
|
end
|
|
82
111
|
end
|
|
83
112
|
|
|
@@ -4,11 +4,11 @@ module KnapsackPro
|
|
|
4
4
|
module RepositoryAdapters
|
|
5
5
|
class GitAdapter < BaseAdapter
|
|
6
6
|
def commit_hash
|
|
7
|
-
|
|
7
|
+
ENV['KNAPSACK_PRO_COMMIT_HASH'] || git_commit_hash
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def branch
|
|
11
|
-
|
|
11
|
+
ENV['KNAPSACK_PRO_BRANCH'] || git_branch
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def branches
|
|
@@ -42,6 +42,14 @@ module KnapsackPro
|
|
|
42
42
|
|
|
43
43
|
private
|
|
44
44
|
|
|
45
|
+
def git_commit_hash
|
|
46
|
+
`git -C "#{working_dir}" rev-parse HEAD`.strip
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def git_branch
|
|
50
|
+
`git -C "#{working_dir}" rev-parse --abbrev-ref HEAD`.strip
|
|
51
|
+
end
|
|
52
|
+
|
|
45
53
|
def git_commit_authors
|
|
46
54
|
git_unshallow if KnapsackPro::Config::Env.ci? && shallow_repository?
|
|
47
55
|
`git log --since "one month ago" 2>/dev/null | git shortlog --summary --email 2>/dev/null`
|
|
@@ -5,7 +5,8 @@ module KnapsackPro
|
|
|
5
5
|
class CucumberRunner < BaseRunner
|
|
6
6
|
def self.run(args)
|
|
7
7
|
ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_cucumber
|
|
8
|
-
ENV['
|
|
8
|
+
ENV['KNAPSACK_PRO_SINGLE_BATCH_MODE_ENABLED'] = 'true'
|
|
9
|
+
ENV['KNAPSACK_PRO_NODE_UUID'] = SecureRandom.uuid
|
|
9
10
|
|
|
10
11
|
adapter_class = KnapsackPro::Adapters::CucumberAdapter
|
|
11
12
|
KnapsackPro::Config::Env.set_test_runner_adapter(adapter_class)
|
|
@@ -5,7 +5,8 @@ module KnapsackPro
|
|
|
5
5
|
class MinitestRunner < BaseRunner
|
|
6
6
|
def self.run(args)
|
|
7
7
|
ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_minitest
|
|
8
|
-
ENV['
|
|
8
|
+
ENV['KNAPSACK_PRO_SINGLE_BATCH_MODE_ENABLED'] = 'true'
|
|
9
|
+
ENV['KNAPSACK_PRO_NODE_UUID'] = SecureRandom.uuid
|
|
9
10
|
|
|
10
11
|
adapter_class = KnapsackPro::Adapters::MinitestAdapter
|
|
11
12
|
KnapsackPro::Config::Env.set_test_runner_adapter(adapter_class)
|
|
@@ -5,7 +5,6 @@ module KnapsackPro
|
|
|
5
5
|
module Queue
|
|
6
6
|
class BaseRunner
|
|
7
7
|
TerminationError = Class.new(StandardError)
|
|
8
|
-
TERMINATION_SIGNALS = %w(HUP INT TERM ABRT QUIT USR1 USR2)
|
|
9
8
|
|
|
10
9
|
@@terminate_process = false
|
|
11
10
|
|
|
@@ -56,17 +55,27 @@ module KnapsackPro
|
|
|
56
55
|
end
|
|
57
56
|
|
|
58
57
|
def trap_signals
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
Signal.trap("TERM") {
|
|
59
|
+
puts "SIGTERM received: Terminating Knapsack Pro..."
|
|
60
|
+
@@terminate_process = true
|
|
61
|
+
post_trap_signals(debug: true)
|
|
62
|
+
log_threads
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
Signal.trap("INT") {
|
|
66
|
+
if @@terminate_process
|
|
67
|
+
puts "SIGINT received: Terminated Knapsack Pro."
|
|
68
|
+
$stdout.flush
|
|
69
|
+
exit!(1)
|
|
70
|
+
else
|
|
71
|
+
puts "SIGINT received: Terminating Knapsack Pro... Interrupt again to force quit."
|
|
62
72
|
@@terminate_process = true
|
|
63
73
|
post_trap_signals
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
end
|
|
74
|
+
end
|
|
75
|
+
}
|
|
67
76
|
end
|
|
68
77
|
|
|
69
|
-
def post_trap_signals
|
|
78
|
+
def post_trap_signals(debug: false)
|
|
70
79
|
end
|
|
71
80
|
|
|
72
81
|
def log_threads
|
|
@@ -8,7 +8,7 @@ module KnapsackPro
|
|
|
8
8
|
require 'cucumber/rake/task'
|
|
9
9
|
|
|
10
10
|
ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_cucumber
|
|
11
|
-
ENV['
|
|
11
|
+
ENV['KNAPSACK_PRO_NORMAL_MODE_ENABLED'] = 'true'
|
|
12
12
|
ENV['KNAPSACK_PRO_QUEUE_ID'] = KnapsackPro::Config::EnvGenerator.set_queue_id
|
|
13
13
|
|
|
14
14
|
adapter_class = KnapsackPro::Adapters::CucumberAdapter
|
|
@@ -12,7 +12,7 @@ module KnapsackPro
|
|
|
12
12
|
::Minitest.class_variable_set('@@installed_at_exit', true)
|
|
13
13
|
|
|
14
14
|
ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_minitest
|
|
15
|
-
ENV['
|
|
15
|
+
ENV['KNAPSACK_PRO_NORMAL_MODE_ENABLED'] = 'true'
|
|
16
16
|
ENV['KNAPSACK_PRO_QUEUE_ID'] = KnapsackPro::Config::EnvGenerator.set_queue_id
|
|
17
17
|
|
|
18
18
|
adapter_class = KnapsackPro::Adapters::MinitestAdapter
|
|
@@ -118,10 +118,10 @@ module KnapsackPro
|
|
|
118
118
|
|
|
119
119
|
private
|
|
120
120
|
|
|
121
|
-
def post_trap_signals
|
|
121
|
+
def post_trap_signals(debug: false)
|
|
122
122
|
RSpec.world.wants_to_quit = true
|
|
123
123
|
|
|
124
|
-
log_current_batch_rspec_command
|
|
124
|
+
log_current_batch_rspec_command if debug
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
def log_current_tests(threads)
|
|
@@ -144,7 +144,7 @@ module KnapsackPro
|
|
|
144
144
|
end
|
|
145
145
|
|
|
146
146
|
def pre_run_setup
|
|
147
|
-
ENV['
|
|
147
|
+
ENV['KNAPSACK_PRO_NORMAL_MODE_ENABLED'] = 'true'
|
|
148
148
|
ENV['KNAPSACK_PRO_QUEUE_ID'] = KnapsackPro::Config::EnvGenerator.set_queue_id
|
|
149
149
|
|
|
150
150
|
KnapsackPro::Config::Env.set_test_runner_adapter(@adapter_class)
|
|
@@ -7,8 +7,9 @@ module KnapsackPro
|
|
|
7
7
|
require 'rspec/core'
|
|
8
8
|
|
|
9
9
|
ENV['KNAPSACK_PRO_TEST_SUITE_TOKEN'] = KnapsackPro::Config::Env.test_suite_token_rspec
|
|
10
|
-
ENV['
|
|
10
|
+
ENV['KNAPSACK_PRO_SINGLE_BATCH_MODE_ENABLED'] = 'true'
|
|
11
11
|
ENV['KNAPSACK_PRO_RSPEC_OPTIONS'] = args.to_s
|
|
12
|
+
ENV['KNAPSACK_PRO_NODE_UUID'] = SecureRandom.uuid
|
|
12
13
|
|
|
13
14
|
adapter_class = KnapsackPro::Adapters::RSpecAdapter
|
|
14
15
|
KnapsackPro::Config::Env.set_test_runner_adapter(adapter_class)
|