solano 1.31.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +15 -0
  2. data/bin/solano +29 -0
  3. data/bin/tddium +29 -0
  4. data/lib/solano.rb +19 -0
  5. data/lib/solano/agent.rb +3 -0
  6. data/lib/solano/agent/solano.rb +128 -0
  7. data/lib/solano/cli.rb +25 -0
  8. data/lib/solano/cli/api.rb +368 -0
  9. data/lib/solano/cli/commands/account.rb +50 -0
  10. data/lib/solano/cli/commands/activate.rb +16 -0
  11. data/lib/solano/cli/commands/api.rb +15 -0
  12. data/lib/solano/cli/commands/config.rb +78 -0
  13. data/lib/solano/cli/commands/console.rb +85 -0
  14. data/lib/solano/cli/commands/describe.rb +104 -0
  15. data/lib/solano/cli/commands/find_failing.rb +64 -0
  16. data/lib/solano/cli/commands/heroku.rb +17 -0
  17. data/lib/solano/cli/commands/hg.rb +48 -0
  18. data/lib/solano/cli/commands/keys.rb +81 -0
  19. data/lib/solano/cli/commands/login.rb +37 -0
  20. data/lib/solano/cli/commands/logout.rb +14 -0
  21. data/lib/solano/cli/commands/password.rb +26 -0
  22. data/lib/solano/cli/commands/rerun.rb +59 -0
  23. data/lib/solano/cli/commands/server.rb +21 -0
  24. data/lib/solano/cli/commands/spec.rb +401 -0
  25. data/lib/solano/cli/commands/status.rb +117 -0
  26. data/lib/solano/cli/commands/stop.rb +19 -0
  27. data/lib/solano/cli/commands/suite.rb +110 -0
  28. data/lib/solano/cli/commands/support.rb +24 -0
  29. data/lib/solano/cli/commands/web.rb +29 -0
  30. data/lib/solano/cli/config.rb +246 -0
  31. data/lib/solano/cli/params_helper.rb +66 -0
  32. data/lib/solano/cli/prompt.rb +128 -0
  33. data/lib/solano/cli/show.rb +136 -0
  34. data/lib/solano/cli/solano.rb +208 -0
  35. data/lib/solano/cli/suite.rb +104 -0
  36. data/lib/solano/cli/text_helper.rb +16 -0
  37. data/lib/solano/cli/timeformat.rb +21 -0
  38. data/lib/solano/cli/util.rb +132 -0
  39. data/lib/solano/constant.rb +581 -0
  40. data/lib/solano/scm.rb +18 -0
  41. data/lib/solano/scm/configure.rb +37 -0
  42. data/lib/solano/scm/git.rb +349 -0
  43. data/lib/solano/scm/git_log_parser.rb +67 -0
  44. data/lib/solano/scm/hg.rb +263 -0
  45. data/lib/solano/scm/hg_log_parser.rb +66 -0
  46. data/lib/solano/scm/scm.rb +119 -0
  47. data/lib/solano/scm/scm_stub.rb +9 -0
  48. data/lib/solano/scm/url.rb +75 -0
  49. data/lib/solano/script.rb +12 -0
  50. data/lib/solano/script/git-remote-hg +1258 -0
  51. data/lib/solano/ssh.rb +66 -0
  52. data/lib/solano/util.rb +63 -0
  53. data/lib/solano/version.rb +5 -0
  54. metadata +413 -0
@@ -0,0 +1,81 @@
1
+ # Copyright (c) 2011-2015 Solano Labs All Rights Reserved
2
+
3
+ module Solano
4
+ class SolanoCli < Thor
5
+ desc "keys", "List SSH keys authorized for Solano CI"
6
+ def keys
7
+ user_details = solano_setup({:scm => false})
8
+
9
+ begin
10
+ if user_details then
11
+ show_third_party_keys_details(user_details)
12
+ end
13
+
14
+ keys_details = @solano_api.get_keys
15
+ show_keys_details(keys_details)
16
+ rescue TddiumClient::Error::API => e
17
+ exit_failure Text::Error::LIST_KEYS_ERROR
18
+ end
19
+ end
20
+
21
+ desc "keys:add [NAME] [PATH]", "Authorize an existing keypair for Solano CI"
22
+ method_option :dir, :type=>:string, :default=>nil
23
+ define_method "keys:add" do |name, path|
24
+ solano_setup({:scm => false})
25
+
26
+ path = File.expand_path(path)
27
+
28
+ output_dir = options[:dir] || ENV['SOLANO_GEM_KEY_DIR']
29
+ output_dir ||= Default::SSH_OUTPUT_DIR
30
+
31
+ begin
32
+ keydata = Solano::Ssh.validate_keys name, path, @solano_api
33
+ say Text::Process::ADD_KEYS_ADD % name
34
+ result = @solano_api.set_keys({:keys => [keydata]})
35
+
36
+ priv_path = path.sub(/[.]pub$/, '')
37
+ say Text::Process::ADD_KEYS_ADD_DONE % [name, priv_path, result["git_server"] || Default::GIT_SERVER, priv_path]
38
+ rescue TddiumClient::Error::API => e
39
+ exit_failure Text::Error::ADD_KEYS_ERROR % name
40
+ rescue SolanoError => e
41
+ exit_failure e.message
42
+ end
43
+ end
44
+
45
+ map "generate" => :gen
46
+ desc "keys:gen [NAME]", "Generate and authorize a keypair for Solano CI"
47
+ method_option :dir, :type=>:string, :default=>nil
48
+ define_method "keys:gen" do |name|
49
+ solano_setup({:scm => false})
50
+
51
+ output_dir = options[:dir] || ENV['SOLANO_GEM_KEY_DIR']
52
+ output_dir ||= Default::SSH_OUTPUT_DIR
53
+
54
+ begin
55
+ keydata = Solano::Ssh.validate_keys name, output_dir, @solano_api, true
56
+ say Text::Process::ADD_KEYS_GENERATE % name
57
+
58
+ result = @solano_api.set_keys({:keys => [keydata]})
59
+ outfile = File.expand_path(File.join(output_dir, "identity.solano.#{name}"))
60
+ say Text::Process::ADD_KEYS_GENERATE_DONE % [name, result["git_server"] || Default::GIT_SERVER, outfile]
61
+ rescue TddiumClient::Error::API => e
62
+ exit_failure Text::Error::ADD_KEYS_ERROR % name
63
+ rescue SolanoError => e
64
+ exit_failure e.message
65
+ end
66
+ end
67
+
68
+ desc "keys:remove [NAME]", "Remove a key that was authorized for Solano CI"
69
+ define_method "keys:remove" do |name|
70
+ solano_setup({:scm => false})
71
+
72
+ begin
73
+ say Text::Process::REMOVE_KEYS % name
74
+ result = @solano_api.delete_keys(name)
75
+ say Text::Process::REMOVE_KEYS_DONE % name
76
+ rescue TddiumClient::Error::API => e
77
+ exit_failure Text::Error::REMOVE_KEYS_ERROR % name
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright (c) 2011-2015 Solano Labs All Rights Reserved
2
+
3
+ module Solano
4
+ class SolanoCli < Thor
5
+ desc "login [[TOKEN]]", "Log in using your email address or token (see: https://ci.solanolabs.com/user_settings/token)"
6
+ method_option :email, :type => :string, :default => nil
7
+ method_option :password, :type => :string, :default => nil
8
+ method_option :ssh_key_file, :type => :string, :default => nil
9
+ def login(*args)
10
+ user_details = solano_setup({:login => false, :scm => false})
11
+
12
+ login_options = options.dup
13
+
14
+ if args.first && args.first =~ /@/
15
+ login_options[:email] ||= args.first
16
+ elsif args.first
17
+ # assume cli token
18
+ login_options[:cli_token] = args.first
19
+ end
20
+
21
+ if user_details then
22
+ say Text::Process::ALREADY_LOGGED_IN
23
+ elsif user = @solano_api.login_user(:params => @solano_api.get_user_credentials(login_options), :show_error => true)
24
+ say Text::Process::LOGGED_IN_SUCCESSFULLY
25
+ if @scm.repo? then
26
+ @api_config.populate_branches(@solano_api.current_branch)
27
+ end
28
+ @api_config.write_config
29
+ else
30
+ exit_failure
31
+ end
32
+ if prompt_missing_ssh_key then
33
+ say Text::Process::NEXT_STEPS
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,14 @@
1
+ # Copyright (c) 2011-2015 Solano Labs All Rights Reserved
2
+
3
+ module Solano
4
+ class SolanoCli < Thor
5
+ desc "logout", "Log out of solano"
6
+ def logout
7
+ solano_setup({:login => false, :scm => false})
8
+
9
+ @api_config.logout
10
+
11
+ say Text::Process::LOGGED_OUT_SUCCESSFULLY
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,26 @@
1
+ # Copyright (c) 2011-2015 Solano Labs All Rights Reserved
2
+
3
+ module Solano
4
+ class SolanoCli < Thor
5
+ desc "password", "Change password"
6
+ map "passwd" => :password
7
+ def password
8
+ user_details = solano_setup({:scm => false})
9
+
10
+ params = {}
11
+ params[:current_password] = HighLine.ask(Text::Prompt::CURRENT_PASSWORD) { |q| q.echo = "*" }
12
+ params[:password] = HighLine.ask(Text::Prompt::NEW_PASSWORD) { |q| q.echo = "*" }
13
+ params[:password_confirmation] = HighLine.ask(Text::Prompt::PASSWORD_CONFIRMATION) { |q| q.echo = "*" }
14
+
15
+ begin
16
+ user_id = user_details["id"]
17
+ @solano_api.update_user(user_id, {:user => params})
18
+ say Text::Process::PASSWORD_CHANGED
19
+ rescue TddiumClient::Error::API => e
20
+ exit_failure Text::Error::PASSWORD_ERROR % e.explanation
21
+ rescue TddiumClient::Error::Base => e
22
+ exit_failure e.message
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,59 @@
1
+ # Copyright (c) 2011-2015 Solano Labs All Rights Reserved
2
+
3
+ module Solano
4
+ class SolanoCli < Thor
5
+ desc "rerun SESSION", "Rerun failing tests from a session"
6
+ method_option :account, :type => :string, :default => nil,
7
+ :aliases => %w(--org --organization)
8
+ method_option :max_parallelism, :type => :numeric, :default => nil
9
+ method_option :no_op, :type=>:boolean, :default => false, :aliases => ["-n"]
10
+ method_option :force, :type=>:boolean, :default => false
11
+ method_option :profile, :type => :string, :default => nil, :aliases => %w(--profile-name)
12
+ def rerun(session_id=nil)
13
+ params = {:scm => true, :repo => false}
14
+ if session_id.nil? then
15
+ params = {:repo => true, :suite => true}
16
+ end
17
+ solano_setup(params)
18
+
19
+ session_id ||= session_id_for_current_suite
20
+
21
+ begin
22
+ result = @solano_api.query_session_tests(session_id)
23
+ rescue TddiumClient::Error::API => e
24
+ exit_failure Text::Error::NO_SESSION_EXISTS
25
+ end
26
+
27
+ tests = result['session']['tests']
28
+ tests = tests.select{ |t| [
29
+ 'failed', 'error', 'notstarted', 'started'].include?(t['status']) }
30
+ tests = tests.map{ |t| t['test_name'] }
31
+
32
+ profile = options[:profile] || result['non_passed_profile_name']
33
+
34
+ cmd = "solano run"
35
+ cmd += " --max-parallelism=#{options[:max_parallelism]}" if options[:max_parallelism]
36
+ cmd += " --org=#{options[:account]}" if options[:account]
37
+ cmd += " --force" if options[:force]
38
+ cmd += " --profile=#{profile}" if profile
39
+ cmd += " #{tests.join(" ")}"
40
+
41
+ say cmd
42
+ Kernel.exec(cmd) if !options[:no_op]
43
+ end
44
+
45
+ private
46
+
47
+ def session_id_for_current_suite
48
+ return unless suite_for_current_branch?
49
+ suite_params = {
50
+ :suite_id => @solano_api.current_suite_id,
51
+ :active => false,
52
+ :limit => 1,
53
+ :origin => %w(ci cli)
54
+ }
55
+ session = @solano_api.get_sessions(suite_params)
56
+ session[0]["id"]
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,21 @@
1
+ # Copyright (c) 2014, 2015 Solano Labs All Rights Reserved
2
+
3
+ module Solano
4
+ class SolanoCli < Thor
5
+ desc 'server', "displays the saved connection info"
6
+ def server
7
+ solano_setup({:scm => false, :login => false})
8
+ self.class.display
9
+ end
10
+
11
+ desc 'server:set --host HOST [--port PORT] [--proto PROTO] [--insecure]', "saves connection info"
12
+ method_option :host, type: :string, required: true
13
+ method_option :port, type: :numeric, default: 443
14
+ method_option :proto, type: :string, default: 'https'
15
+ method_option :insecure, type: :boolean, default: false
16
+ define_method 'server:set' do
17
+ solano_setup({:scm => false, :login => false})
18
+ self.class.write_params options
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,401 @@
1
+ # Copyright (c) 2011-2016 Solano Labs All Rights Reserved
2
+ require 'digest'
3
+ require 'net/http'
4
+
5
+ module Solano
6
+ class SolanoCli < Thor
7
+ map "cucumber" => :spec
8
+ map "test" => :spec
9
+ map "run" => :spec
10
+ desc "run [PATTERN]", "Run the test suite, or tests that match PATTERN"
11
+ method_option :account, :type => :string, :default => nil,
12
+ :aliases => %w(--org --organization)
13
+ method_option :tag, :type => :string, :default => nil
14
+ method_option :user_data_file, :type => :string, :default => nil
15
+ method_option :max_parallelism, :type => :numeric, :default => nil
16
+ method_option :test_pattern, :type => :string, :default => nil
17
+ method_option :test_exclude_pattern, :type => :string, :default => nil
18
+ method_option :force, :type => :boolean, :default => false
19
+ method_option :quiet, :type => :boolean, :default => false
20
+ method_option :machine, :type => :boolean, :default => false
21
+ method_option :session_id, :type => :numeric, :default => nil
22
+ method_option :tool, :type => :hash, :default => {}
23
+ method_option :env, :type=>:hash, :default => {}
24
+ method_option :profile, :type => :string, :default => nil, :aliases => %w(--profile-name)
25
+ method_option :queue, :type => :string, :default => nil
26
+ method_option :session_manager, :type => :string, :default => nil
27
+ method_option :default_branch, :type => :string, :default => nil
28
+ method_option :force_snapshot, :type => :boolean, :default => false
29
+ method_option :volume, :type => :string, :default => nil
30
+ def spec(*pattern)
31
+ machine_data = {}
32
+
33
+ solano_setup({:repo => true})
34
+
35
+ suite_auto_configure unless options[:machine]
36
+
37
+ exit_failure unless suite_for_current_branch?
38
+
39
+ if !options[:machine] && @solano_api.get_keys.empty? then
40
+ warn(Text::Warning::NO_SSH_KEY)
41
+ end
42
+
43
+ if @scm.changes?(options) then
44
+ exit_failure(Text::Error::SCM_CHANGES_NOT_COMMITTED) if !options[:force]
45
+ warn(Text::Warning::SCM_CHANGES_NOT_COMMITTED)
46
+ end
47
+
48
+ test_execution_params = {}
49
+
50
+ if user_data_file_path = options[:user_data_file] then
51
+ if File.exists?(user_data_file_path) then
52
+ user_data = File.open(user_data_file_path) { |file| file.read }
53
+ test_execution_params[:user_data_text] = Base64.encode64(user_data)
54
+ test_execution_params[:user_data_filename] = File.basename(user_data_file_path)
55
+ say Text::Process::USING_SPEC_OPTION[:user_data_file] % user_data_file_path
56
+ else
57
+ exit_failure Text::Error::NO_USER_DATA_FILE % user_data_file_path
58
+ end
59
+ end
60
+
61
+ if max_parallelism = options[:max_parallelism] then
62
+ test_execution_params[:max_parallelism] = max_parallelism
63
+ say Text::Process::USING_SPEC_OPTION[:max_parallelism] % max_parallelism
64
+ end
65
+
66
+ test_execution_params[:tag] = options[:tag] if options[:tag]
67
+ test_pattern = nil
68
+
69
+ if pattern.is_a?(Array) && pattern.size > 0 then
70
+ test_pattern = pattern.join(",")
71
+ end
72
+
73
+ test_pattern ||= options[:test_pattern]
74
+ if test_pattern then
75
+ say Text::Process::USING_SPEC_OPTION[:test_pattern] % test_pattern
76
+ end
77
+
78
+ test_exclude_pattern ||= options[:test_exclude_pattern]
79
+ if test_exclude_pattern then
80
+ say Text::Process::USING_SPEC_OPTION[:test_exclude_pattern] % test_exclude_pattern
81
+ end
82
+
83
+ tries = 0
84
+ while tries < Default::SCM_READY_TRIES do
85
+ # Call the API to get the suite and its tests
86
+ suite_details = @solano_api.get_suite_by_id(@solano_api.current_suite_id,
87
+ :session_id => options[:session_id])
88
+
89
+ if suite_details["repoman_current"] == true
90
+ break
91
+ else
92
+ @solano_api.demand_repoman_account(suite_details["account_id"])
93
+
94
+ say Text::Process::SCM_REPO_WAIT
95
+ sleep @api_config.scm_ready_sleep
96
+ end
97
+
98
+ tries += 1
99
+ end
100
+ exit_failure Text::Error::SCM_REPO_NOT_READY unless suite_details["repoman_current"]
101
+
102
+ #update_suite_parameters!(suite_details, options[:session_id])
103
+
104
+ start_time = Time.now
105
+
106
+ new_session_params = {
107
+ :commits_encoded => read_and_encode_latest_commits,
108
+ :cache_control_encoded => read_and_encode_cache_control,
109
+ :cache_save_paths_encoded => read_and_encode_cache_save_paths,
110
+ :raw_config_file => read_and_encode_config_file
111
+ }
112
+
113
+ if options[:profile]
114
+ if options[:session_id].nil?
115
+ say Text::Process::USING_PROFILE % options[:profile]
116
+ new_session_params[:profile_name] = options[:profile]
117
+ else
118
+ exit_fail Text::Error::CANNOT_OVERRIDE_PROFILE
119
+ end
120
+ end
121
+
122
+ if options[:queue]
123
+ if options[:session_id].nil?
124
+ say Text::Process::USING_PROFILE % options[:profile]
125
+ new_session_params[:queue] = options[:queue]
126
+ else
127
+ exit_fail Text::Error::CANNOT_OVERRIDE_QUEUE
128
+ end
129
+ end
130
+
131
+ if options[:env]
132
+ say Text::Process::USING_CUSTOM_USER_ENV_VARS % "#{options[:env]}"
133
+ new_session_params[:env] = options[:env]
134
+ end
135
+
136
+ if options[:volume]
137
+ say Text::Process::VOLUME_OVERRIDE % options[:volume]
138
+ new_session_params[:volume] = options[:volume]
139
+ end
140
+
141
+ if options[:session_manager] then
142
+ say Text::Process::USING_SESSION_MANAGER % options[:session_manager]
143
+ new_session_params[:session_manager] = options[:session_manager]
144
+ end
145
+
146
+ new_session_params[:cli_current_commit] = @scm.current_commit
147
+
148
+ # Create a session
149
+ # or use an already-created session
150
+ #
151
+ session_id = options[:session_id]
152
+ session_data = if session_id && session_id > 0
153
+ @solano_api.update_session(session_id, new_session_params)
154
+ else
155
+ sess, manager = @solano_api.create_session(@solano_api.current_suite_id, new_session_params)
156
+ sess
157
+ end
158
+
159
+ session_data ||= {}
160
+ session_id ||= session_data["id"]
161
+
162
+ if manager == 'DestroFreeSessionManager'
163
+ begin
164
+ if !options[:force_snapshot] then
165
+ #check if there is a snapshot
166
+ res = @solano_api.get_snapshot_commit({:session_id => session_id})
167
+ if res['snap_commit'] then
168
+ snapshot_commit = res['snap_commit']
169
+ #No snapshot
170
+ else
171
+ say Text::Process::NO_SNAPSHOT
172
+ res = @scm.create_snapshot(session_id, {:api => @solano_api, :default_branch => options[:default_branch]})
173
+ snapshot_commit = @scm.get_snap_id
174
+ end
175
+ say Text::Process::SNAPSHOT_COMMIT % snapshot_commit
176
+ #if we already had a snapshot or we created a master snapshot
177
+ #create a patch
178
+ @scm.create_patch(session_id, {:api => @solano_api, :commit => snapshot_commit})
179
+ #forced snapshot creation
180
+ else
181
+ say Text::Process::FORCED_SNAPSHOT
182
+ res = @scm.create_snapshot(session_id, {:api => @solano_api, :force => true})
183
+ snapshot_commit = @scm.get_snap_id
184
+ end
185
+ #start tests
186
+ start_test_executions = @solano_api.start_destrofree_session(session_id, {:test_pattern => test_pattern, :test_exclude_pattern=>test_exclude_pattern})
187
+ rescue Exception, RuntimeError => e
188
+ @solano_api.stop_session(session_id)
189
+ say "ERROR: #{e.message}"
190
+ return
191
+ end
192
+ else
193
+
194
+ push_options = {}
195
+ if options[:machine]
196
+ push_options[:use_private_uri] = true
197
+ end
198
+
199
+ if !@scm.push_latest(session_data, suite_details, push_options) then
200
+ exit_failure Text::Error::SCM_PUSH_FAILED
201
+ end
202
+
203
+ machine_data[:session_id] = session_id
204
+
205
+ # Register the tests
206
+ @solano_api.register_session(session_id, @solano_api.current_suite_id, test_pattern, test_exclude_pattern)
207
+
208
+ # Start the tests
209
+ start_test_executions = @solano_api.start_session(session_id, test_execution_params)
210
+
211
+
212
+ num_tests_started = start_test_executions["started"].to_i
213
+
214
+ say Text::Process::STARTING_TEST % num_tests_started.to_s
215
+ end
216
+ tests_finished = false
217
+ finished_tests = {}
218
+ latest_message = -100000
219
+ test_statuses = Hash.new(0)
220
+ session_status = nil
221
+ messages = nil
222
+ last_finish_timestamp = nil
223
+
224
+ report = start_test_executions["report"]
225
+
226
+ # In CI mode, just hang up here. The session will continue running.
227
+ if options[:machine] then
228
+ say Text::Process::BUILD_CONTINUES
229
+ return
230
+ end
231
+
232
+ say ""
233
+ say Text::Process::CHECK_TEST_REPORT % report
234
+ say Text::Process::TERMINATE_INSTRUCTION
235
+ say ""
236
+
237
+ # Catch Ctrl-C to interrupt the test
238
+ Signal.trap(:INT) do
239
+ say Text::Process::INTERRUPT
240
+ say Text::Process::CHECK_TEST_STATUS
241
+ tests_finished = true
242
+ session_status = "interrupted"
243
+ end
244
+
245
+ while !tests_finished do
246
+ current_test_executions = @solano_api.poll_session(session_id)
247
+ session_status = current_test_executions['session_status']
248
+
249
+ messages, latest_message = update_messages(latest_message,
250
+ finished_tests,
251
+ messages,
252
+ current_test_executions["messages"])
253
+
254
+ # Print out the progress of running tests
255
+ current_test_executions["tests"].each do |test_name, result_params|
256
+ if finished_tests.size == 0 && result_params["finished"] then
257
+ say ""
258
+ say Text::Process::CHECK_TEST_REPORT % report
259
+ say Text::Process::TERMINATE_INSTRUCTION
260
+ say ""
261
+ end
262
+ if result_params["finished"] && !finished_tests[test_name]
263
+ test_status = result_params["status"]
264
+ message = case test_status
265
+ when "passed" then [".", :green, false]
266
+ when "failed" then ["F", :red, false]
267
+ when "error" then ["E", nil, false]
268
+ when "pending" then ["*", :yellow, false]
269
+ when "skipped" then [".", :yellow, false]
270
+ else [".", nil, false]
271
+ end
272
+ finished_tests[test_name] = test_status
273
+ last_finish_timestamp = Time.now
274
+ test_statuses[test_status] += 1
275
+ say *message
276
+ end
277
+ end
278
+
279
+ # XXX time out if all tests are done and the session isn't done.
280
+ if current_test_executions['session_done'] || current_test_executions['phase'] == 'done' &&
281
+ ((!num_tests_started.nil? && finished_tests.size >= num_tests_started) && (Time.now - last_finish_timestamp) > Default::TEST_FINISH_TIMEOUT)
282
+ tests_finished = true
283
+ end
284
+
285
+ sleep(Default::SLEEP_TIME_BETWEEN_POLLS) if !tests_finished
286
+ end
287
+
288
+ display_alerts(messages, 'error', Text::Status::SPEC_ERRORS)
289
+
290
+ # Print out the result
291
+ say ""
292
+ say Text::Process::RUN_SOLANO_WEB
293
+ say ""
294
+ say Text::Process::FINISHED_TEST % (Time.now - start_time)
295
+ say "#{finished_tests.size} tests, #{test_statuses["failed"]} failures, #{test_statuses["error"]} errors, #{test_statuses["pending"]} pending, #{test_statuses["skipped"]} skipped"
296
+
297
+ if test_statuses['failed'] > 0
298
+ say ""
299
+ say Text::Process::FAILED_TESTS
300
+ finished_tests.each do |name, status|
301
+ next if status != 'failed'
302
+ say " - #{name}"
303
+ end
304
+ say ""
305
+ end
306
+
307
+ say Text::Process::SUMMARY_STATUS % session_status
308
+ say ""
309
+
310
+ suite = suite_details.merge({"id" => @solano_api.current_suite_id})
311
+ @api_config.set_suite(suite)
312
+ @api_config.write_config
313
+
314
+ exit_failure if session_status != 'passed'
315
+ rescue TddiumClient::Error::API => e
316
+ exit_failure "Failed due to error: #{e.explanation}"
317
+ rescue TddiumClient::Error::Base => e
318
+ exit_failure "Failed due to error: #{e.message}"
319
+ rescue RuntimeError => e
320
+ exit_failure "Failed due to internal error: #{e.inspect} #{e.backtrace}"
321
+ end
322
+
323
+ private
324
+
325
+ def update_messages(latest_message, finished_tests, messages, current, display=true)
326
+ messages = current
327
+ if !options[:machine] && finished_tests.size == 0 && messages
328
+ messages.each do |m|
329
+ seqno = m["seqno"].to_i
330
+ if seqno > latest_message
331
+ if !options[:quiet] || m["level"] == 'error' then
332
+ display_message(m)
333
+ end
334
+ latest_message = seqno
335
+ end
336
+ end
337
+ end
338
+ [messages, latest_message]
339
+ end
340
+
341
+ def read_and_encode_latest_commits
342
+ commits = @scm.commits
343
+ commits_packed = Solano.message_pack(commits)
344
+ commits_encoded = Base64.encode64(commits_packed)
345
+ commits_encoded
346
+ end
347
+
348
+ def docker_enabled
349
+ if @repo_config['system'] then
350
+ if @repo_config['system']['docker'] then
351
+ @repo_config['system']['docker']
352
+ else
353
+ false
354
+ end
355
+ else
356
+ false
357
+ end
358
+ end
359
+
360
+ def cache_control_config
361
+ @repo_config['cache'] || {}
362
+ end
363
+
364
+ def read_and_encode_cache_control
365
+ cache_key_paths = cache_control_config['key_paths'] || cache_control_config[:key_paths]
366
+ cache_key_paths ||= ["Gemfile", "Gemfile.lock", "requirements.txt", "packages.json", "package.json"]
367
+ cache_key_paths.reject!{|x| x =~ /(solano|tddium).yml$/}
368
+
369
+ if docker_enabled then
370
+ cache_key_paths << "Dockerfile"
371
+ end
372
+
373
+ cache_control_data = {}
374
+ cache_key_paths.each do |p|
375
+ if File.exists?(p) then
376
+ cache_control_data[p] = Digest::SHA1.file(p).to_s
377
+ end
378
+ end
379
+
380
+ msgpack = Solano.message_pack(cache_control_data)
381
+ cache_control_encoded = Base64.encode64(msgpack)
382
+ end
383
+
384
+ def read_and_encode_cache_save_paths
385
+ cache_save_paths = cache_control_config['save_paths'] || cache_control_config[:save_paths]
386
+
387
+ if docker_enabled then
388
+ (cache_save_paths ||= []) << "HOME/docker-graph"
389
+ end
390
+
391
+ msgpack = Solano.message_pack(cache_save_paths)
392
+ cache_save_paths_encoded = Base64.encode64(msgpack)
393
+ end
394
+
395
+ def read_and_encode_config_file
396
+ config, raw_config = @repo_config.read_config(false)
397
+ encoded = Base64.encode64(raw_config)
398
+ return encoded
399
+ end
400
+ end
401
+ end