colorful-mina 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/.travis.yml +21 -0
- data/CONTRIBUTING.md +124 -0
- data/Gemfile +10 -0
- data/HISTORY.md +348 -0
- data/LICENSE +23 -0
- data/Makefile +29 -0
- data/Notes.md +70 -0
- data/README.md +1015 -0
- data/Rakefile +20 -0
- data/bin/mina +65 -0
- data/data/deploy.rb +80 -0
- data/data/deploy.sh.erb +106 -0
- data/lib/mina.rb +23 -0
- data/lib/mina/bundler.rb +49 -0
- data/lib/mina/chruby.rb +49 -0
- data/lib/mina/default.rb +145 -0
- data/lib/mina/deploy.rb +138 -0
- data/lib/mina/deploy_helpers.rb +34 -0
- data/lib/mina/exec_helpers.rb +111 -0
- data/lib/mina/foreman.rb +82 -0
- data/lib/mina/git.rb +62 -0
- data/lib/mina/helpers.rb +386 -0
- data/lib/mina/output_helpers.rb +95 -0
- data/lib/mina/rails.rb +206 -0
- data/lib/mina/rake.rb +9 -0
- data/lib/mina/rbenv.rb +47 -0
- data/lib/mina/rvm.rb +88 -0
- data/lib/mina/settings.rb +32 -0
- data/lib/mina/ssh_helpers.rb +123 -0
- data/lib/mina/tools.rb +20 -0
- data/lib/mina/version.rb +5 -0
- data/lib/mina/whenever.rb +27 -0
- data/manual/index.md +15 -0
- data/manual/modules.md +2 -0
- data/mina.gemspec +17 -0
- data/spec/command_helper.rb +52 -0
- data/spec/commands/cleanup_spec.rb +16 -0
- data/spec/commands/command_spec.rb +71 -0
- data/spec/commands/custom_config_spec.rb +20 -0
- data/spec/commands/deploy_spec.rb +36 -0
- data/spec/commands/outside_project_spec.rb +35 -0
- data/spec/commands/real_deploy_spec.rb +53 -0
- data/spec/commands/ssh_spec.rb +14 -0
- data/spec/commands/verbose_spec.rb +21 -0
- data/spec/dsl/invoke_spec.rb +48 -0
- data/spec/dsl/queue_spec.rb +49 -0
- data/spec/dsl/settings_in_rake_spec.rb +39 -0
- data/spec/dsl/settings_spec.rb +61 -0
- data/spec/dsl/to_spec.rb +20 -0
- data/spec/fixtures/custom_file_env/custom_deploy.rb +15 -0
- data/spec/fixtures/empty_env/config/deploy.rb +15 -0
- data/spec/helpers/exec_helper_spec.rb +19 -0
- data/spec/helpers/output_helper_spec.rb +24 -0
- data/spec/spec_helper.rb +27 -0
- data/support/Readme-footer.md +32 -0
- data/support/Readme-header.md +16 -0
- data/support/guide.md +297 -0
- data/support/index.html +53 -0
- data/support/to_md.rb +11 -0
- data/test_env/config/deploy.rb +69 -0
- metadata +150 -0
data/lib/mina/deploy.rb
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
# # Modules: Deployment
|
2
|
+
# This module is automatically loaded for all Mina projects.
|
3
|
+
|
4
|
+
# ## Settings
|
5
|
+
# Any and all of these settings can be overriden in your `deploy.rb`.
|
6
|
+
|
7
|
+
# ### releases_path
|
8
|
+
# (default: 'releases')
|
9
|
+
set_default :releases_path, "releases"
|
10
|
+
|
11
|
+
# ### shared_path
|
12
|
+
# (default: 'shared')
|
13
|
+
set_default :shared_path, "shared"
|
14
|
+
|
15
|
+
# ### current_path
|
16
|
+
# (default: 'current_path')
|
17
|
+
set_default :current_path, "current"
|
18
|
+
|
19
|
+
# ### lock_file
|
20
|
+
# Name of the file to generate while a deploy is currently ongoing.
|
21
|
+
# (default: 'deploy.lock')
|
22
|
+
set_default :lock_file, "deploy.lock"
|
23
|
+
|
24
|
+
# ### keep_releases
|
25
|
+
# Number of releases to keep when doing the `deploy:cleanup` task.
|
26
|
+
# (default: 5)
|
27
|
+
set_default :keep_releases, 5
|
28
|
+
|
29
|
+
namespace :deploy do
|
30
|
+
# ## Tasks
|
31
|
+
|
32
|
+
# ### deploy:force_unlock
|
33
|
+
# Forces a deploy unlock by deleting the lock file.
|
34
|
+
#
|
35
|
+
# $ mina deploy:force_unlock
|
36
|
+
#
|
37
|
+
# You can also combine that task with `deploy`:
|
38
|
+
#
|
39
|
+
# $ mina deploy:force_unlock deploy
|
40
|
+
|
41
|
+
desc "Forces a deploy unlock."
|
42
|
+
task :force_unlock do
|
43
|
+
queue %{echo "-----> Unlocking"}
|
44
|
+
queue echo_cmd %{rm -f "#{deploy_to}/#{lock_file}"}
|
45
|
+
end
|
46
|
+
|
47
|
+
# ### deploy:link_shared_paths
|
48
|
+
# Links the shared paths in the `shared_paths` setting.
|
49
|
+
|
50
|
+
desc "Links paths set in :shared_paths."
|
51
|
+
task :link_shared_paths do
|
52
|
+
dirs = settings.shared_paths!.map { |file| File.dirname("./#{file}") }.uniq
|
53
|
+
|
54
|
+
cmds = dirs.map do |dir|
|
55
|
+
echo_cmd %{mkdir -p "#{dir}"}
|
56
|
+
end
|
57
|
+
|
58
|
+
cmds += shared_paths.map do |file|
|
59
|
+
[
|
60
|
+
echo_cmd(%{rm -rf "./#{file}"}),
|
61
|
+
echo_cmd(%{ln -s "#{deploy_to}/#{shared_path}/#{file}" "./#{file}"})
|
62
|
+
]
|
63
|
+
end
|
64
|
+
|
65
|
+
queue %{
|
66
|
+
#{print_str '-> Symlinking shared paths'}
|
67
|
+
#{cmds.flatten.join(" &&\n")}
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
# ### deploy:cleanup
|
72
|
+
# Cleans up old releases.
|
73
|
+
#
|
74
|
+
# By default, the last 5 releases are kept on each server (though you can
|
75
|
+
# change this with the keep_releases setting). All other deployed revisions
|
76
|
+
# are removed from the servers."
|
77
|
+
|
78
|
+
desc "Clean up old releases."
|
79
|
+
task :cleanup do
|
80
|
+
queue %{
|
81
|
+
#{print_str "-> Cleaning up old releases (keeping " + keep_releases + "!})"}
|
82
|
+
#{echo_cmd %{cd "#{deploy_to!}/#{releases_path!}" || exit 15}}
|
83
|
+
#{echo_cmd %{count=`ls -1d [0-9]* | sort -rn | wc -l`}}
|
84
|
+
#{echo_cmd %{remove=$((count > #{keep_releases} ? count - #{keep_releases} : 0))}}
|
85
|
+
#{echo_cmd %{ls -1d [0-9]* | sort -rn | tail -n $remove | xargs rm -rf {}}}
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# ### setup
|
91
|
+
# Sets up a site's directory structure.
|
92
|
+
|
93
|
+
desc "Sets up a site."
|
94
|
+
task :setup do
|
95
|
+
set_default :term_mode, :pretty
|
96
|
+
|
97
|
+
settings.deploy_to!
|
98
|
+
|
99
|
+
user = settings.user? ? "#{settings.user}" : "username"
|
100
|
+
|
101
|
+
queue %{
|
102
|
+
#{print_str "-> Setting up #{deploy_to}"} && (
|
103
|
+
#{echo_cmd %{mkdir -p "#{deploy_to}"}} &&
|
104
|
+
#{echo_cmd %{chown -R `whoami` "#{deploy_to}"}} &&
|
105
|
+
#{echo_cmd %{chmod g+rx,u+rwx "#{deploy_to}"}} &&
|
106
|
+
#{echo_cmd %{cd "#{deploy_to}"}} &&
|
107
|
+
#{echo_cmd %{mkdir -p "#{releases_path}"}} &&
|
108
|
+
#{echo_cmd %{chmod g+rx,u+rwx "#{releases_path}"}} &&
|
109
|
+
#{echo_cmd %{mkdir -p "#{shared_path}"}} &&
|
110
|
+
#{echo_cmd %{chmod g+rx,u+rwx "#{shared_path}"}} &&
|
111
|
+
echo "" &&
|
112
|
+
#{echo_cmd %{ls -la "#{deploy_to}"}} &&
|
113
|
+
echo "" &&
|
114
|
+
#{print_str "-> Done."}
|
115
|
+
) || (
|
116
|
+
echo "! ERROR: Setup failed."
|
117
|
+
echo "! Ensure that the path '#{deploy_to}' is accessible to the SSH user."
|
118
|
+
echo "! Try doing:"
|
119
|
+
echo "! sudo mkdir -p \\"#{deploy_to}\\" && sudo chown -R #{user} \\"#{deploy_to}\\""
|
120
|
+
)
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
# ### run[]
|
125
|
+
# Runs a command on a server.
|
126
|
+
#
|
127
|
+
# $ mina run[tail -f logs.txt]
|
128
|
+
|
129
|
+
desc "Runs a command in the server."
|
130
|
+
task :run, [:command] => [:environment] do |t, args|
|
131
|
+
command = args[:command]
|
132
|
+
unless command
|
133
|
+
puts %[You need to provide a command. Try: mina "run[ls -la]"]
|
134
|
+
exit 1
|
135
|
+
end
|
136
|
+
|
137
|
+
queue %[cd #{deploy_to!} && #{command}]
|
138
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# # Helpers: Deploy helpers
|
2
|
+
# Helpers for deployment.
|
3
|
+
module Mina
|
4
|
+
module DeployHelpers
|
5
|
+
# ### deploy
|
6
|
+
# Wraps the things inside it in a deploy script and queues it.
|
7
|
+
# This generates a script using deploy_script and queues it.
|
8
|
+
#
|
9
|
+
# Returns nothing.
|
10
|
+
#
|
11
|
+
def deploy(&blk)
|
12
|
+
queue deploy_script(&blk)
|
13
|
+
end
|
14
|
+
|
15
|
+
# ### deploy_script
|
16
|
+
# Wraps the things inside it in a deploy script.
|
17
|
+
#
|
18
|
+
# script = deploy_script do
|
19
|
+
# invoke :'git:checkout'
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# queue script
|
23
|
+
#
|
24
|
+
# Returns the deploy script as a string, ready for `queue`ing.
|
25
|
+
#
|
26
|
+
def deploy_script(&blk)
|
27
|
+
set_default :term_mode, :pretty
|
28
|
+
code = isolate do
|
29
|
+
yield
|
30
|
+
erb Mina.root_path('data/deploy.sh.erb')
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# # Helpers: Exec helpers
|
2
|
+
# Provides `pretty_system` which Mina uses to parse SSH output, and delegate to
|
3
|
+
# the appropriate Output helper.
|
4
|
+
|
5
|
+
module Mina
|
6
|
+
module ExecHelpers
|
7
|
+
|
8
|
+
# ### pretty_system
|
9
|
+
# __Internal:__ A pretty version of the default `#system` commands, but
|
10
|
+
# indents and puts color.
|
11
|
+
#
|
12
|
+
# Returns the exit code in integer form.
|
13
|
+
#
|
14
|
+
def pretty_system(code)
|
15
|
+
require 'shellwords'
|
16
|
+
cmds = Shellwords.shellsplit(code)
|
17
|
+
coathooks = 0
|
18
|
+
|
19
|
+
status =
|
20
|
+
Tools.popen4(*cmds) do |pid, i, o, e|
|
21
|
+
# Handle `^C`.
|
22
|
+
trap("INT") { Sys.handle_sigint(coathooks += 1, pid, self) }
|
23
|
+
|
24
|
+
# __In the background,__ make stdin passthru, and stream stderr.
|
25
|
+
th_err = Sys.stream_stderr!(e) { |str| print_stderr str }
|
26
|
+
th_in = Sys.stream_stdin! { |chr| i.putc chr }
|
27
|
+
|
28
|
+
# __In the foreground,__ stream stdout to the output helper.
|
29
|
+
Sys.stream_stdout(o) { |ch| print_char ch }
|
30
|
+
|
31
|
+
th_err.join
|
32
|
+
th_in.terminate
|
33
|
+
end
|
34
|
+
|
35
|
+
status.exitstatus
|
36
|
+
end
|
37
|
+
|
38
|
+
# ## Private methods
|
39
|
+
# Delegate functions, mostly.
|
40
|
+
|
41
|
+
module Sys
|
42
|
+
|
43
|
+
extend self
|
44
|
+
|
45
|
+
# ### Sys.handle_sigint!
|
46
|
+
# Called when a `^C` is pressed. The param `count` is how many times it's
|
47
|
+
# been pressed since. Returns nothing.
|
48
|
+
|
49
|
+
def handle_sigint(count, pid, this)
|
50
|
+
puts ""
|
51
|
+
if count > 1
|
52
|
+
this.print_status "Mina: SIGINT received again. Force quitting..."
|
53
|
+
Process.kill "KILL", pid
|
54
|
+
else
|
55
|
+
this.print_status "Mina: SIGINT received."
|
56
|
+
Process.kill "TERM", pid
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# ### Sys.stream_stderr!
|
61
|
+
# __Internal:__ Read from stderr stream `err` *[0]*, supress expected
|
62
|
+
# errors *[1]*, and yield. Returns the thread.
|
63
|
+
|
64
|
+
def stream_stderr!(err, &blk)
|
65
|
+
Thread.new do
|
66
|
+
begin
|
67
|
+
while str = err.gets #[0]
|
68
|
+
next if str.include? "bash: no job control in this shell" #[1]
|
69
|
+
next if str.include? "stdin is not a terminal"
|
70
|
+
|
71
|
+
yield str.strip #[2]
|
72
|
+
end
|
73
|
+
rescue Interrupt
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# ### Sys.stream_stdin!
|
79
|
+
# __Internal:__ Read from the real stdin stream and pass it onto the given
|
80
|
+
# stdin stream `i`. Returns the thread.
|
81
|
+
|
82
|
+
def stream_stdin!(&blk)
|
83
|
+
Thread.new do
|
84
|
+
begin
|
85
|
+
while (char = STDIN.getbyte rescue nil)
|
86
|
+
yield char if char
|
87
|
+
end
|
88
|
+
rescue Interrupt
|
89
|
+
# rubinius
|
90
|
+
rescue SignalException
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# ### Sys.stream_stdout
|
96
|
+
# __Internal:__ Read from given stdout stream `o` and delegate it to the
|
97
|
+
# output helper.
|
98
|
+
|
99
|
+
def stream_stdout(o, &blk)
|
100
|
+
while str = o.getc
|
101
|
+
# Ruby 1.8.7 fix
|
102
|
+
str = str.chr if str.is_a? Fixnum
|
103
|
+
|
104
|
+
yield str
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
data/lib/mina/foreman.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# # Modules: Foreman
|
2
|
+
# Adds settings and tasks for managing projects with [foreman].
|
3
|
+
#
|
4
|
+
# NOTE: Requires sudo privileges
|
5
|
+
#
|
6
|
+
# [foreman]: http://rubygems.org/ddolar/foreman
|
7
|
+
#
|
8
|
+
# require 'mina/foreman'
|
9
|
+
#
|
10
|
+
# ## Common usage
|
11
|
+
#
|
12
|
+
# set :application, "app-name"
|
13
|
+
#
|
14
|
+
# task :deploy => :environment do
|
15
|
+
# deploy do
|
16
|
+
# # ...
|
17
|
+
# invoke 'foreman:export'
|
18
|
+
# # ...
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# to :launch do
|
22
|
+
# invoke 'foreman:restart'
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
|
27
|
+
# ## Settings
|
28
|
+
# Any and all of these settings can be overriden in your `deploy.rb`.
|
29
|
+
|
30
|
+
# ### foreman_app
|
31
|
+
# Sets the service name that foreman will export to upstart. Uses *application*
|
32
|
+
# variable as a default. It should be set, otherwise export command will fail.
|
33
|
+
|
34
|
+
# ### foreman_user
|
35
|
+
# Sets the user under which foreman will execute the service. Defaults to *user*
|
36
|
+
|
37
|
+
# ### foreman_log
|
38
|
+
# Sets the foreman log path. Defaults to *shared/log*
|
39
|
+
|
40
|
+
set_default :foreman_app, lambda { application }
|
41
|
+
set_default :foreman_user, lambda { user }
|
42
|
+
set_default :foreman_log, lambda { "#{deploy_to!}/#{shared_path}/log" }
|
43
|
+
set_default :foreman_sudo, true
|
44
|
+
set_default :foreman_format, 'upstart'
|
45
|
+
set_default :foreman_location, '/etc/init'
|
46
|
+
|
47
|
+
namespace :foreman do
|
48
|
+
desc 'Export the Procfile to Ubuntu upstart scripts'
|
49
|
+
task :export do
|
50
|
+
sudo_cmd = "sudo" if foreman_sudo
|
51
|
+
export_cmd = "#{sudo_cmd} bundle exec foreman export #{foreman_format} #{foreman_location} -a #{foreman_app} -u #{foreman_user} -d #{deploy_to!}/#{current_path!} -l #{foreman_log}"
|
52
|
+
|
53
|
+
queue %{
|
54
|
+
echo "-----> Exporting foreman procfile for #{foreman_app}"
|
55
|
+
#{echo_cmd %[cd #{deploy_to!}/#{current_path!} ; #{export_cmd}]}
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
desc "Start the application services"
|
60
|
+
task :start do
|
61
|
+
queue %{
|
62
|
+
echo "-----> Starting #{foreman_app} services"
|
63
|
+
#{echo_cmd %[sudo start #{foreman_app}]}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
desc "Stop the application services"
|
68
|
+
task :stop do
|
69
|
+
queue %{
|
70
|
+
echo "-----> Stopping #{foreman_app} services"
|
71
|
+
#{echo_cmd %[sudo stop #{foreman_app}]}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
desc "Restart the application services"
|
76
|
+
task :restart do
|
77
|
+
queue %{
|
78
|
+
echo "-----> Restarting #{foreman_app} services"
|
79
|
+
#{echo_cmd %[sudo start #{foreman_app} || sudo restart #{foreman_app}]}
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
data/lib/mina/git.rb
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# # Modules: Git
|
4
|
+
# Adds settings and tasks related to managing Git.
|
5
|
+
#
|
6
|
+
# require 'mina/git'
|
7
|
+
|
8
|
+
# ## Settings
|
9
|
+
# Any and all of these settings can be overriden in your `deploy.rb`.
|
10
|
+
|
11
|
+
# ### branch
|
12
|
+
# Sets the branch to be deployed.
|
13
|
+
|
14
|
+
set_default :branch, 'master'
|
15
|
+
|
16
|
+
namespace :git do
|
17
|
+
# ## Deploy tasks
|
18
|
+
# These tasks are meant to be invoked inside deploy scripts, not invoked on
|
19
|
+
# their own.
|
20
|
+
|
21
|
+
# ### git:clone
|
22
|
+
# Clones the Git repository. Meant to be used inside a deploy script.
|
23
|
+
|
24
|
+
desc "Clones the Git repository to the release path."
|
25
|
+
task :clone do
|
26
|
+
if revision?
|
27
|
+
error "The Git option `:revision` has now been deprecated."
|
28
|
+
error "Please use `:commit` or `:branch` instead."
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
|
32
|
+
clone = if commit?
|
33
|
+
%[
|
34
|
+
#{print_str "-> Using git commit '" + commit + "'"} &&
|
35
|
+
#{echo_cmd %[git clone "#{repository!}" . --recursive]} &&
|
36
|
+
#{echo_cmd %[git checkout -b current_release "#{commit}" --force]} &&
|
37
|
+
]
|
38
|
+
else
|
39
|
+
%{
|
40
|
+
if [ ! -d "#{deploy_to}/scm/objects" ]; then
|
41
|
+
#{print_str '-> Cloning the Git repository'}
|
42
|
+
#{echo_cmd %[git clone "#{repository!}" "#{deploy_to}/scm" --bare]}
|
43
|
+
else
|
44
|
+
#{print_str '-> Fetching new git commits'}
|
45
|
+
#{echo_cmd %[(cd "#{deploy_to}/scm" && git fetch "#{repository!}" "#{branch}:#{branch}" --force)]}
|
46
|
+
fi &&
|
47
|
+
#{print_str "-> Using git branch '" + branch + "'"} &&
|
48
|
+
#{echo_cmd %[git clone "#{deploy_to}/scm" . --recursive --branch "#{branch}"]} &&
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
status = %[
|
53
|
+
#{print_str '-> Using this git commit'} &&
|
54
|
+
echo &&
|
55
|
+
#{echo_cmd %[git --no-pager log --format='%aN (%h):%n> %s' -n 1]} &&
|
56
|
+
#{echo_cmd %[rm -rf .git]} &&
|
57
|
+
echo
|
58
|
+
]
|
59
|
+
|
60
|
+
queue clone + status
|
61
|
+
end
|
62
|
+
end
|
data/lib/mina/helpers.rb
ADDED
@@ -0,0 +1,386 @@
|
|
1
|
+
# # Helpers
|
2
|
+
|
3
|
+
module Mina
|
4
|
+
module Helpers
|
5
|
+
|
6
|
+
# ### invoke
|
7
|
+
# Invokes another Rake task.
|
8
|
+
#
|
9
|
+
# Invokes the task given in `task`. Returns nothing.
|
10
|
+
#
|
11
|
+
# invoke :'git:clone'
|
12
|
+
# invoke :restart
|
13
|
+
#
|
14
|
+
# Options:
|
15
|
+
# reenable (bool) - Execute the task even next time.
|
16
|
+
#
|
17
|
+
|
18
|
+
def invoke(task, options = {})
|
19
|
+
Rake.application.invoke_task task
|
20
|
+
if options[:reenable]
|
21
|
+
name = Rake.application.parse_task_string(task).first
|
22
|
+
Rake::Task[name].reenable
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# ### erb
|
27
|
+
# Evaluates an ERB block in the current scope and returns a string.
|
28
|
+
#
|
29
|
+
# a = 1
|
30
|
+
# b = 2
|
31
|
+
#
|
32
|
+
# # Assuming foo.erb is <%= a %> and <%= b %>
|
33
|
+
# puts erb('foo.erb')
|
34
|
+
#
|
35
|
+
# #=> "1 and 2"
|
36
|
+
#
|
37
|
+
# Returns the output string of the ERB template.
|
38
|
+
|
39
|
+
def erb(file, b=binding)
|
40
|
+
require 'erb'
|
41
|
+
erb = ERB.new(File.read(file))
|
42
|
+
erb.result b
|
43
|
+
end
|
44
|
+
|
45
|
+
# ### run!
|
46
|
+
# SSHs into the host and runs the code that has been queued.
|
47
|
+
#
|
48
|
+
# This is already automatically invoked before Rake exits to run all
|
49
|
+
# commands that have been queued up.
|
50
|
+
#
|
51
|
+
# queue "sudo restart"
|
52
|
+
# run!
|
53
|
+
#
|
54
|
+
# Returns nothing.
|
55
|
+
|
56
|
+
def run!
|
57
|
+
report_time { ssh commands(:default) }
|
58
|
+
end
|
59
|
+
|
60
|
+
# ### report_time
|
61
|
+
# Report time elapsed in the block.
|
62
|
+
# Returns the output of the block.
|
63
|
+
#
|
64
|
+
# report_time do
|
65
|
+
# sleep 2
|
66
|
+
# # do other things
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# # Output:
|
70
|
+
# # Elapsed time: 2.00 seconds
|
71
|
+
|
72
|
+
def report_time(&blk)
|
73
|
+
time, output = measure &blk
|
74
|
+
print_str "Elapsed time: %.2f seconds" % [time]
|
75
|
+
output
|
76
|
+
end
|
77
|
+
|
78
|
+
# ### measure
|
79
|
+
# Measures the time (in seconds) a block takes.
|
80
|
+
# Returns a [time, output] tuple.
|
81
|
+
|
82
|
+
def measure(&blk)
|
83
|
+
t = Time.now
|
84
|
+
output = yield
|
85
|
+
[Time.now - t, output]
|
86
|
+
end
|
87
|
+
|
88
|
+
# ### mina_cleanup
|
89
|
+
# __Internal:__ Invoked when Rake exits.
|
90
|
+
#
|
91
|
+
# Returns nothing.
|
92
|
+
|
93
|
+
def mina_cleanup!
|
94
|
+
run! if commands.any?
|
95
|
+
end
|
96
|
+
|
97
|
+
# ## Errors
|
98
|
+
|
99
|
+
# ### die
|
100
|
+
# Exits with a nice looking message.
|
101
|
+
# Returns nothing.
|
102
|
+
#
|
103
|
+
# die 2
|
104
|
+
# die 2, "Tests failed"
|
105
|
+
|
106
|
+
def die(code=1, msg=null)
|
107
|
+
str = "Failed with status #{code}"
|
108
|
+
str += " (#{msg})" if msg
|
109
|
+
err = Failed.new(str)
|
110
|
+
err.exitstatus = code
|
111
|
+
raise err
|
112
|
+
end
|
113
|
+
|
114
|
+
# ### error
|
115
|
+
# __Internal:__ Prints to stdout.
|
116
|
+
# Consider using `print_error` instead.
|
117
|
+
|
118
|
+
def error(str)
|
119
|
+
$stderr.write "#{str}\n"
|
120
|
+
end
|
121
|
+
|
122
|
+
# ## Queueing
|
123
|
+
|
124
|
+
# ### queue
|
125
|
+
# Queues code to be run.
|
126
|
+
#
|
127
|
+
# This queues code to be run to the current code bucket (defaults to `:default`).
|
128
|
+
# To get the things that have been queued, use commands[:default]
|
129
|
+
#
|
130
|
+
# Returns nothing.
|
131
|
+
#
|
132
|
+
# queue "sudo restart"
|
133
|
+
# queue "true"
|
134
|
+
#
|
135
|
+
# commands == ['sudo restart', 'true']
|
136
|
+
|
137
|
+
def queue(code)
|
138
|
+
commands
|
139
|
+
commands(@to) << unindent(code)
|
140
|
+
end
|
141
|
+
|
142
|
+
# ### queue!
|
143
|
+
# Shortcut for `queue`ing a command that shows up in verbose mode.
|
144
|
+
|
145
|
+
def queue!(code)
|
146
|
+
queue echo_cmd(code)
|
147
|
+
end
|
148
|
+
|
149
|
+
# ### echo_cmd
|
150
|
+
# Converts a bash command to a command that echoes before execution.
|
151
|
+
# Used to show commands in verbose mode. This does nothing unless verbose mode is on.
|
152
|
+
#
|
153
|
+
# Returns a string of the compound bash command, typically in the format of
|
154
|
+
# `echo xx && xx`. However, if `verbose_mode?` is false, it returns the
|
155
|
+
# input string unharmed.
|
156
|
+
#
|
157
|
+
# echo_cmd("ln -nfs releases/2 current")
|
158
|
+
# #=> echo "$ ln -nfs releases/2 current" && ln -nfs releases/2 current
|
159
|
+
|
160
|
+
def echo_cmd(str)
|
161
|
+
if verbose_mode?
|
162
|
+
require 'shellwords'
|
163
|
+
"echo #{Shellwords.escape("$ " + str)} &&\n#{str}"
|
164
|
+
else
|
165
|
+
str
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# ## Commands
|
170
|
+
|
171
|
+
# ### commands
|
172
|
+
# Returns an array of queued code strings.
|
173
|
+
#
|
174
|
+
# You may give an optional `aspect`.
|
175
|
+
#
|
176
|
+
# Returns an array of strings.
|
177
|
+
#
|
178
|
+
# queue "sudo restart"
|
179
|
+
# queue "true"
|
180
|
+
#
|
181
|
+
# to :clean do
|
182
|
+
# queue "rm"
|
183
|
+
# end
|
184
|
+
#
|
185
|
+
# commands == ["sudo restart", "true"]
|
186
|
+
# commands(:clean) == ["rm"]
|
187
|
+
|
188
|
+
def commands(aspect=:default)
|
189
|
+
(@commands ||= begin
|
190
|
+
@to = :default
|
191
|
+
Hash.new { |h, k| h[k] = Array.new }
|
192
|
+
end)[aspect]
|
193
|
+
end
|
194
|
+
|
195
|
+
# ### isolate
|
196
|
+
# Starts a new block where new `commands` are collected.
|
197
|
+
#
|
198
|
+
# Returns nothing.
|
199
|
+
#
|
200
|
+
# queue "sudo restart"
|
201
|
+
# queue "true"
|
202
|
+
# commands.should == ['sudo restart', 'true']
|
203
|
+
#
|
204
|
+
# isolate do
|
205
|
+
# queue "reload"
|
206
|
+
# commands.should == ['reload']
|
207
|
+
# end
|
208
|
+
#
|
209
|
+
# commands.should == ['sudo restart', 'true']
|
210
|
+
|
211
|
+
def isolate(&blk)
|
212
|
+
old, @commands = @commands, nil
|
213
|
+
result = yield
|
214
|
+
new_code, @commands = @commands, old
|
215
|
+
result
|
216
|
+
end
|
217
|
+
|
218
|
+
# ### in_directory
|
219
|
+
# Starts a new block where #commands are collected, to be executed inside `path`.
|
220
|
+
#
|
221
|
+
# Returns nothing.
|
222
|
+
#
|
223
|
+
# in_directory './webapp' do
|
224
|
+
# queue "./reload"
|
225
|
+
# end
|
226
|
+
#
|
227
|
+
# commands.should == ['cd ./webapp && (./reload && true)']
|
228
|
+
|
229
|
+
def in_directory(path, &blk)
|
230
|
+
isolated_commands = isolate { yield; commands }
|
231
|
+
isolated_commands.each { |cmd| queue "(cd #{path} && (#{cmd}))" }
|
232
|
+
end
|
233
|
+
|
234
|
+
# ### to
|
235
|
+
# Defines instructions on how to do a certain thing.
|
236
|
+
# This makes the commands that are `queue`d go into a different bucket in commands.
|
237
|
+
#
|
238
|
+
# Returns nothing.
|
239
|
+
#
|
240
|
+
# to :prepare do
|
241
|
+
# run "bundle install"
|
242
|
+
# end
|
243
|
+
# to :launch do
|
244
|
+
# run "nginx -s restart"
|
245
|
+
# end
|
246
|
+
#
|
247
|
+
# commands(:prepare) == ["bundle install"]
|
248
|
+
# commands(:restart) == ["nginx -s restart"]
|
249
|
+
|
250
|
+
def to(name, &blk)
|
251
|
+
old, @to = @to, name
|
252
|
+
yield
|
253
|
+
ensure
|
254
|
+
@to = old
|
255
|
+
end
|
256
|
+
|
257
|
+
# ## Settings helpers
|
258
|
+
|
259
|
+
# ### set
|
260
|
+
# Sets settings.
|
261
|
+
# Sets given symbol `key` to value in `value`.
|
262
|
+
#
|
263
|
+
# Returns the value.
|
264
|
+
#
|
265
|
+
# set :domain, 'kickflip.me'
|
266
|
+
|
267
|
+
def set(key, value)
|
268
|
+
settings.send :"#{key}=", value
|
269
|
+
end
|
270
|
+
|
271
|
+
# ### set_default
|
272
|
+
# Sets default settings.
|
273
|
+
# Sets given symbol `key` to value in `value` only if the key isn't set yet.
|
274
|
+
#
|
275
|
+
# Returns the value.
|
276
|
+
#
|
277
|
+
# set_default :term_mode, :pretty
|
278
|
+
# set :term_mode, :system
|
279
|
+
# settings.term_mode.should == :system
|
280
|
+
#
|
281
|
+
# set :term_mode, :system
|
282
|
+
# set_default :term_mode, :pretty
|
283
|
+
# settings.term_mode.should == :system
|
284
|
+
|
285
|
+
def set_default(key, value)
|
286
|
+
settings.send :"#{key}=", value unless settings.send(:"#{key}?")
|
287
|
+
end
|
288
|
+
|
289
|
+
# ### settings
|
290
|
+
# Accesses the settings hash.
|
291
|
+
#
|
292
|
+
# set :domain, 'kickflip.me'
|
293
|
+
#
|
294
|
+
# settings.domain #=> 'kickflip.me'
|
295
|
+
# domain #=> 'kickflip.me'
|
296
|
+
|
297
|
+
def settings
|
298
|
+
@settings ||= Settings.new
|
299
|
+
end
|
300
|
+
|
301
|
+
# ### method_missing
|
302
|
+
# Hook to get settings.
|
303
|
+
# See #settings for an explanation.
|
304
|
+
#
|
305
|
+
# Returns things.
|
306
|
+
|
307
|
+
def method_missing(meth, *args, &blk)
|
308
|
+
settings.send meth, *args
|
309
|
+
end
|
310
|
+
|
311
|
+
# ## Command line mode helpers
|
312
|
+
|
313
|
+
# ### verbose_mode?
|
314
|
+
# Checks if Rake was invoked with --verbose.
|
315
|
+
#
|
316
|
+
# Returns true or false.
|
317
|
+
#
|
318
|
+
# if verbose_mode?
|
319
|
+
# queue %[echo "-----> Starting a new process"]
|
320
|
+
# end
|
321
|
+
|
322
|
+
def verbose_mode?
|
323
|
+
if Rake.respond_to?(:verbose)
|
324
|
+
#- Rake 0.9.x
|
325
|
+
Rake.verbose == true
|
326
|
+
else
|
327
|
+
#- Rake 0.8.x
|
328
|
+
RakeFileUtils.verbose_flag != :default
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
# ### simulate_mode?
|
333
|
+
# Checks if Rake was invoked with --simulate.
|
334
|
+
#
|
335
|
+
# Returns true or false.
|
336
|
+
|
337
|
+
def simulate_mode?
|
338
|
+
!! ENV['simulate']
|
339
|
+
end
|
340
|
+
|
341
|
+
# ## Internal helpers
|
342
|
+
|
343
|
+
# ### indent
|
344
|
+
# Indents a given code block with `count` spaces before it.
|
345
|
+
|
346
|
+
def indent(count, str)
|
347
|
+
str.gsub(/^/, " "*count)
|
348
|
+
end
|
349
|
+
|
350
|
+
# ### unindent
|
351
|
+
# __Internal:__ Normalizes indentation on a given string.
|
352
|
+
#
|
353
|
+
# Returns the normalized string without extraneous indentation.
|
354
|
+
#
|
355
|
+
# puts unindent %{
|
356
|
+
# Hello
|
357
|
+
# There
|
358
|
+
# }
|
359
|
+
# # Output:
|
360
|
+
# # Hello
|
361
|
+
# # There
|
362
|
+
|
363
|
+
def unindent(code)
|
364
|
+
if code =~ /^\n([ \t]+)/
|
365
|
+
code = code.gsub(/^#{$1}/, '')
|
366
|
+
end
|
367
|
+
|
368
|
+
code.strip
|
369
|
+
end
|
370
|
+
|
371
|
+
# ### reindent
|
372
|
+
# Resets the indentation on a given code block.
|
373
|
+
|
374
|
+
def reindent(n, code)
|
375
|
+
indent n, unindent(code)
|
376
|
+
end
|
377
|
+
|
378
|
+
# ### capture
|
379
|
+
# Returns the output of command via SSH.
|
380
|
+
|
381
|
+
def capture(cmd, options={})
|
382
|
+
ssh cmd, options.merge(:return => true)
|
383
|
+
end
|
384
|
+
|
385
|
+
end
|
386
|
+
end
|