avodeploy 0.4 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +14 -0
- data/README.md +8 -5
- data/avodeploy.gemspec +3 -4
- data/bin/avo +14 -12
- data/lib/avodeploy.rb +29 -0
- data/lib/{avocado → avodeploy}/bootstrap.rb +24 -30
- data/lib/{avocado → avodeploy}/command_execution_result.rb +1 -1
- data/lib/{avocado → avodeploy}/config.rb +8 -4
- data/lib/{avocado → avodeploy}/core_ext/hash_insert_at.rb +0 -0
- data/lib/{avocado → avodeploy}/core_ext/string_colors.rb +0 -0
- data/lib/{avocado → avodeploy}/deployment.rb +3 -3
- data/lib/{avocado → avodeploy}/multi_io.rb +1 -1
- data/lib/avodeploy/scm_provider/git_scm_provider.rb +73 -0
- data/lib/avodeploy/scm_provider/scm_provider.rb +70 -0
- data/lib/{avocado → avodeploy}/skel/manifest_template.rb.erb +1 -1
- data/lib/{avocado → avodeploy}/strategy/base.rb +1 -1
- data/lib/avodeploy/strategy/local_copy.rb +101 -0
- data/lib/{avocado → avodeploy}/target.rb +3 -1
- data/lib/avodeploy/task/local_task_execution_environment.rb +127 -0
- data/lib/avodeploy/task/remote_task_execution_environment.rb +114 -0
- data/lib/avodeploy/task/task.rb +76 -0
- data/lib/{avocado → avodeploy}/task/task_dependency.rb +8 -6
- data/lib/avodeploy/task/task_execution_environment.rb +88 -0
- data/lib/avodeploy/task/task_manager.rb +185 -0
- data/lib/{avocado → avodeploy}/version.rb +2 -2
- metadata +27 -40
- data/lib/avocado/Gemfile +0 -9
- data/lib/avocado/scm_provider/git_scm_provider.rb +0 -71
- data/lib/avocado/scm_provider/scm_provider.rb +0 -68
- data/lib/avocado/strategy/local_copy.rb +0 -99
- data/lib/avocado/task/local_task_execution_environment.rb +0 -127
- data/lib/avocado/task/remote_task_execution_environment.rb +0 -112
- data/lib/avocado/task/task.rb +0 -84
- data/lib/avocado/task/task_execution_environment.rb +0 -86
- data/lib/avocado/task/task_manager.rb +0 -183
data/lib/avocado/Gemfile
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
=begin
|
2
|
-
AVOCADO
|
3
|
-
The flexible and easy to use deployment framework for web applications
|
4
|
-
|
5
|
-
This program is free software; you can redistribute it and/or
|
6
|
-
modify it under the terms of the GNU General Public License Version 2
|
7
|
-
as published by the Free Software Foundation.
|
8
|
-
|
9
|
-
This program is distributed in the hope that it will be useful,
|
10
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
-
GNU General Public License for more details.
|
13
|
-
|
14
|
-
You should have received a copy of the GNU General Public License
|
15
|
-
along with this program; if not, write to the Free Software
|
16
|
-
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
-
=end
|
18
|
-
|
19
|
-
module Avocado
|
20
|
-
class GitScmProvider
|
21
|
-
|
22
|
-
# Initializes the provider
|
23
|
-
#
|
24
|
-
# @param env [TaskExecutionEnvironment] Environment for the commands to be executed in
|
25
|
-
def initialize(env)
|
26
|
-
raise ArgumentError, "env must be a TaskExecutionEnvironment" unless env.kind_of?(TaskExecutionEnvironment)
|
27
|
-
|
28
|
-
@env = env
|
29
|
-
end
|
30
|
-
|
31
|
-
# Checks out repository code from a system and switches to the given branch
|
32
|
-
#
|
33
|
-
# @param url [String] the repository location
|
34
|
-
# @param local_dir [String] path to the working copy
|
35
|
-
# @param branch [String] the branch to check out
|
36
|
-
def checkout_from_remote(url, local_dir, branch)
|
37
|
-
res = @env.command("git clone #{url} #{local_dir}")
|
38
|
-
raise RuntimeError, "Could not clone from git url #{url}" unless res.retval == 0
|
39
|
-
|
40
|
-
@env.chdir(local_dir)
|
41
|
-
res = @env.command("git checkout #{branch}")
|
42
|
-
@env.chdir('../')
|
43
|
-
|
44
|
-
raise RuntimeError, "could not switch to branch #{branch}" unless res.retval == 0
|
45
|
-
end
|
46
|
-
|
47
|
-
# Returns the current revision of the working copy
|
48
|
-
#
|
49
|
-
# @return [String] the current revision of the working copy
|
50
|
-
def revision
|
51
|
-
res = @env.command("git rev-parse HEAD")
|
52
|
-
|
53
|
-
res.stdout.gsub("\n", "")
|
54
|
-
end
|
55
|
-
|
56
|
-
# Returns scm files to be executed in the deployment process
|
57
|
-
#
|
58
|
-
# @return [Array] array of scm control files
|
59
|
-
def scm_files
|
60
|
-
[ '.git', '.gitignore' ]
|
61
|
-
end
|
62
|
-
|
63
|
-
# Returns the scm tools that have to be installed on specific systems
|
64
|
-
#
|
65
|
-
# @return [Array] array of utilities
|
66
|
-
def cli_utils
|
67
|
-
[ 'git' ]
|
68
|
-
end
|
69
|
-
|
70
|
-
end
|
71
|
-
end
|
@@ -1,68 +0,0 @@
|
|
1
|
-
=begin
|
2
|
-
AVOCADO
|
3
|
-
The flexible and easy to use deployment framework for web applications
|
4
|
-
|
5
|
-
This program is free software; you can redistribute it and/or
|
6
|
-
modify it under the terms of the GNU General Public License Version 2
|
7
|
-
as published by the Free Software Foundation.
|
8
|
-
|
9
|
-
This program is distributed in the hope that it will be useful,
|
10
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
-
GNU General Public License for more details.
|
13
|
-
|
14
|
-
You should have received a copy of the GNU General Public License
|
15
|
-
along with this program; if not, write to the Free Software
|
16
|
-
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
-
=end
|
18
|
-
|
19
|
-
module Avocado
|
20
|
-
# scm provider facade
|
21
|
-
class ScmProvider
|
22
|
-
|
23
|
-
# Initializes the scm provider
|
24
|
-
#
|
25
|
-
# @param env [TaskExecutionEnvironment] env for the commands to be executed in
|
26
|
-
# @param scm [Symbol] the scm provider to user
|
27
|
-
def initialize(env, scm)
|
28
|
-
raise ArgumentError, 'env must be a TaskExecutionEnvironment' unless env.is_a?(TaskExecutionEnvironment)
|
29
|
-
|
30
|
-
@env = env
|
31
|
-
@real_provider = nil
|
32
|
-
|
33
|
-
if scm == :git
|
34
|
-
@real_provider = GitScmProvider.new(env)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Checks out repository code from a system and switches to the given branch
|
39
|
-
#
|
40
|
-
# @param url [String] the repository location
|
41
|
-
# @param local_dir [String] path to the working copy
|
42
|
-
# @param branch [String] the branch to check out
|
43
|
-
def checkout_from_remote(url, local_dir, branch)
|
44
|
-
@real_provider.checkout_from_remote(url, local_dir, branch)
|
45
|
-
end
|
46
|
-
|
47
|
-
# Returns the current revision of the working copy
|
48
|
-
#
|
49
|
-
# @return [String] the current revision of the working copy
|
50
|
-
def scm_files
|
51
|
-
@real_provider.scm_files
|
52
|
-
end
|
53
|
-
|
54
|
-
# Returns scm files to be executed in the deployment process
|
55
|
-
#
|
56
|
-
# @return [Array] array of scm control files
|
57
|
-
def cli_utils
|
58
|
-
@real_provider.cli_utils
|
59
|
-
end
|
60
|
-
|
61
|
-
# Returns the scm tools that have to be installed on specific systems
|
62
|
-
#
|
63
|
-
# @return [Array] array of utilities
|
64
|
-
def revision
|
65
|
-
@real_provider.revision
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
@@ -1,99 +0,0 @@
|
|
1
|
-
=begin
|
2
|
-
AVOCADO
|
3
|
-
The flexible and easy to use deployment framework for web applications
|
4
|
-
|
5
|
-
This program is free software; you can redistribute it and/or
|
6
|
-
modify it under the terms of the GNU General Public License Version 2
|
7
|
-
as published by the Free Software Foundation.
|
8
|
-
|
9
|
-
This program is distributed in the hope that it will be useful,
|
10
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
-
GNU General Public License for more details.
|
13
|
-
|
14
|
-
You should have received a copy of the GNU General Public License
|
15
|
-
along with this program; if not, write to the Free Software
|
16
|
-
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
-
=end
|
18
|
-
|
19
|
-
Avocado::Deployment.configure do
|
20
|
-
|
21
|
-
task :check_local_tools, before: :deploy, scope: :local, visibility: :private do
|
22
|
-
check_util_availability [ 'tar' ].concat(@scm.cli_utils)
|
23
|
-
end
|
24
|
-
|
25
|
-
task :check_remote_system, before: :deploy, scope: :remote, visibility: :private do
|
26
|
-
check_util_availability [ 'tar' ]
|
27
|
-
end
|
28
|
-
|
29
|
-
task :check_temp_existance, visibility: :private, after: :deploy, scope: :local do
|
30
|
-
if File.exist?('.avocado-tmp')
|
31
|
-
raise RuntimeError, 'The avocado tmp directory (.avocado-tmp) does already exist. That may indicate that another deployment is already running.'
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
task :switch_to_temp_dir, visibility: :private, after: :check_temp_existance, scope: :local do
|
36
|
-
command "mkdir .avocado-tmp/"
|
37
|
-
chdir(".avocado-tmp/")
|
38
|
-
end
|
39
|
-
|
40
|
-
task :checkout_from_scm, visibility: :private, after: :switch_to_temp_dir, scope: :local do
|
41
|
-
@scm.checkout_from_remote(get(:repo_url), 'working-copy', get(:branch))
|
42
|
-
end
|
43
|
-
|
44
|
-
task :get_scm_info, visibility: :private, after: :checkout_from_scm, scope: :local do
|
45
|
-
chdir("working-copy/")
|
46
|
-
|
47
|
-
set(:revision, @scm.revision)
|
48
|
-
|
49
|
-
chdir("../")
|
50
|
-
end
|
51
|
-
|
52
|
-
task :delete_ignored_files, visibility: :private, after: :get_scm_info, scope: :local do
|
53
|
-
if get(:ignore_files).concat(@scm.scm_files).size > 0
|
54
|
-
chdir("working-copy/")
|
55
|
-
command "rm -rfv #{get(:ignore_files).concat(@scm.scm_files).join(' ')}"
|
56
|
-
chdir("../")
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
task :create_revision_file, visibility: :private, after: :delete_ignored_files, scope: :local do
|
61
|
-
chdir("working-copy/")
|
62
|
-
command "echo '#{get(:revision)}' > REVISION"
|
63
|
-
chdir("../")
|
64
|
-
end
|
65
|
-
|
66
|
-
task :create_deployment_tarball, visibility: :private, after: :create_revision_file, scope: :local do
|
67
|
-
chdir("working-copy/")
|
68
|
-
command "tar cvfz ../deploy.tar.gz ."
|
69
|
-
chdir("../")
|
70
|
-
end
|
71
|
-
|
72
|
-
task :upload, visibility: :private, after: :create_deployment_tarball, scope: :local do
|
73
|
-
targets.each_pair do |key, target|
|
74
|
-
copy_to_target(target, 'deploy.tar.gz', '/tmp/deploy.tar.gz')
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
task :create_deploy_dir, visibility: :private, after: :upload, scope: :remote do
|
79
|
-
command "if [ ! -d '#{get(:deploy_dir)}' ]; then mkdir -p #{get(:deploy_dir)}; fi"
|
80
|
-
end
|
81
|
-
|
82
|
-
task :unpack, visibility: :private, after: :create_deploy_dir, scope: :remote do
|
83
|
-
command "tar xvfz /tmp/deploy.tar.gz -C #{get(:deploy_dir)}/"
|
84
|
-
end
|
85
|
-
|
86
|
-
task :cleanup_remote, visibility: :private, after: :unpack, scope: :remote do
|
87
|
-
command "rm /tmp/deploy.tar.gz"
|
88
|
-
end
|
89
|
-
|
90
|
-
task :log_deployment, visibility: :private, after: :cleanup_remote, scope: :remote do
|
91
|
-
command "echo '[#{ Time.now.strftime('%Y-%m-%d %H-%M-%S')}] revision #{get(:revision)} deployed by #{ENV['USER']}' >> #{get(:log_file)}"
|
92
|
-
end
|
93
|
-
|
94
|
-
task :cleanup_local, visibility: :private, after: :cleanup_remote, scope: :local do
|
95
|
-
chdir("../")
|
96
|
-
command "rm -rf .avocado-tmp"
|
97
|
-
end
|
98
|
-
|
99
|
-
end
|
@@ -1,127 +0,0 @@
|
|
1
|
-
=begin
|
2
|
-
AVOCADO
|
3
|
-
The flexible and easy to use deployment framework for web applications
|
4
|
-
|
5
|
-
This program is free software; you can redistribute it and/or
|
6
|
-
modify it under the terms of the GNU General Public License Version 2
|
7
|
-
as published by the Free Software Foundation.
|
8
|
-
|
9
|
-
This program is distributed in the hope that it will be useful,
|
10
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
-
GNU General Public License for more details.
|
13
|
-
|
14
|
-
You should have received a copy of the GNU General Public License
|
15
|
-
along with this program; if not, write to the Free Software
|
16
|
-
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
-
=end
|
18
|
-
|
19
|
-
module Avocado
|
20
|
-
class LocalTaskExecutionEnvironment < TaskExecutionEnvironment
|
21
|
-
|
22
|
-
# Initialized the environment
|
23
|
-
#
|
24
|
-
# @param config [Hash] deployment configuration
|
25
|
-
def initialize(config)
|
26
|
-
super
|
27
|
-
|
28
|
-
@dir = Dir.pwd
|
29
|
-
end
|
30
|
-
|
31
|
-
# Checks, if all utilities are available for the deployment process
|
32
|
-
# to be executed
|
33
|
-
#
|
34
|
-
# @param utils [Array] array with utilities to check
|
35
|
-
def check_util_availability(utils)
|
36
|
-
super(utils, 'locally')
|
37
|
-
end
|
38
|
-
|
39
|
-
# Changes the directory for commands to be executed in
|
40
|
-
#
|
41
|
-
# @param dir [String] the directory to change to
|
42
|
-
def chdir(dir)
|
43
|
-
log.debug "changing directory [#{dir.yellow}] " + "locally".cyan
|
44
|
-
|
45
|
-
Dir.chdir(dir)
|
46
|
-
@dir = Dir.pwd
|
47
|
-
end
|
48
|
-
|
49
|
-
# Returns the current working directory
|
50
|
-
#
|
51
|
-
# @return [String] current working directory
|
52
|
-
def cwd
|
53
|
-
@dir
|
54
|
-
end
|
55
|
-
|
56
|
-
# Returns all target systems to deploy to
|
57
|
-
#
|
58
|
-
# @return [Hash] hash of target systems
|
59
|
-
def targets
|
60
|
-
Avocado::Deployment.instance.config.targets
|
61
|
-
end
|
62
|
-
|
63
|
-
# Copies a file to a remote system (= target)
|
64
|
-
#
|
65
|
-
# @param target [Target] the target system to deploy to
|
66
|
-
# @param file [String] the local file to upload
|
67
|
-
# @param remote [String] path on the remote system
|
68
|
-
def copy_to_target(target, file, remote)
|
69
|
-
log = Avocado::Deployment.instance.log
|
70
|
-
|
71
|
-
log.info "started upload of file #{file} to #{target.name}"
|
72
|
-
|
73
|
-
Net::SSH.start(
|
74
|
-
target.config[:host],
|
75
|
-
target.config[:user]
|
76
|
-
) do |session|
|
77
|
-
session.scp.upload!(file, remote, :recursive => true) do |ch, name, sent, total|
|
78
|
-
percentage = 0
|
79
|
-
|
80
|
-
begin
|
81
|
-
percentage = (sent.to_f * 100 / total.to_f).to_i
|
82
|
-
rescue Exception => e
|
83
|
-
Avocado::Deployment.instance.handle_abort(e)
|
84
|
-
end
|
85
|
-
|
86
|
-
#log.info "\r#{name}: #{percentage}%"
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
log.info "upload completed"
|
91
|
-
end
|
92
|
-
|
93
|
-
# Executes a command locally in the current directory
|
94
|
-
#
|
95
|
-
# @param cmd [String] the command to execute
|
96
|
-
# @return [CommandExecutionResult] result of the command exection
|
97
|
-
def command(cmd)
|
98
|
-
log = Avocado::Deployment.instance.log
|
99
|
-
|
100
|
-
log.info "Executing [" + cmd.yellow + "] " + "locally".cyan
|
101
|
-
|
102
|
-
result = Avocado::CommandExecutionResult.new
|
103
|
-
|
104
|
-
begin
|
105
|
-
stdout, stderr, status = ::Open3.capture3(cmd, :chdir => cwd())
|
106
|
-
|
107
|
-
result.stdin = cmd
|
108
|
-
result.stdout = stdout
|
109
|
-
result.stderr = stderr
|
110
|
-
result.retval = status.exitstatus
|
111
|
-
|
112
|
-
if result.stdout.nil? == false && result.stdout.empty? == false
|
113
|
-
log.debug "Stdout: " + result.stdout.green
|
114
|
-
end
|
115
|
-
|
116
|
-
if result.stderr.nil? == false && result.stderr.empty? == false
|
117
|
-
log.debug "Stderr: " + result.stderr.red
|
118
|
-
end
|
119
|
-
rescue Exception => e
|
120
|
-
handle_abort e
|
121
|
-
end
|
122
|
-
|
123
|
-
result
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
127
|
-
end
|
@@ -1,112 +0,0 @@
|
|
1
|
-
=begin
|
2
|
-
AVOCADO
|
3
|
-
The flexible and easy to use deployment framework for web applications
|
4
|
-
|
5
|
-
This program is free software; you can redistribute it and/or
|
6
|
-
modify it under the terms of the GNU General Public License Version 2
|
7
|
-
as published by the Free Software Foundation.
|
8
|
-
|
9
|
-
This program is distributed in the hope that it will be useful,
|
10
|
-
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
-
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
-
GNU General Public License for more details.
|
13
|
-
|
14
|
-
You should have received a copy of the GNU General Public License
|
15
|
-
along with this program; if not, write to the Free Software
|
16
|
-
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
17
|
-
=end
|
18
|
-
|
19
|
-
module Avocado
|
20
|
-
class RemoteTaskExecutionEnvironment < TaskExecutionEnvironment
|
21
|
-
|
22
|
-
attr_accessor :config
|
23
|
-
|
24
|
-
# Creates a connection between the local and the remote system over ssh
|
25
|
-
def establish_connection
|
26
|
-
Avocado::Deployment.instance.log.info "connecting to #{get(:user)}@#{get(:host)}..."
|
27
|
-
|
28
|
-
begin
|
29
|
-
@session = ::Net::SSH.start(get(:host), get(:user))
|
30
|
-
rescue ::Net::SSH::AuthenticationFailed => e
|
31
|
-
handle_abort e
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
# Checks, if all utilities are available for the deployment process
|
36
|
-
# to be executed
|
37
|
-
#
|
38
|
-
# @param utils [Array] array with utilities to check
|
39
|
-
def check_util_availability(utils)
|
40
|
-
super(utils, 'remotely')
|
41
|
-
end
|
42
|
-
|
43
|
-
# Executes a command via ssh
|
44
|
-
#
|
45
|
-
# @param ssh [Net::SSH::Connection::Session] ssh session
|
46
|
-
# @param command [String] the command to execute
|
47
|
-
def ssh_exec!(ssh, command)
|
48
|
-
stdout_data = ""
|
49
|
-
stderr_data = ""
|
50
|
-
exit_code = nil
|
51
|
-
exit_signal = nil
|
52
|
-
ssh.open_channel do |channel|
|
53
|
-
channel.exec(command) do |ch, success|
|
54
|
-
unless success
|
55
|
-
abort "FAILED: couldn't execute command (ssh.channel.exec)"
|
56
|
-
end
|
57
|
-
channel.on_data do |ch,data|
|
58
|
-
stdout_data+=data
|
59
|
-
end
|
60
|
-
|
61
|
-
channel.on_extended_data do |ch,type,data|
|
62
|
-
stderr_data+=data
|
63
|
-
end
|
64
|
-
|
65
|
-
channel.on_request("exit-status") do |ch,data|
|
66
|
-
exit_code = data.read_long
|
67
|
-
end
|
68
|
-
|
69
|
-
channel.on_request("exit-signal") do |ch, data|
|
70
|
-
exit_signal = data.read_long
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
ssh.loop
|
75
|
-
|
76
|
-
result = Avocado::CommandExecutionResult.new
|
77
|
-
result.stdin = command
|
78
|
-
result.stdout = stdout_data
|
79
|
-
result.stderr = stderr_data
|
80
|
-
result.retval = exit_code
|
81
|
-
|
82
|
-
result
|
83
|
-
end
|
84
|
-
|
85
|
-
# Executes a command on the remote system
|
86
|
-
#
|
87
|
-
# @param cmd [String] the command to execute
|
88
|
-
# @return [CommandExecutionResult] result of the command exection
|
89
|
-
def command(cmd)
|
90
|
-
Avocado::Deployment.instance.log.info "Executing [" + cmd.yellow + "] on remote " + get(:ssh_host).cyan
|
91
|
-
|
92
|
-
result = Avocado::CommandExecutionResult.new
|
93
|
-
|
94
|
-
begin
|
95
|
-
result = ssh_exec!(@session, cmd)
|
96
|
-
|
97
|
-
if result.stdout.nil? == false && result.stdout.empty? == false
|
98
|
-
Avocado::Deployment.instance.log.debug "Stdout@#{get(:host)}: ".cyan + result.stdout.green
|
99
|
-
end
|
100
|
-
|
101
|
-
if result.stderr.nil? == false && result.stderr.empty? == false
|
102
|
-
Avocado::Deployment.instance.log.debug "Stderr@#{get(:host)}: ".cyan + result.stderr.red
|
103
|
-
end
|
104
|
-
rescue Exception => e
|
105
|
-
handle_abort e
|
106
|
-
end
|
107
|
-
|
108
|
-
result
|
109
|
-
end
|
110
|
-
|
111
|
-
end
|
112
|
-
end
|