有些地方理解的还不是很到位,还请大家指点.
mina 是一个快速部署工具。原理:
Mina::Application
类
主要是初始化 mina,执行队列中的命令.
主要方法 run
Mina::Backend::Local
和 Mina::Backend::Remote
类
一个是在当前机器执行命令,一个是在远程服务器执行命令。
关键方法:ssh
Mina::Helpers::Internal
模块
主要是对命令进行一些字符串方面的处理,记录部署花费时间等.<br>
Mina::Helpers::Output
模块
主要是输出的操作,用不同的颜色表示不同信息的输出。
Mina::Runner::Exec
Mina::Runner::Pretty
Mina::Runner::Printer
Mina::Runner::System
这几个类实现了 ruby 调用 shell 的几种形式。
Mina::Commands
类
定义了对命令的一些基本操作.
例如:
方法:command
将命令加入队列
方法:process
将命令的队列转换成字符串
方法:run
执行命令
Mina::Configuration
类
定义了 mina 中最基本的方法
例如:
set
fetch
Mina::DSL
模块
定义了一些关键的,会直接使用到的方法
例如:
方法:invoke
让任务可以被调用
方法:in_path
在某个目录下运行命令
方法:run
执行某个命令
Mina::Runner
类
主要是命令的最后执行.
例子:
mina run["ls -l"]
mina 任务源码(tasks/mina/default.rb
)
接下来分析下 mina 运行这个命令的流程
mina 定义了运行命令的任务
desc 'Runs a command in the server.'
task :run, [:command] do |_, args|
ensure!(:deploy_to)
command = args[:command]
unless command
puts "You need to provide a command. Try: mina 'run[ls -la]'"
exit 1
end
in_path fetch(:deploy_to) do
command command
end
end
所有的 mina
任务的运行都要先执行文件 bin/mina
#!/usr/bin/env ruby
require 'mina'
Mina::Application.new.run
初始化 mina,同时开始执行命令
我们来看这个过程都发生了那些操作,关键点:
def run
Rake.application = self
super
end
top_level_tasks
调用。其中很关键的是: :run_commands
.这个会导致对 run_commands 这个任务的执行(稍后会提到)之后会回到任务mina run["ls -l"]
之中
ensure!(:deploy_to)
command = args[:command]
确认部署目录有值,获取参数
核心来了
in_path fetch(:deploy_to) do
command command
end
我们来看 in_path 方法:
def in_path(path, indent: nil)
real_commands = commands
@commands = Commands.new
yield
real_commands.command(commands.process(path), quiet: true, indent: indent)
@commands = real_commands
end
yield
中的操作也就是command command
这是将命令ls -l
放入队列之中.
def initialize(stage = :default)
@stage = stage
@queue = Hash.new { |hash, key| hash[key] = [] }
end
def command(code, strip: true, quiet: false, indent: nil)
code = unindent(code) if strip
code = indent(indent, code) if indent
queue[stage] << (quiet ? code : echo_cmd(code))
end
对命令进行一些简单的处理,然后加入队列queue[:default]
中.
类似结果:
#<Mina::Commands:0x0000000291a5d0 @stage=:default, @queue={:default=>["ls -l"]}>
将命令进行一些处理再次放入队列之中
def process(path = nil)
if path
queue[stage].unshift(%{echo "$ cd #{path}"}) if fetch(:verbose)
%{(cd #{path} && #{queue[stage].join(' && ')} && cd -)}
else
queue[stage].join("\n")
end
end
类似结果:
> #<Mina::Commands:0x0000000291a698 @stage=:default, @queue={:default=>["(cd /home/zhang/dongfeng && ls -l && cd -)"]}>
接下来会执行任务 run_commands
,这个应该还是Mina::Application
的run
方法中的super
的因素。具体原因还不清楚.
task :environment do
end
task :run_commands do
if commands.run_default?
invoke :environment
commands.run(:remote)
end
end
当 stage == :default 且命令队列不为空的时候才执行.
会调用任务 mina environment,默认为空,但是项目中的配置文件一般会覆写这个任务用来加载ruby
环境,例如rvm
等.
快到最后时刻了
def report_time
time_start = Time.now
output = yield
print_info "Elapsed time: %.2f seconds" % [Time.now - time_start]
output
end
定义了计算时间的方法.
def run(backend)
return if queue.empty?
report_time do
status = Mina::Runner.new(process, backend).run
error! 'Run Error' unless status
end
end
命令的执行即将开始. 关键参数类似结果:
process : (cd /home/zhang/dongfeng && ls -l && cd -) backend: remote
Mina::Runner
的关键方法:
def run
Mina::Runner.const_get(class_name_for(execution_mode)).new(script).run
end
private
def script
Mina::Backend.const_get(class_name_for(backend)).new(commands).prepare
end
Mina::Runner.const_get(class_name_for(execution_mode))
主要就是选用那种执行方式:
Mina::Runner::Exec
Mina::Runner::Pretty
Mina::Runner::Printer
Mina::Runner::System
而 script 则确定在远程还是在本地执行,此处是在远程执行.
def prepare
if fetch(:simulate)
[
'#!/usr/bin/env bash', "# Executing the following via '#{ssh}':",
'#', commands, ' '
].join("\n")
else
command = Shellwords.escape(commands)
w = [ssh, '--', command].join(' ')
end
end
def ssh
ensure!(:domain)
args = fetch(:domain)
args = "#{fetch(:user)}@#{fetch(:domain)}" if set?(:user)
args += " -i #{fetch(:identity_file)}" if set?(:identity_file)
args += " -p #{fetch(:port)}" if set?(:port)
args += ' -A' if set?(:forward_agent)
args += " #{fetch(:ssh_options)}" if set?(:ssh_options)
args += ' -tt'
"ssh #{args}"
end
prepare
会对命令进行一些处理
类似结果:
(cd\ /home/zhang/dongfeng\ &&\ ls\ -l\ &&\ cd\ -)
然后把 ssh 远程登录的命令加入到所有命令的最开始处. 类似结果:
ssh user_name@***.xyz -p 22 -tt -- (cd\ /home/zhang/dongfeng\ &&\ ls\ -l\ &&\ cd\ -)
此时就开始登录远程服务器执行命令了,静待即可.