solano 1.31.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.
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