Class: Solano::SolanoCli

Inherits:
Thor
  • Object
show all
Extended by:
ParamsHelper
Includes:
SolanoConstant
Defined in:
lib/solano/cli/show.rb,
lib/solano/cli/util.rb,
lib/solano/cli/suite.rb,
lib/solano/cli/prompt.rb,
lib/solano/cli/solano.rb,
lib/solano/cli/commands/api.rb,
lib/solano/cli/commands/web.rb,
lib/solano/cli/commands/keys.rb,
lib/solano/cli/commands/spec.rb,
lib/solano/cli/commands/stop.rb,
lib/solano/cli/commands/login.rb,
lib/solano/cli/commands/rerun.rb,
lib/solano/cli/commands/suite.rb,
lib/solano/cli/commands/config.rb,
lib/solano/cli/commands/heroku.rb,
lib/solano/cli/commands/logout.rb,
lib/solano/cli/commands/server.rb,
lib/solano/cli/commands/status.rb,
lib/solano/cli/commands/account.rb,
lib/solano/cli/commands/console.rb,
lib/solano/cli/commands/support.rb,
lib/solano/cli/commands/activate.rb,
lib/solano/cli/commands/describe.rb,
lib/solano/cli/commands/password.rb,
lib/solano/cli/commands/find_failing.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ParamsHelper

default_host, default_insecure, default_port, default_proto, display, load_params, write_params

Constructor Details

#initialize(*args) ⇒ SolanoCli

Returns a new instance of SolanoCli.



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/solano/cli/solano.rb', line 29

def initialize(*args)
  super(*args)

  # TODO: read host from .solano file
  # TODO: allow selecting which .solano "profile" to use
  cli_opts = options[:insecure] ? { :insecure => true } : {}
  cli_opts[:debug] = true
  @tddium_client = TddiumClient::InternalClient.new(options[:host],
                                                    options[:port],
                                                    options[:proto],
                                                    1,
                                                    caller_version,
                                                    cli_opts)
  @tddium_clientv3 = TddiumClient::InternalClient.new(options[:host],
                                                    options[:port],
                                                    options[:proto],
                                                    "api/v3",
                                                    caller_version,
                                                    cli_opts)
  @cli_options = options
end

Instance Attribute Details

#scmObject (readonly)

Returns the value of attribute scm.



8
9
10
# File 'lib/solano/cli/solano.rb', line 8

def scm
  @scm
end

#user_detailsObject (readonly)

Returns the value of attribute user_details.



9
10
11
# File 'lib/solano/cli/solano.rb', line 9

def user_details
  @user_details
end

Class Method Details

.exit_on_failure?Boolean

Thor has the wrong default behavior

Returns:

  • (Boolean)


80
81
82
# File 'lib/solano/cli/solano.rb', line 80

def self.exit_on_failure?
  return true
end

Instance Method Details

#accountObject



7
8
9
10
11
12
13
14
15
16
# File 'lib/solano/cli/commands/account.rb', line 7

def 
  user_details = solano_setup({:scm => false})

  if user_details then
    # User is already logged in, so just display the info
    show_user_details(user_details)
  else
    exit_failure Text::Error::USE_ACTIVATE
  end
end

#activateObject



9
10
11
12
13
14
# File 'lib/solano/cli/commands/activate.rb', line 9

def activate
  say "To activate your account, please visit"
  say "https://ci.predix.io/"

  solano_setup({:scm => false})
end

#config(scope = "suite") ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/solano/cli/commands/config.rb', line 9

def config(scope="suite")
  params = {:repo => true}
  if scope == 'suite' then
    params[:suite] = true
  end
  if options[:account] then
    params[:account] = options[:account]
  end
  solano_setup(params)

  begin
    config_details = @solano_api.get_config_key(scope)
    show_config_details(scope, config_details['env'])
  rescue TddiumClient::Error::API => e
    exit_failure Text::Error::LIST_CONFIG_ERROR
  rescue Exception => e
    exit_failure e.message
  end
end

#console(*cmd) ⇒ Object



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/solano/cli/commands/console.rb', line 12

def console(*cmd)
  solano_setup({:repo => true})
  origin_url = @scm.origin_url
  current_suite_id = @solano_api.current_suite_id
  if current_suite_id then
    say "Checking for sessions on this branch"
    session_result = @solano_api.call_api(:get, "/sessions", {:suite_id => current_suite_id})["sessions"]
  elsif suite_for_default_branch? then
    say "Checking for sessions on default branch"
    session_result = @solano_api.call_api(:get, "/sessions", {:repo_url => origin_url})["sessions"]
  else
    say "Checking for sessions on this repository"
    session_result = @solano_api.call_api(:get, "/sessions", {:repo_url => origin_url})["sessions"]
  end
  if defined?(session_result) && session_result.any? then
    session = session_result[0]
    session_id = session["id"]
    q_result = @solano_api.query_session(session_id).tddium_response["session"]
    suite_id = q_result["suite_id"]
    start_result = @solano_api.start_console(session_id, suite_id).tddium_response
    session_id = start_result["interactive_session_id"] # the new interactive session's id
    if start_result["message"] == "interactive started" then
      say "Starting console session #{session_id}"
      ssh_command = nil
      failures = 0
      while !ssh_command do
        sleep(Default::SLEEP_TIME_BETWEEN_POLLS)
        begin
          session = @solano_api.query_session(session_id).tddium_response["session"]
          failures = 0 # clear any previous transient failures
        rescue Exception => e
          failures += 1
          say e.to_s
          session = {}
        end
        if failures > 2 then
          say "Errors connecting to server"
          return # give up.
        end
        if session["stage2_ready"] && session["ssh_command"] then
          if cmd then
            ssh_command = "#{session['ssh_command']} -o StrictHostKeyChecking=no \"#{cmd.join(' ')}\""
          else
            ssh_command = "#{session['ssh_command']} -o StrictHostKeyChecking=no"
          end
        end
      end
      say "SSH Command is #{ssh_command}"
      # exec terminates this ruby process and lets user control the ssh i/o
      exec ssh_command
    elsif start_result["message"] == "interactive already running"
      dur = duration(Time.parse(start_result['session']['expires_at']) - Time.now)
      say "Interactive session already running (expires in #{dur})"
      session_id = start_result["session"]["id"]
      session = @solano_api.query_session(session_id).tddium_response["session"]
      if cmd then
        exec "#{session['ssh_command']} -o StrictHostKeyChecking=no \"#{cmd.join(' ')}\""
      else
        exec "#{session['ssh_command']} -o StrictHostKeyChecking=no"
      end
    else
      say start_result["message"]
    end
  else
    say "Unable to find any previous sessions. Execute solano run first or switch to a branch with a recent session."
  end
end

#describe(session_id = nil) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/solano/cli/commands/describe.rb', line 15

def describe(session_id=nil)
  solano_setup({:repo => false})

  status_message = ''
  if !session_id then
    # params to get the most recent session id on current branch
    suite_params = {
      :suite_id => @solano_api.current_suite_id,
      :active => false,
      :limit => 1
    } if suite_for_current_branch?

    sessions = suite_params ? @solano_api.get_sessions(suite_params) : []
    if sessions.empty? then
      exit_failure Text::Status::NO_INACTIVE_SESSION
    end

    session_id = sessions[0]['id']

    session_status = sessions[0]['status'].upcase
    session_commit = sessions[0]['commit']
    current_commit = @scm.current_commit
    if session_commit == current_commit
      commit_message = "equal to your current commit"
    else
      cnt_ahead = @scm.number_of_commits(session_commit, current_commit)
      if cnt_ahead == 0
        cnt_behind = @scm.number_of_commits(current_commit, session_commit)
        commit_message = "your workspace is behind by #{cnt_behind} commits"
      else
        commit_message = "your workspace is ahead by #{cnt_ahead} commits"
      end
    end

    duration = sessions[0]['duration']
    start_timeago = "%s ago" % Solano::TimeFormat.seconds_to_human_time(Time.now - Time.parse(sessions[0]["start_time"]))
    if duration.nil?
      finish_timeago = "no info about duration found, started #{start_timeago}"
    elsif session_status == 'RUNNING'
      finish_timeago = "in process, started #{start_timeago}"
    else
      finish_time = Time.parse(sessions[0]["start_time"]) + duration
      finish_timeago = "%s ago" % Solano::TimeFormat.seconds_to_human_time(Time.now - finish_time)
    end

    status_message = Text::Status::SESSION_STATUS % [session_commit, commit_message, session_status, finish_timeago]
  end

  result = @solano_api.query_session_tests(session_id)

  session_result = Hash.new
  if options[:verbose] then
    session_result = @solano_api.query_session(session_id)
  end

  filtered = result['session']['tests']
  if !options[:all]
    filtered = filtered.select{|x| x['status'] == 'failed'}
  end

  if options[:type]
    filtered = filtered.select{|x| x['test_type'].downcase == options[:type].downcase}
  end

  if options[:json]
    json = result['session']
    json['session'] = session_result['session']
    puts JSON.pretty_generate(json)
  elsif options[:names]
    say filtered.map{|x| x['test_name']}.join(" ")
  else
    filtered.sort!{|a,b| [a['test_type'], a['test_name']] <=> [b['test_type'], b['test_name']]}

    say Text::Process::DESCRIBE_SESSION % [session_id, status_message, options[:all] ? 'all' : 'failed']

    table = 
      [["Test", "Status", "Duration"],
       ["----", "------", "--------"]] +
      filtered.map do |x|
      [
        x['test_name'],
        x['status'],
        x['elapsed_time'] ? "#{x['elapsed_time']}s" : "-"
      ]
    end
    print_table table
  end
end

#find_failing(*files) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# File 'lib/solano/cli/commands/find_failing.rb', line 7

def find_failing(*files)
  solano_setup({:repo => true})

  failing = files.pop
  if !files.include?(failing)
    exit_failure "Files have to include the failing file, use the copy helper"
  elsif files.size < 2
    exit_failure "Files have to be more than 2, use the copy helper"
  elsif !success?([failing])
    exit_failure "#{failing} fails when run on it's own"
  elsif success?(files)
    exit_failure "tests pass locally"
  else
    loop do
      a = remove_from(files, files.size / 2, :not => failing)
      b = files - (a - [failing])
      status, files = find_failing_set([a, b], failing)
      if status == :finished
        say "Fails when #{files.join(", ")} are run together"
        break
      elsif status == :continue
        next
      else
        exit_failure "unable to isolate failure to 2 files"
      end
    end
  end
end

#herokuObject



10
11
12
13
14
15
# File 'lib/solano/cli/commands/heroku.rb', line 10

def heroku
  say "To activate your heroku account, please visit"
  say "https://ci.predix.io/"

  solano_setup({:scm => false})
end

#keysObject



6
7
8
9
10
11
12
13
14
15
16
17
18
19
# File 'lib/solano/cli/commands/keys.rb', line 6

def keys
  user_details = solano_setup({:scm => false})

  begin
    if user_details then
      show_third_party_keys_details(user_details)
    end

    keys_details = @solano_api.get_keys
    show_keys_details(keys_details)
  rescue TddiumClient::Error::API => e
    exit_failure Text::Error::LIST_KEYS_ERROR
  end
end

#login(*args) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# File 'lib/solano/cli/commands/login.rb', line 9

def (*args)
  user_details = solano_setup({:login => false, :scm => false})

   = options.dup

  if args.first && args.first =~ /@/
    [:email] ||= args.first 
  elsif args.first
    # assume cli token
    [:cli_token] = args.first
  end

  if user_details then
    say Text::Process::ALREADY_LOGGED_IN
  elsif user = @solano_api.(:params => @solano_api.get_user_credentials(), :show_error => true)
    say Text::Process::LOGGED_IN_SUCCESSFULLY 
    if @scm.repo? then
      @api_config.populate_branches(@solano_api.current_branch)
    end
    @api_config.write_config
  else
    exit_failure
  end
  if prompt_missing_ssh_key then
    say Text::Process::NEXT_STEPS
  end
end

#logoutObject



6
7
8
9
10
11
12
# File 'lib/solano/cli/commands/logout.rb', line 6

def logout
  solano_setup({:login => false, :scm => false})

  @api_config.logout

  say Text::Process::LOGGED_OUT_SUCCESSFULLY
end

#passwordObject



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/solano/cli/commands/password.rb', line 7

def password
  user_details = solano_setup({:scm => false})

  params = {}
  params[:current_password] = HighLine.ask(Text::Prompt::CURRENT_PASSWORD) { |q| q.echo = "*" }
  params[:password] = HighLine.ask(Text::Prompt::NEW_PASSWORD) { |q| q.echo = "*" }
  params[:password_confirmation] = HighLine.ask(Text::Prompt::PASSWORD_CONFIRMATION) { |q| q.echo = "*" }

  begin
    user_id = user_details["id"]
    @solano_api.update_user(user_id, {:user => params})
    say Text::Process::PASSWORD_CHANGED
  rescue TddiumClient::Error::API => e
    exit_failure Text::Error::PASSWORD_ERROR % e.explanation
  rescue TddiumClient::Error::Base => e
    exit_failure e.message
  end
end

#rerun(session_id = nil) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'lib/solano/cli/commands/rerun.rb', line 13

def rerun(session_id=nil)
  params = {:scm => true, :repo => false}
  if session_id.nil? then
    params = {:repo => true, :suite => true}
  end
  solano_setup(params)

  session_id ||= session_id_for_current_suite

  begin
    result = @solano_api.query_session_tests(session_id)
  rescue TddiumClient::Error::API => e
    exit_failure Text::Error::NO_SESSION_EXISTS
  end

  tests = result['session']['tests']
  tests = tests.select{ |t| [
    'failed', 'error', 'notstarted', 'started'].include?(t['status']) }
  tests = tests.map{ |t| t['test_name'] }

  profile = options[:profile] || result['non_passed_profile_name']

  cmd = "solano run"
  cmd += " --max-parallelism=#{options[:max_parallelism]}" if options[:max_parallelism]
  cmd += " --org=#{options[:account]}" if options[:account]
  cmd += " --force" if options[:force]
  cmd += " --profile=#{profile}" if profile
  cmd += " --queue=#{options[:queue]}" if options[:queue]
  cmd += " #{tests.join(" ")}"

  say cmd
  Kernel.exec(cmd) if !options[:no_op]
end

#serverObject



6
7
8
9
# File 'lib/solano/cli/commands/server.rb', line 6

def server
  solano_setup({:scm => false, :login => false})
  self.class.display
end

#spec(*pattern) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
# File 'lib/solano/cli/commands/spec.rb', line 30

def spec(*pattern)
  machine_data = {}

  solano_setup({:repo => true})

  suite_auto_configure unless options[:machine]

  exit_failure unless suite_for_current_branch?

  if !options[:machine] && @solano_api.get_keys.empty? then
    warn(Text::Warning::NO_SSH_KEY)
  end

  if @scm.changes?(options) then
    exit_failure(Text::Error::SCM_CHANGES_NOT_COMMITTED) if !options[:force]
    warn(Text::Warning::SCM_CHANGES_NOT_COMMITTED)
  end

  test_execution_params = {}

  if user_data_file_path = options[:user_data_file] then
    if File.exists?(user_data_file_path) then
      user_data = File.open(user_data_file_path) { |file| file.read }
      test_execution_params[:user_data_text] = Base64.encode64(user_data)
      test_execution_params[:user_data_filename] = File.basename(user_data_file_path)
      say Text::Process::USING_SPEC_OPTION[:user_data_file] % user_data_file_path
    else
      exit_failure Text::Error::NO_USER_DATA_FILE % user_data_file_path
    end
  end

  if max_parallelism = options[:max_parallelism] then
    test_execution_params[:max_parallelism] = max_parallelism
    say Text::Process::USING_SPEC_OPTION[:max_parallelism] % max_parallelism
  end

  test_execution_params[:tag] = options[:tag] if options[:tag]
  test_pattern = nil

  if pattern.is_a?(Array) && pattern.size > 0 then
    test_pattern = pattern.join(",")
  end

  test_pattern ||= options[:test_pattern]
  if test_pattern then
    say Text::Process::USING_SPEC_OPTION[:test_pattern] % test_pattern
  end

  test_exclude_pattern ||= options[:test_exclude_pattern]
  if test_exclude_pattern then
    say Text::Process::USING_SPEC_OPTION[:test_exclude_pattern] % test_exclude_pattern
  end

   # Call the API to get the suite and its tests
  suite_details = @solano_api.get_suite_by_id(@solano_api.current_suite_id,
                                                :session_id => options[:session_id])

  start_time = Time.now

  new_session_params = {
    :commits_encoded => read_and_encode_latest_commits,
    :cache_control_encoded => read_and_encode_cache_control,
    :cache_save_paths_encoded => read_and_encode_cache_save_paths,
    :raw_config_file => read_and_encode_config_file
  }

  if options[:profile]
    if  options[:session_id].nil?
      say Text::Process::USING_PROFILE % options[:profile]
      new_session_params[:profile_name] = options[:profile]
    else
      exit_fail Text::Error::CANNOT_OVERRIDE_PROFILE
    end
  end

 if options[:queue]
    if  options[:session_id].nil?
      say Text::Process::USING_PROFILE % options[:profile]
      new_session_params[:queue] = options[:queue]
    else
      exit_fail Text::Error::CANNOT_OVERRIDE_QUEUE
    end
  end

  if options[:env]
    say Text::Process::USING_CUSTOM_USER_ENV_VARS % "#{options[:env]}"
    new_session_params[:env] = options[:env]
  end

  if options[:volume]
    say Text::Process::VOLUME_OVERRIDE % options[:volume]
    new_session_params[:volume] = options[:volume]
  end

  if options[:session_manager] then
      say Text::Process::USING_SESSION_MANAGER % options[:session_manager]
      new_session_params[:session_manager] = options[:session_manager]
  end

  new_session_params[:cli_current_commit] = @scm.current_commit

  # Create a session
  # or use an already-created session
  #
  session_id = options[:session_id]
  session_data = if session_id && session_id > 0
    @solano_api.update_session(session_id, new_session_params)
  else
    sess, manager = @solano_api.create_session(@solano_api.current_suite_id, new_session_params)
    sess
  end

  session_data ||= {}
  session_id ||= session_data["id"]

  if manager == 'DestroFreeSessionManager'
    begin
      if !options[:force_snapshot] then
        #check if there is a snapshot
        res = @solano_api.get_snapshot_commit({:session_id => session_id})
        if res['snap_commit'] then
          snapshot_commit = res['snap_commit']
        #No snapshot
        else
          say Text::Process::NO_SNAPSHOT
          res = @scm.create_snapshot(session_id, {:api => @solano_api, :default_branch => options[:default_branch]})
          snapshot_commit = @scm.get_snap_id
        end
        say Text::Process::SNAPSHOT_COMMIT % snapshot_commit
        #if we already had a snapshot or we created a master snapshot
        #create a patch
        @scm.create_patch(session_id, {:api => @solano_api, :commit => snapshot_commit})
      #forced snapshot creation
      else
        say Text::Process::FORCED_SNAPSHOT
        res = @scm.create_snapshot(session_id, {:api => @solano_api, :force => true})
        snapshot_commit = @scm.get_snap_id
      end
      #start tests
      start_test_executions = @solano_api.start_destrofree_session(session_id, {:test_pattern => test_pattern, :test_exclude_pattern=>test_exclude_pattern})
    rescue Exception, RuntimeError => e
       @solano_api.stop_session(session_id)
       say "ERROR: #{e.message}"
       return
    end
  else

    push_options = {}
    if options[:machine]
      push_options[:use_private_uri] = true
    end

    if !@scm.push_latest(session_data, suite_details, push_options) then
      exit_failure Text::Error::SCM_PUSH_FAILED
    end

    machine_data[:session_id] = session_id

    # Register the tests
    @solano_api.register_session(session_id, @solano_api.current_suite_id, test_pattern, test_exclude_pattern)

    # Start the tests
    start_test_executions = @solano_api.start_session(session_id, test_execution_params)


    num_tests_started = start_test_executions["started"].to_i

    say Text::Process::STARTING_TEST % num_tests_started.to_s
  end
  tests_finished = false
  finished_tests = {}
  latest_message = -100000
  test_statuses = Hash.new(0)
  session_status = nil
  messages = nil
  last_finish_timestamp = nil

  report = start_test_executions["report"]

  # In CI mode, just hang up here.  The session will continue running.
  if options[:machine] then
    say Text::Process::BUILD_CONTINUES
    return
  end

  say ""
  say Text::Process::CHECK_TEST_REPORT % report
  say Text::Process::TERMINATE_INSTRUCTION
  say ""

  # Catch Ctrl-C to interrupt the test
  Signal.trap(:INT) do
    say Text::Process::INTERRUPT
    say Text::Process::CHECK_TEST_STATUS
    tests_finished = true
    session_status = "interrupted"
  end

  while !tests_finished do
    current_test_executions = @solano_api.poll_session(session_id)
    session_status = current_test_executions['session_status']

    messages, latest_message = update_messages(latest_message,
                                               finished_tests,
                                               messages,
                                               current_test_executions["messages"])

    # Print out the progress of running tests
    current_test_executions["tests"].each do |test_name, result_params|
      if finished_tests.size == 0 && result_params["finished"] then
        say ""
        say Text::Process::CHECK_TEST_REPORT % report
        say Text::Process::TERMINATE_INSTRUCTION
        say ""
      end
      if result_params["finished"] && !finished_tests[test_name]
        test_status = result_params["status"]
        message = case test_status
                  when "passed" then [".", :green, false]
                  when "failed" then ["F", :red, false]
                  when "error" then ["E", nil, false]
                  when "pending" then ["*", :yellow, false]
                  when "skipped" then [".", :yellow, false]
                  else [".", nil, false]
                  end
        finished_tests[test_name] = test_status
        last_finish_timestamp = Time.now
        test_statuses[test_status] += 1
        say *message
      end
    end

    # XXX time out if all tests are done and the session isn't done.
    if current_test_executions['session_done'] || current_test_executions['phase'] == 'done' &&
      ((!num_tests_started.nil? && finished_tests.size >= num_tests_started) && (Time.now - last_finish_timestamp) > Default::TEST_FINISH_TIMEOUT)
      tests_finished = true
    end

    sleep(Default::SLEEP_TIME_BETWEEN_POLLS) if !tests_finished
  end

  display_alerts(messages, 'error', Text::Status::SPEC_ERRORS)

  # Print out the result
  say ""
  say Text::Process::RUN_SOLANO_WEB
  say ""
  say Text::Process::FINISHED_TEST % (Time.now - start_time)
  say "#{finished_tests.size} tests, #{test_statuses["failed"]} failures, #{test_statuses["error"]} errors, #{test_statuses["pending"]} pending, #{test_statuses["skipped"]} skipped"

  if test_statuses['failed'] > 0
    say ""
    say Text::Process::FAILED_TESTS
    finished_tests.each do |name, status|
      next if status != 'failed'
      say " - #{name}"
    end
    say ""
  end

  say Text::Process::SUMMARY_STATUS % session_status
  say ""

  suite = suite_details.merge({"id" => @solano_api.current_suite_id})
  @api_config.set_suite(suite)
  @api_config.write_config

  exit_failure if session_status != 'passed'
rescue TddiumClient::Error::API => e
  exit_failure "Failed due to error: #{e.explanation}"
rescue TddiumClient::Error::Base => e
  exit_failure "Failed due to error: #{e.message}"
rescue RuntimeError => e
  exit_failure "Failed due to internal error: #{e.inspect} #{e.backtrace}"
end

#statusObject



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/solano/cli/commands/status.rb', line 12

def status
  solano_setup({:repo => true})

  begin
    # solano_setup asserts that we're in a supported SCM repo
    origin_url = @scm.origin_url
    repo_params = {
      :active => true,
      :repo_url => origin_url
    }

    if suite_for_current_branch? then
      status_branch = @solano_api.current_branch
      suite_params = {
        :active => false,
        :limit => 10
      }
    elsif suite_for_default_branch? then
      status_branch = @solano_api.default_branch
      say Text::Error::TRY_DEFAULT_BRANCH % status_branch
      suite_params = {
        :active => false,
        :limit => 10
      }
    end

    if options[:commit] then
      repo_params[:last_commit_id] = options[:commit]
      suite_params[:last_commit_id] = options[:commit]
    end

    suites = @solano_api.get_suites(:repo_url => origin_url, :branch => status_branch)
    if suites.count == 0
      exit_failure Text::Error::CANT_FIND_SUITE % [origin_url, status_branch]
    elsif suites.count > 1
      if options[:account] then
        suites = suites.select { |s| s['account'] == options[:account] }
      else
        say Text::Status::SUITE_IN_MULTIPLE_ACCOUNTS % [origin_url, status_branch]
        suites.each { |s| say '  ' + s['account'] }
         = ask Text::Status::SUITE_IN_MULTIPLE_ACCOUNTS_PROMPT
        suites = suites.select { |s| s['account'] ==  }
      end
    end

    if suites.count == 0
      exit_failure Text::Error::INVALID_ACCOUNT_NAME
    end

    suite_params[:suite_id] = suites.first['id']

    if options[:json] 
      res = {}
      res[:running] = { origin_url => @solano_api.get_sessions(repo_params) }          
      res[:history] = { 
        status_branch => @solano_api.get_sessions(suite_params)
      } if suite_params
      puts JSON.pretty_generate(res)
    else
      show_session_details(
        status_branch,
        repo_params, 
        Text::Status::NO_ACTIVE_SESSION, 
        Text::Status::ACTIVE_SESSIONS,
        true
      )
      show_session_details(
        status_branch,
        suite_params, 
        Text::Status::NO_INACTIVE_SESSION, 
        Text::Status::INACTIVE_SESSIONS,
        false
      ) if suite_params
      say Text::Process::RERUN_SESSION
    end

  rescue TddiumClient::Error::Base => e
    exit_failure e.message
  end
end

#stop(ls_id = nil) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
# File 'lib/solano/cli/commands/stop.rb', line 6

def stop(ls_id=nil)
  solano_setup({:scm => false})
  if ls_id then
    begin
      say "Stopping session #{ls_id} ..."
      say @solano_api.stop_session(ls_id)['notice']
    rescue 
    end
  else
    exit_failure 'Stop requires a session id -- e.g. `solano stop 7869764`'
  end
end

#suite(*argv) ⇒ Object



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/solano/cli/commands/suite.rb', line 19

def suite(*argv)
  solano_setup({:repo => true})

  params = {}

  # Load tool options into params
  tool_cli_populate(options, params)

  begin

    if options[:delete]
      # Deleting works differently (for now) because api_config can't handle
      # multiple suites with the same branch name in two different accounts.

      repo_url = @scm.origin_url

      if argv.is_a?(Array) && argv.size > 0
        branch = argv[0]
      else
        branch = @solano_api.current_branch
      end

      suites = @solano_api.get_suites(:repo_url => repo_url, :branch => branch)
      if suites.count == 0
        exit_failure Text::Error::CANT_FIND_SUITE % [repo_url, branch]
      elsif suites.count > 1
        say Text::Process::SUITE_IN_MULTIPLE_ACCOUNTS % [repo_url, branch]
        suites.each { |s| say '  ' + s['account'] }
         = ask Text::Process::SUITE_IN_MULTIPLE_ACCOUNTS_PROMPT
        suites = suites.select { |s| s['account'] ==  }
        if suites.count == 0
          exit_failure Text::Error::INVALID_ACCOUNT_NAME
        end
      end

      suite = suites.first

      unless options[:non_interactive]
        yn = ask Text::Process::CONFIRM_DELETE_SUITE % [suite['repo_url'], suite['branch'], suite['account']]
        unless yn.downcase == Text::Prompt::Response::YES
          exit_failure Text::Process::ABORTING
        end
      end

      @solano_api.permanent_destroy_suite(suite['id'])
      @api_config.delete_suite(suite['branch'])
      @api_config.write_config

    elsif @solano_api.current_suite_id then
      # Suite ID set in solano config file
      current_suite = @solano_api.get_suite_by_id(@solano_api.current_suite_id)
      if options[:edit] then
        update_suite(current_suite, options)
      else
        say Text::Process::EXISTING_SUITE, :bold
        say format_suite_details(current_suite)
      end

      @api_config.set_suite(current_suite)
      @api_config.write_config
    else
      # Need to find or construct the suite (and repo)
      params[:branch] = @scm.current_branch
      params[:repo_url] = @scm.origin_url
      params[:repo_name] = @scm.repo_name
      params[:scm] = @scm.scm_name

      say Text::Process::NO_CONFIGURED_SUITE % [params[:repo_name], params[:branch]]

      prompt_suite_params(options, params)

      params.each do |k,v|
        params.delete(k) if v == 'disable'
      end

      # Create new suite if it does not exist yet
      say Text::Process::CREATING_SUITE % [params[:repo_name], params[:branch]]
      new_suite = @solano_api.create_suite(params)

      # Save the created suite
      @api_config.set_suite(new_suite)
      @api_config.write_config

      say Text::Process::CREATED_SUITE, :bold
      say format_suite_details(new_suite)
    end
  rescue TddiumClient::Error::Base => e
    exit_failure(e.explanation)
  end
end

#versionObject



75
76
77
# File 'lib/solano/cli/solano.rb', line 75

def version
  say VERSION
end

#web(*args) ⇒ Object



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'lib/solano/cli/commands/web.rb', line 6

def web(*args)
  session_id = args.first

  params = {:login => false}
  if session_id.nil? then
    params[:scm] = true
    params[:repo] = true
  end

  solano_setup(params)

  if session_id
    fragment = "1/reports/#{session_id}" if session_id =~ /^(\d+)(\.\w+)*$/
  end
  fragment ||= 'latest'

  begin
    Launchy.open("#{options[:proto]}://#{options[:host]}/#{fragment}")
  rescue Launchy::Error => e
    exit_failure e.message
  end
end