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.
- checksums.yaml +15 -0
- data/bin/solano +29 -0
- data/bin/tddium +29 -0
- data/lib/solano.rb +19 -0
- data/lib/solano/agent.rb +3 -0
- data/lib/solano/agent/solano.rb +128 -0
- data/lib/solano/cli.rb +25 -0
- data/lib/solano/cli/api.rb +368 -0
- data/lib/solano/cli/commands/account.rb +50 -0
- data/lib/solano/cli/commands/activate.rb +16 -0
- data/lib/solano/cli/commands/api.rb +15 -0
- data/lib/solano/cli/commands/config.rb +78 -0
- data/lib/solano/cli/commands/console.rb +85 -0
- data/lib/solano/cli/commands/describe.rb +104 -0
- data/lib/solano/cli/commands/find_failing.rb +64 -0
- data/lib/solano/cli/commands/heroku.rb +17 -0
- data/lib/solano/cli/commands/hg.rb +48 -0
- data/lib/solano/cli/commands/keys.rb +81 -0
- data/lib/solano/cli/commands/login.rb +37 -0
- data/lib/solano/cli/commands/logout.rb +14 -0
- data/lib/solano/cli/commands/password.rb +26 -0
- data/lib/solano/cli/commands/rerun.rb +59 -0
- data/lib/solano/cli/commands/server.rb +21 -0
- data/lib/solano/cli/commands/spec.rb +401 -0
- data/lib/solano/cli/commands/status.rb +117 -0
- data/lib/solano/cli/commands/stop.rb +19 -0
- data/lib/solano/cli/commands/suite.rb +110 -0
- data/lib/solano/cli/commands/support.rb +24 -0
- data/lib/solano/cli/commands/web.rb +29 -0
- data/lib/solano/cli/config.rb +246 -0
- data/lib/solano/cli/params_helper.rb +66 -0
- data/lib/solano/cli/prompt.rb +128 -0
- data/lib/solano/cli/show.rb +136 -0
- data/lib/solano/cli/solano.rb +208 -0
- data/lib/solano/cli/suite.rb +104 -0
- data/lib/solano/cli/text_helper.rb +16 -0
- data/lib/solano/cli/timeformat.rb +21 -0
- data/lib/solano/cli/util.rb +132 -0
- data/lib/solano/constant.rb +581 -0
- data/lib/solano/scm.rb +18 -0
- data/lib/solano/scm/configure.rb +37 -0
- data/lib/solano/scm/git.rb +349 -0
- data/lib/solano/scm/git_log_parser.rb +67 -0
- data/lib/solano/scm/hg.rb +263 -0
- data/lib/solano/scm/hg_log_parser.rb +66 -0
- data/lib/solano/scm/scm.rb +119 -0
- data/lib/solano/scm/scm_stub.rb +9 -0
- data/lib/solano/scm/url.rb +75 -0
- data/lib/solano/script.rb +12 -0
- data/lib/solano/script/git-remote-hg +1258 -0
- data/lib/solano/ssh.rb +66 -0
- data/lib/solano/util.rb +63 -0
- data/lib/solano/version.rb +5 -0
- 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
|