class Solano::SolanoCli

Attributes

scm[R]
user_details[R]

Public Class Methods

exit_on_failure?() click to toggle source

Thor has the wrong default behavior

# File lib/solano/cli/solano.rb, line 80
def self.exit_on_failure?
  return true
end
new(*args) click to toggle source
Calls superclass method
# 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

Public Instance Methods

account() click to toggle source
# File lib/solano/cli/commands/account.rb, line 7
def account
  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
activate() click to toggle source
# 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") click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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) click to toggle source
# 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
heroku() click to toggle source
# 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
invoke_command(command, *args) click to toggle source
Calls superclass method
# File lib/solano/cli/solano.rb, line 90
def invoke_command(command, *args)
  begin
    super
  rescue InvocationError
    if command.name == "help"
      exit_failure Text::Error::CANT_INVOKE_COMMAND
    else
      raise
    end
  end
end
keys() click to toggle source
# 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) click to toggle source
# File lib/solano/cli/commands/login.rb, line 9
def login(*args)
  user_details = solano_setup({:login => false, :scm => false})

  login_options = options.dup

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

  if user_details then
    say Text::Process::ALREADY_LOGGED_IN
  elsif user = @solano_api.login_user(:params => @solano_api.get_user_credentials(login_options), :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
logout() click to toggle source
# 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
password() click to toggle source
# 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) click to toggle source
# File lib/solano/cli/commands/rerun.rb, line 14
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 += " --pipeline=#{options[:pipeline]}" if options[:pipeline]
  cmd += " #{tests.join(" ")}"

  say cmd
  Kernel.exec(cmd) if !options[:no_op]
end
server() click to toggle source
# File lib/solano/cli/commands/server.rb, line 6
def server
  solano_setup({:scm => false, :login => false})
  self.class.display
end
spec(*pattern) click to toggle source
# File lib/solano/cli/commands/spec.rb, line 31
def spec(*pattern)
  machine_data = {}

  solano_setup({:repo => true})

  unless options[:pipeline]
    suites = @solano_api.get_suites.select { |suite| suite['repo_url'] == @scm.origin_url }
    repo_names = suites.map { |suite| suite['repo_name'] }.uniq
    if repo_names.count > 1
      say "You already have pipelines with url: #{@scm.origin_url}"
      repo_names.each { |repo_name| say " " + repo_name }
      exit_failure "You must specify an pipeline by passing the --pipeline option."
    end
  end

  suite_auto_configure unless options[:machine]
  exit_failure unless suite_for_current_branch?

  suite_id = calc_current_suite_id
  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(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(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, 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" => 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
status() click to toggle source
# File lib/solano/cli/commands/status.rb, line 13
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
   }

    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

    if options[:pipeline]
      suites = @solano_api.get_suites(:branch => status_branch).select { |s| s['repo_name'] == options[:pipeline] }
    else
      suites = @solano_api.get_suites(:repo_url => origin_url, :branch => status_branch)
    end

    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'] }
        account = ask Text::Status::SUITE_IN_MULTIPLE_ACCOUNTS_PROMPT
        suites = suites.select { |s| s['account'] == account }
      end
    end

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

    suite_params[:suite_id] = suites.first['id']
    repo_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) click to toggle source
# 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) click to toggle source
# 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'] }
        account = ask Text::Process::SUITE_IN_MULTIPLE_ACCOUNTS_PROMPT
        suites = suites.select { |s| s['account'] == 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
version() click to toggle source
# File lib/solano/cli/solano.rb, line 75
def version
  say VERSION
end
web(*args) click to toggle source
# 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

Protected Instance Methods

calc_current_suite_id() click to toggle source
# File lib/solano/cli/suite.rb, line 65
def calc_current_suite_id
  if options[:pipeline] && options[:account]
    suites = @solano_api.get_suites.select { |s| s['repo_name'] == options[:pipeline] && s['account'] == options[:account] }
    suite = suites.select { |s| s['branch'] == @scm.current_branch }.first
    suite ? suite['id'] : nil
  elsif options[:pipeline]
    suites = @solano_api.get_suites.select { |s| s['repo_name'] == options[:pipeline] }
    suite = suites.select { |s| s['branch'] == @scm.current_branch }.first
    suite ? suite['id'] : nil
  else
    @solano_api.current_suite_id
  end
end
caller_version() click to toggle source
# File lib/solano/cli/solano.rb, line 106
def caller_version
  "solano-#{VERSION}"
end
configured_test_exclude_pattern() click to toggle source
# File lib/solano/cli/solano.rb, line 117
def configured_test_exclude_pattern
  pattern = @repo_config["test_exclude_pattern"]

  return nil if pattern.nil? || pattern.empty?
  return pattern
end
configured_test_pattern() click to toggle source
# File lib/solano/cli/solano.rb, line 110
def configured_test_pattern
  pattern = @repo_config["test_pattern"]

  return nil if pattern.nil? || pattern.empty?
  return pattern
end
display_alerts(messages, level, heading) click to toggle source
# File lib/solano/cli/util.rb, line 123
def display_alerts(messages, level, heading)
  return unless messages
  interest = messages.select{|m| [level].include?(m['level'])}
  if interest.size > 0
    say heading
    interest.each do |m|
      display_message(m, '')
    end
  end
end
display_message(message, prefix=' ---> ') click to toggle source
# File lib/solano/cli/util.rb, line 113
def display_message(message, prefix=' ---> ')
  color = case message["level"]
            when "error" then :red
            when "warn" then :yellow
            else nil
          end
  print prefix
  say message["text"].rstrip, color
end
exit_failure(msg='') click to toggle source
# File lib/solano/cli/util.rb, line 109
def exit_failure(msg='')
  abort msg
end
format_suite_details(suite) click to toggle source
# File lib/solano/cli/suite.rb, line 57
def format_suite_details(suite)
  # Given an API response containing a "suite" key, compose a string with
  # important information about the suite
  solano_deploy_key_file_name = @api_config.solano_deploy_key_file_name
  details = ERB.new(Text::Status::SUITE_DETAILS).result(binding)
  details
end
format_usage(usage) click to toggle source
# File lib/solano/cli/show.rb, line 63
def format_usage(usage)
  "All tests: %.2f worker-hours  ($%.2f)" % [
    usage["hours"] || 0, usage["charge"] || 0]
end
normalize_bundler_version(bundler_version) click to toggle source
# File lib/solano/cli/util.rb, line 78
def normalize_bundler_version(bundler_version)
  if !bundler_version.nil? then
    bundler_version.chomp!
    bundler_version =~ /Bundler version (.*)\z/
    bundler_version = $1
  end
  return bundler_version
end
prompt(text, current_value, default_value, dont_prompt=false) click to toggle source
# File lib/solano/cli/prompt.rb, line 7
def prompt(text, current_value, default_value, dont_prompt=false)
  value = current_value || (dont_prompt ? nil : ask(text % default_value, :bold))
  (value.nil? || value.empty?) ? default_value : value
end
prompt_missing_ssh_key() click to toggle source
# File lib/solano/cli/prompt.rb, line 12
def prompt_missing_ssh_key
  keys = @solano_api.get_keys
  if keys.empty? then
    say Text::Process::SSH_KEY_NEEDED
    keydata = prompt_ssh_key(nil)
    result = @solano_api.set_keys({:keys => [keydata]})
    return true
  end
  false
rescue SolanoError => e
  exit_failure e.message
rescue TddiumClient::Error::API => e
  exit_failure e.explanation
end
prompt_ssh_key(current, name='default') click to toggle source
# File lib/solano/cli/prompt.rb, line 27
def prompt_ssh_key(current, name='default')
  # Prompt for ssh-key file
  ssh_file = prompt(Text::Prompt::SSH_KEY, current, Default::SSH_FILE)
  Solano::Ssh.load_ssh_key(ssh_file, name)
end
prompt_suite_params(options, params, current={}) click to toggle source
# File lib/solano/cli/prompt.rb, line 33
def prompt_suite_params(options, params, current={})
  say Text::Process::DETECTED_BRANCH % params[:branch] if params[:branch]
  params[:ruby_version] ||= tool_version(:ruby)
  params[:bundler_version] ||= normalize_bundler_version(tool_version(:bundle))
  params[:rubygems_version] ||= tool_version(:gem)

  ask_or_update = lambda do |key, text, default|
    params[key] = prompt(text, options[key], current.fetch(key.to_s, default), options[:non_interactive])
  end

  account_announced = false

  # If we already have a suite, it already has an account, so no need to
  # figure it out here.
  unless current['account_id']
    # Find an account id. Strategy:
    # 1. Use a command line option, if specified.
    # 2. If the user has only one account, use that.
    # 3. If the user has existing suites with the same repo, and they are
    # all in the same account, prompt with that as a default.
    # 4. Prompt.
    # IF we're not allowed to prompt and have no default, fail.
    accounts = user_details["participating_accounts"]
    account_name = if options[:account]
      account_announced = true
      say Text::Process::USING_ACCOUNT_FROM_FLAG % options[:account]
      options[:account]
    elsif accounts.length == 1
      account_announced = true
      say Text::Process::USING_ACCOUNT % accounts.first["account"]
      accounts.first["account"]
    else
      # Get all of this user's suites with this repo.
      repo_suites = @solano_api.get_suites(:repo_url => params[:repo_url])
      acct_ids = repo_suites.map{|s| s['account']}.uniq
      default = acct_ids.length == 1 ? acct_ids.first : nil

      if not options[:non_interactive] or default.nil?
        say "You are a member of these organizations:"
        accounts.each do |account|
          say "  " + account['account']
        end
      end

      msg = default.nil? ? Text::Prompt::ACCOUNT : Text::Prompt::ACCOUNT_DEFAULT
      prompt(msg, nil, default, options[:non_interactive])
    end

    if account_name.nil?
      exit_failure (options[:non_interactive] ?
                    Text::Error::MISSING_ACCOUNT_OPTION :
                    Text::Error::MISSING_ACCOUNT)
    end
    account = accounts.select{|a| a['account'] == account_name}.first
    if account.nil?
      exit_failure Text::Error::NOT_IN_ACCOUNT % account_name
    end

    if !account_announced then
      say Text::Process::USING_ACCOUNT % account_name
    end
    params[:account_id] = account["account_id"].to_s
  end

  pattern = configured_test_pattern
  cfn = @repo_config.config_filename

  if pattern.is_a?(Array)
    say Text::Process::CONFIGURED_PATTERN % [cfn, pattern.map{|p| " - #{p}"}.join("\n"), cfn]
    params[:test_pattern] = pattern.join(",")
  elsif pattern
    exit_failure Text::Error::INVALID_CONFIGURED_PATTERN % [cfn, cfn, pattern.inspect, cfn]
  else
    say Text::Process::TEST_PATTERN_INSTRUCTIONS unless options[:non_interactive]
    ask_or_update.call(:test_pattern, Text::Prompt::TEST_PATTERN, Default::SUITE_TEST_PATTERN)
  end

  exclude_pattern = configured_test_exclude_pattern
  cfn = @repo_config.config_filename

  if exclude_pattern.is_a?(Array)
    say Text::Process::CONFIGURED_EXCLUDE_PATTERN % [cfn, exclude_pattern.map{|p| " - #{p}"}.join("\n"), cfn]
    params[:test_exclude_pattern] = exclude_pattern.join(",")
  elsif exclude_pattern
    exit_failure Text::Error::INVALID_CONFIGURED_PATTERN % [cfn, cfn, exclude_pattern.inspect, cfn]
  end

  unless options[:non_interactive]
    say(Text::Process::SETUP_CI)
  end

  ask_or_update.call(:ci_pull_url, Text::Prompt::CI_PULL_URL, @scm.origin_url) 
  ask_or_update.call(:ci_push_url, Text::Prompt::CI_PUSH_URL, nil)
end
set_shell() click to toggle source
# File lib/solano/cli/util.rb, line 9
def set_shell
  if !$stdout.tty? || !$stderr.tty? then
    @shell = Thor::Shell::Basic.new
  end
end
show_attributes(names_to_display, attributes) click to toggle source
# File lib/solano/cli/show.rb, line 19
def show_attributes(names_to_display, attributes)
  names_to_display.each do |attr|
    say Text::Status::ATTRIBUTE_DETAIL % [attr.gsub("_", " ").capitalize, attributes[attr]] if attributes[attr]
  end
end
show_config_details(scope, config) click to toggle source
# File lib/solano/cli/show.rb, line 7
def show_config_details(scope, config)
  if !config || config.length == 0
    say Text::Process::NO_CONFIG
  else
    say Text::Status::CONFIG_DETAILS % scope
    config.each do |k,v| 
      say "#{k}=#{v}"
    end
  end
  say Text::Process::CONFIG_EDIT_COMMANDS
end
show_keys_details(keys) click to toggle source
# File lib/solano/cli/show.rb, line 25
def show_keys_details(keys)
  say Text::Status::KEYS_DETAILS
  if keys.length == 0
    say Text::Process::NO_KEYS
  else
    keys.each do |k| 
      if k["fingerprint"]
        say((" %-18.18s %s" % [k["name"], k["fingerprint"]]).rstrip)
      elsif k["pub"]
        fingerprint = ssh_key_fingerprint(k["pub"])
        if fingerprint then
          say((" %-18.18s %s" % [k["name"], fingerprint]).rstrip)
        else
          say((" %-18.18s" % k["name"]).rstrip)
        end
      else
        say((" %-18.18s" % k["name"]).rstrip)
      end
    end
  end
  say Text::Process::KEYS_EDIT_COMMANDS
end
show_ssh_config(dir=nil) click to toggle source
# File lib/solano/cli/show.rb, line 52
def show_ssh_config(dir=nil)
  dir ||= ENV['SOLANO_GEM_KEY_DIR']
  dir ||= Default::SSH_OUTPUT_DIR

  path = File.expand_path(File.join(dir, "identity.solano.*"))

  Dir[path].reject{|fn| fn =~ /.pub$/}.each do |fn|
    say Text::Process::SSH_CONFIG % {:scm_host=>"git.solanolabs.com", :file=>fn}
  end
end
show_third_party_keys_details(user) click to toggle source
# File lib/solano/cli/show.rb, line 48
def show_third_party_keys_details(user)
  say ERB.new(Text::Status::USER_THIRD_PARTY_KEY_DETAILS).result(binding)
end
show_user_details(user) click to toggle source
# File lib/solano/cli/show.rb, line 68
def show_user_details(user)
  current_suites = @solano_api.get_suites
  memberships = @solano_api.get_memberships
  account_usage = @solano_api.get_usage

  # Given the user is logged in, he should be able to
  # use "solano account" to display information about his account:
  # Email address
  # Account creation date
  say ERB.new(Text::Status::USER_DETAILS).result(binding)

  # Use "all_accounts" here instead of "participating_accounts" -- these
  # are the accounts the user can administer.
  # Select those accounts that match org param or all if no org param

  org = options['org']
  regexp = org.nil? ? nil : Regexp.new(org.to_s)
  account_list = user['all_accounts']
  account_list.select! do |acct|
    if regexp.nil? || regexp =~ acct[:account] then
      acct
    else
      nil
    end
  end
  account_list.compact!

  account_list.each do |acct|
    id = acct['account_id'].to_i

    say ERB.new(Text::Status::ACCOUNT_DETAILS).result(binding)

    acct_suites = current_suites.select{|s| s['account_id'].to_i == id}
    if acct_suites.empty? then
      say '  ' + Text::Status::NO_SUITE
    else
      say '  ' + Text::Status::ALL_SUITES
      suites = acct_suites.sort_by{|s| "#{s['org_name']}/#{s['repo_name']}"}
      print_table suites.map {|suite|
        repo_name = suite['repo_name']
        if suite['org_name'] && suite['org_name'] != 'unknown'
          repo_name = suite['org_name'] + '/' + repo_name
        end
        [repo_name, suite['branch'], suite['repo_url'] || '']
      }, :indent => 4
    end

    # Uugh, json converts the keys to strings.
    usage = account_usage[id.to_s]
    if usage
      say "\n  Usage:"
      say "    Current month:  " + format_usage(usage["current_month"])
      say "    Last month:     " + format_usage(usage["last_month"])
    end

    acct_members = memberships.select{|m| m['account_id'].to_i == id}
    if acct_members.length > 1
      say "\n  " + Text::Status::ACCOUNT_MEMBERS
      print_table acct_members.map {|ar|
        [ar['user_handle'], ar['user_email'], ar['role']]
      }, :indent => 4
    end
  end

rescue TddiumClient::Error::Base => e
  exit_failure e.message
end
sniff_ruby_version() click to toggle source
# File lib/solano/cli/util.rb, line 58
def sniff_ruby_version
  ruby_version = @repo_config["ruby_version"]
  return ruby_version unless ruby_version.nil? || ruby_version.to_s.empty?

  if !options[:machine] then
    scm_root = @scm.root
    if scm_root then
      rvmrc = File.join(scm_root, '.rvmrc')
      ruby_version_path = File.join(scm_root, '.ruby_version')
      if File.exists?(ruby_version_path) then
        ruby_version = sniff_ruby_version_rvmrc(ruby_version)
      elsif File.exists?(rvmrc) then
        ruby_version = sniff_ruby_version_rvmrc(rvmrc)
        warn("Detected ruby #{ruby_version} in .rvmrc; make sure patch level is correct")
      end
    end
  end
  return ruby_version
end
sniff_ruby_version_rvmrc(rvmrc) click to toggle source
# File lib/solano/cli/util.rb, line 41
def sniff_ruby_version_rvmrc(rvmrc)
  ruby_version = nil
  File.open(rvmrc, 'r') do |file|
    file.each_line do |line|
      line.sub!(/^\s+/, '')
      next unless line =~ /^rvm/
      fields = Shellwords.shellsplit(line)
      fields.each do |field|
        if field =~ /^(ree|1[.][89])/ then
          ruby_version = field.sub(/@.*/, '')
        end
      end
    end
  end
  return ruby_version
end
solano_setup(params={}) click to toggle source
# File lib/solano/cli/solano.rb, line 124
def solano_setup(params={})
  if params[:deprecated] then
    say Text::Error::COMMAND_DEPRECATED
  end

  # suite => repo => scm
  params[:suite] = params[:suite] == true

  params[:repo] = params[:repo] == true
  params[:repo] ||= params[:suite]

  params[:scm] = !params.member?(:scm) || params[:scm] == true
  params[:scm] ||= params[:repo]

  params[:login] = true unless params[:login] == false

  $stdout.sync = true
  $stderr.sync = true

  set_shell

  @scm, ok = Solano::SCM.configure
  if params[:scm] && !ok then
    say Text::Error::SCM_NOT_FOUND
    exit_failure
  end

  @repo_config = RepoConfig.new(@scm)
  if origin_url = @repo_config[:origin_url] then
    @scm.default_origin_url = origin_url
  end

  host = @cli_options[:host]
  @api_config = ApiConfig.new(@scm, @tddium_client, host, @cli_options)
  @solano_api = SolanoAPI.new(@scm, @tddium_client, @api_config, {v3: @tddium_clientv3})

  @api_config.set_api(@solano_api)

  begin
    @api_config.load_config
  rescue ::Solano::SolanoError => e
    say e.message
    exit_failure
  end

  user_details = @solano_api.user_logged_in?(true, params[:login])
  if params[:login] && user_details.nil? then
    exit_failure
  end

  if params[:repo] then
    if !@scm.repo? then
      say Text::Error::SCM_NOT_A_REPOSITORY
      exit_failure
    end

    if @scm.origin_url.nil? then
      say Text::Error::SCM_NO_ORIGIN
      exit_failure
    end

    begin
      Solano::SCM.valid_repo_url?(@scm.origin_url)
    rescue SolanoError => e
      say e.message
      exit_failure
    end
  end

  if params[:suite] then
    if @scm.current_branch.nil? then
      say Text::Error::SCM_NO_BRANCH
      exit_failure
    end

    if !suite_for_current_branch? then
      exit_failure
    end
  end

  @user_details = user_details
end
ssh_key_fingerprint(pubkey) click to toggle source
# File lib/solano/cli/util.rb, line 87
def ssh_key_fingerprint(pubkey)
  tmp = Tempfile.new('ssh-pub')
  tmp.write(pubkey)
  tmp.close
  fingerprint = nil
  IO.popen("ssh-keygen -lf #{tmp.path}") do |io|
    fingerprint = io.read
    # Keep just bits and fingerprint
    if fingerprint then
      fingerprint.chomp!
      fingerprint = fingerprint.split(/\s+/)[0..1].join(' ')
    end
  end
  tmp.unlink

  return fingerprint
end
suite_auto_configure() click to toggle source
# File lib/solano/cli/suite.rb, line 22
def suite_auto_configure
  # Did the user set a configuration option on the command line?
  # If so, auto-configure a new suite and re-configure an existing one
  user_config = options.member?(:tool)
  current_suite_id = calc_current_suite_id
  if current_suite_id && !user_config then
    current_suite = @solano_api.get_suite_by_id(current_suite_id)
  else
    params = Hash.new
    params[:branch] = @scm.current_branch
    params[:repo_url] = @scm.origin_url
    params[:repo_name] = options[:pipeline] || @scm.repo_name
    params[:scm] = @scm.scm_name
    if options[:account] && !params.member?(:account_id) then
      account_id = @solano_api.get_account_id(options[:account])
      params[:account_id] = account_id if account_id
    end

    tool_cli_populate(options, params)
    defaults = {}

    prompt_suite_params(options.merge({:non_interactive => true}), params, defaults)

    # Create new suite if it does not exist yet
    say Text::Process::CREATING_SUITE % [params[:repo_name], params[:branch]]

    current_suite = @solano_api.create_suite(params)

    # Save the created suite
    @api_config.set_suite(current_suite)
    @api_config.write_config
  end
  return current_suite
end
suite_for_current_branch?() click to toggle source
# File lib/solano/cli/suite.rb, line 79
def suite_for_current_branch?
  return true if calc_current_suite_id
  say Text::Error::NO_SUITE_EXISTS % @scm.current_branch
  false
end
suite_for_default_branch?() click to toggle source
# File lib/solano/cli/suite.rb, line 85
def suite_for_default_branch?
  return true if @solano_api.default_suite_id
  say Text::Error::NO_SUITE_EXISTS % @scm.default_branch
  false
end
suite_remembered_option(options, key, default) { |result| ... } click to toggle source
repo_config_file has authority over solano.yml now
Update the suite parameters from solano.yml

def update_suite_parameters!(current_suite, session_id=nil) end

# File lib/solano/cli/suite.rb, line 96
def suite_remembered_option(options, key, default, &block)
  remembered = false
  if options[key] != default
    result = options[key]
  elsif remembered = current_suite_options[key.to_s]
    result = remembered
    remembered = true
  else
    result = default
  end

  if result then
    msg = Text::Process::USING_SPEC_OPTION[key] % result
    msg +=  Text::Process::REMEMBERED if remembered
    msg += "\n"
    say msg
    yield result if block_given?
  end
  result
end
tool_cli_populate(options, params) click to toggle source
# File lib/solano/cli/util.rb, line 15
def tool_cli_populate(options, params)
  if options[:tool].is_a?(Hash) then
    options[:tool].each_pair do |key, value|
      params[key.to_sym] = value
    end
  end
end
tool_version(tool) click to toggle source
# File lib/solano/cli/util.rb, line 23
def tool_version(tool)
  key = "#{tool}_version".to_sym
  result = @repo_config[key]

  if result
    say Text::Process::CONFIGURED_VERSION % [tool, result, @repo_config.config_filename]
    return result
  end

  begin
    result = `#{tool} -v`.strip
  rescue Errno::ENOENT
    exit_failure("#{tool} is not on PATH; please install and try again")
  end
  say Text::Process::DEPENDENCY_VERSION % [tool, result]
  result
end
update_suite(suite, options) click to toggle source
# File lib/solano/cli/suite.rb, line 7
def update_suite(suite, options)
  params = {}
  prompt_suite_params(options, params, suite)

  ask_or_update = lambda do |key, text, default|
    params[key] = prompt(text, options[key], suite.fetch(key.to_s, default), options[:non_interactive])
  end

  ask_or_update.call(:campfire_room, Text::Prompt::CAMPFIRE_ROOM, '')
  ask_or_update.call(:hipchat_room, Text::Prompt::HIPCHAT_ROOM, '')

  @solano_api.update_suite(suite['id'], params)
  say Text::Process::UPDATED_SUITE
end
warn(msg='') click to toggle source
# File lib/solano/cli/util.rb, line 105
def warn(msg='')
  STDERR.puts("WARNING: #{msg}")
end

Private Instance Methods

cache_control_config() click to toggle source
# File lib/solano/cli/commands/spec.rb, line 354
def cache_control_config
  @repo_config['cache'] || {}
end
capture_stdout() { || ... } click to toggle source
# File lib/solano/cli/commands/status.rb, line 135
def capture_stdout
  old, $stdout = $stdout, StringIO.new
  yield
  $stdout.string
ensure
  $stdout = old
end
docker_enabled() click to toggle source
# File lib/solano/cli/commands/spec.rb, line 342
def docker_enabled
  if @repo_config['system'] then
     if @repo_config['system']['docker'] then
       @repo_config['system']['docker']
     else
       false
     end
  else
    false
  end
end
duration(d) click to toggle source

return nice string version of hrs mins secs from time delta

# File lib/solano/cli/commands/console.rb, line 82
def duration(d)
  secs = d.to_int
  mins = secs / 60
  hours = mins / 60
  if hours > 0 then
    "#{hours} hours #{mins % 60} minutes"
  elsif mins > 0 then
    "#{mins} minutes #{secs % 60} seconds"
  else
    "#{secs} seconds"
  end
end
find_failing_set(sets, failing) click to toggle source
# File lib/solano/cli/commands/find_failing.rb, line 38
def find_failing_set(sets, failing)
  sets.each do |set|
    next if set == [failing]
    if !success?(set)
      if set.size == 2
        return [:finished, set]
      else
        return [:continue, set]
      end
    end
  end
  return [:failure, []]
end
read_and_encode_cache_control() click to toggle source
# File lib/solano/cli/commands/spec.rb, line 358
def read_and_encode_cache_control
  cache_key_paths = cache_control_config['key_paths'] || cache_control_config[:key_paths]
  cache_key_paths ||= ["Gemfile", "Gemfile.lock", "requirements.txt", "packages.json", "package.json"]
  cache_key_paths.reject!{|x| x =~ /(solano|tddium).yml$/}

  if docker_enabled then
    cache_key_paths << "Dockerfile"
  end

  cache_control_data = {}
  cache_key_paths.each do |p|
    if File.exists?(p) then
      cache_control_data[p] = Digest::SHA1.file(p).to_s
    end
  end

  msgpack = Solano.message_pack(cache_control_data)
  cache_control_encoded = Base64.encode64(msgpack)
end
read_and_encode_cache_save_paths() click to toggle source
# File lib/solano/cli/commands/spec.rb, line 378
def read_and_encode_cache_save_paths
  cache_save_paths = cache_control_config['save_paths'] || cache_control_config[:save_paths]

  if docker_enabled then
    (cache_save_paths ||= []) << "HOME/docker-graph"
  end

  msgpack = Solano.message_pack(cache_save_paths)
  cache_save_paths_encoded = Base64.encode64(msgpack)
end
read_and_encode_config_file() click to toggle source
# File lib/solano/cli/commands/spec.rb, line 389
def read_and_encode_config_file
  config, raw_config = @repo_config.read_config(false)
  encoded = Base64.encode64(raw_config)
  return encoded
end
read_and_encode_latest_commits() click to toggle source
# File lib/solano/cli/commands/spec.rb, line 335
def read_and_encode_latest_commits
  commits = @scm.commits
  commits_packed = Solano.message_pack(commits)
  commits_encoded = Base64.encode64(commits_packed)
  commits_encoded
end
remove_from(set, x, options) click to toggle source
# File lib/solano/cli/commands/find_failing.rb, line 52
def remove_from(set, x, options)
  set.dup.delete_if { |f| f != options[:not] && (x -= 1) >= 0 }
end
session_id_for_current_suite() click to toggle source
# File lib/solano/cli/commands/rerun.rb, line 51
def session_id_for_current_suite
  return unless suite_for_current_branch?
  suite_params = {
    :suite_id => calc_current_suite_id,
    :active => false,
    :limit => 1,
    :origin => %w(ci cli)
  }
  session = @solano_api.get_sessions(suite_params)
  session[0]["id"]
end
show_session_details(status_branch, params, no_session_prompt, all_session_prompt, include_branch) click to toggle source
# File lib/solano/cli/commands/status.rb, line 101
def show_session_details(status_branch, params, no_session_prompt, all_session_prompt, include_branch)
  current_sessions = @solano_api.get_sessions(params)

  say ""
  if current_sessions.empty? then
    say no_session_prompt
  else
    commit_size = 0...7
    head = @scm.current_commit[commit_size]

    say all_session_prompt % (params[:suite_id] ? status_branch : "")
    say ""
    header = ["Session #", "Commit", ("Branch" if include_branch), "Status", "Duration", "Started"].compact
    table = [header, header.map { |t| "-" * t.size }] + current_sessions.map do |session|
      duration = "%ds" % session['duration']
      start_timeago = "%s ago" % Solano::TimeFormat.seconds_to_human_time(Time.now - Time.parse(session["start_time"]))
      status = session["status"]
      if status.nil? || status.strip == "" then
        status = 'unknown'
      end

      [
        session["id"].to_s,
        session["commit"] ? session['commit'][commit_size] : '-      ',
        (session["branch"] if include_branch),
        status,
        duration,
        start_timeago
      ].compact
    end
    say(capture_stdout { print_table table }.gsub(head, "\e[7m#{head}\e[0m"))
  end
end
success?(files) click to toggle source
# File lib/solano/cli/commands/find_failing.rb, line 56
def success?(files)
  command = "bundle exec ruby #{files.map { |f| "-r./#{f.sub(/\.rb$/, "")}" }.join(" ")} -e ''"
  say "Running: #{command}"
  status = system(command)
  say "Status: #{status ? "Success" : "Failure"}"
  status
end
update_messages(latest_message, finished_tests, messages, current, display=true) click to toggle source
# File lib/solano/cli/commands/spec.rb, line 319
def update_messages(latest_message, finished_tests, messages, current, display=true)
  messages = current
  if !options[:machine] && finished_tests.size == 0 && messages
    messages.each do |m|
      seqno = m["seqno"].to_i
      if seqno > latest_message
        if !options[:quiet] || m["level"] == 'error' then
          display_message(m)
        end
        latest_message = seqno
      end
    end
  end
  [messages, latest_message]
end