mina-traackr 0.3.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.
- data/.gitignore +10 -0
- data/.rspec +1 -0
- data/.travis.yml +12 -0
- data/CONTRIBUTING.md +124 -0
- data/Gemfile +10 -0
- data/HISTORY.md +304 -0
- data/LICENSE +23 -0
- data/Makefile +29 -0
- data/Notes.md +72 -0
- data/Rakefile +20 -0
- data/Readme.md +1009 -0
- data/bin/mina +65 -0
- data/data/deploy.rb +74 -0
- data/data/deploy.sh.erb +120 -0
- data/lib/mina.rb +23 -0
- data/lib/mina/bundler.rb +44 -0
- data/lib/mina/chruby.rb +49 -0
- data/lib/mina/default.rb +144 -0
- data/lib/mina/deploy.rb +138 -0
- data/lib/mina/deploy_helpers.rb +34 -0
- data/lib/mina/exec_helpers.rb +104 -0
- data/lib/mina/foreman.rb +78 -0
- data/lib/mina/git.rb +62 -0
- data/lib/mina/helpers.rb +383 -0
- data/lib/mina/output_helpers.rb +92 -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 +122 -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 +40 -0
- data/spec/commands/outside_project_spec.rb +35 -0
- data/spec/commands/real_deploy_spec.rb +54 -0
- data/spec/commands/ssh_spec.rb +14 -0
- data/spec/commands/verbose_spec.rb +21 -0
- data/spec/dsl/invoke_spec.rb +33 -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 +55 -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/output_helper_spec.rb +38 -0
- data/spec/spec_helper.rb +21 -0
- data/support/Readme-footer.md +32 -0
- data/support/Readme-header.md +17 -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 +72 -0
- metadata +157 -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
|
+
echo "-----> 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
|
+
echo "-----> 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
|
+
echo "-----> 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
|
+
echo "-----> 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,104 @@
|
|
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
|
+
pid_err = Sys.stream_stderr!(e) { |str| print_stderr str }
|
26
|
+
pid_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
|
+
Process.waitpid pid_err
|
32
|
+
Process.kill 'TERM', pid_in
|
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 PID.
|
63
|
+
|
64
|
+
def stream_stderr!(err, &blk)
|
65
|
+
fork do
|
66
|
+
trap("INT") {}
|
67
|
+
|
68
|
+
while str = err.gets #[0]
|
69
|
+
next if str.include? "bash: no job control in this shell" #[1]
|
70
|
+
next if str.include? "stdin is not a terminal"
|
71
|
+
|
72
|
+
yield str.strip #[2]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# ### Sys.stream_stdin!
|
78
|
+
# __Internal:__ Read from the real stdin stream and pass it onto the given
|
79
|
+
# stdin stream `i`. Returns the PID.
|
80
|
+
|
81
|
+
def stream_stdin!(&blk)
|
82
|
+
fork do
|
83
|
+
trap("INT") {}
|
84
|
+
|
85
|
+
while (char = STDIN.getbyte rescue nil)
|
86
|
+
yield char if char
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# ### Sys.stream_stdout
|
92
|
+
# __Internal:__ Read from given stdout stream `o` and delegate it to the
|
93
|
+
# output helper.
|
94
|
+
|
95
|
+
def stream_stdout(o, &blk)
|
96
|
+
while str = o.getc
|
97
|
+
yield str
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
data/lib/mina/foreman.rb
ADDED
@@ -0,0 +1,78 @@
|
|
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
|
+
|
44
|
+
namespace :foreman do
|
45
|
+
desc 'Export the Procfile to Ubuntu upstart scripts'
|
46
|
+
task :export do
|
47
|
+
export_cmd = "sudo bundle exec foreman export upstart /etc/init -a #{foreman_app} -u #{foreman_user} -l #{foreman_log}"
|
48
|
+
|
49
|
+
queue %{
|
50
|
+
echo "-----> Exporting foreman procfile for #{foreman_app}"
|
51
|
+
#{echo_cmd %[cd #{deploy_to!}/#{current_path!} ; #{export_cmd}]}
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "Start the application services"
|
56
|
+
task :start do
|
57
|
+
queue %{
|
58
|
+
echo "-----> Starting #{foreman_app} services"
|
59
|
+
#{echo_cmd %[sudo start #{foreman_app}]}
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
desc "Stop the application services"
|
64
|
+
task :stop do
|
65
|
+
queue %{
|
66
|
+
echo "-----> Stopping #{foreman_app} services"
|
67
|
+
#{echo_cmd %[sudo stop #{foreman_app}]}
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
desc "Restart the application services"
|
72
|
+
task :restart do
|
73
|
+
queue %{
|
74
|
+
echo "-----> Restarting #{foreman_app} services"
|
75
|
+
#{echo_cmd %[sudo start #{foreman_app} || sudo restart #{foreman_app}]}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
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
|
+
echo "-----> 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
|
+
echo "-----> Cloning the Git repository"
|
42
|
+
#{echo_cmd %[git clone "#{repository!}" "#{deploy_to}/scm" --bare]}
|
43
|
+
else
|
44
|
+
echo "-----> Fetching new git commits"
|
45
|
+
#{echo_cmd %[(cd "#{deploy_to}/scm" && git fetch "#{repository!}" "#{branch}:#{branch}" --force)]}
|
46
|
+
fi &&
|
47
|
+
echo "-----> Using git branch '#{branch}'" &&
|
48
|
+
#{echo_cmd %[git clone "#{deploy_to}/scm" . --recursive --branch "#{branch}"]} &&
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
status = %[
|
53
|
+
echo "-----> 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,383 @@
|
|
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
|
+
Rake::Task[task].reenable if options[:reenable]
|
21
|
+
end
|
22
|
+
|
23
|
+
# ### erb
|
24
|
+
# Evaluates an ERB block in the current scope and returns a string.
|
25
|
+
#
|
26
|
+
# a = 1
|
27
|
+
# b = 2
|
28
|
+
#
|
29
|
+
# # Assuming foo.erb is <%= a %> and <%= b %>
|
30
|
+
# puts erb('foo.erb')
|
31
|
+
#
|
32
|
+
# #=> "1 and 2"
|
33
|
+
#
|
34
|
+
# Returns the output string of the ERB template.
|
35
|
+
|
36
|
+
def erb(file, b=binding)
|
37
|
+
require 'erb'
|
38
|
+
erb = ERB.new(File.read(file))
|
39
|
+
erb.result b
|
40
|
+
end
|
41
|
+
|
42
|
+
# ### run!
|
43
|
+
# SSHs into the host and runs the code that has been queued.
|
44
|
+
#
|
45
|
+
# This is already automatically invoked before Rake exits to run all
|
46
|
+
# commands that have been queued up.
|
47
|
+
#
|
48
|
+
# queue "sudo restart"
|
49
|
+
# run!
|
50
|
+
#
|
51
|
+
# Returns nothing.
|
52
|
+
|
53
|
+
def run!
|
54
|
+
report_time { ssh commands(:default) }
|
55
|
+
end
|
56
|
+
|
57
|
+
# ### report_time
|
58
|
+
# Report time elapsed in the block.
|
59
|
+
# Returns the output of the block.
|
60
|
+
#
|
61
|
+
# report_time do
|
62
|
+
# sleep 2
|
63
|
+
# # do other things
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# # Output:
|
67
|
+
# # Elapsed time: 2.00 seconds
|
68
|
+
|
69
|
+
def report_time(&blk)
|
70
|
+
time, output = measure &blk
|
71
|
+
print_str "Elapsed time: %.2f seconds" % [time]
|
72
|
+
output
|
73
|
+
end
|
74
|
+
|
75
|
+
# ### measure
|
76
|
+
# Measures the time (in seconds) a block takes.
|
77
|
+
# Returns a [time, output] tuple.
|
78
|
+
|
79
|
+
def measure(&blk)
|
80
|
+
t = Time.now
|
81
|
+
output = yield
|
82
|
+
[Time.now - t, output]
|
83
|
+
end
|
84
|
+
|
85
|
+
# ### mina_cleanup
|
86
|
+
# __Internal:__ Invoked when Rake exits.
|
87
|
+
#
|
88
|
+
# Returns nothing.
|
89
|
+
|
90
|
+
def mina_cleanup!
|
91
|
+
run! if commands.any?
|
92
|
+
end
|
93
|
+
|
94
|
+
# ## Errors
|
95
|
+
|
96
|
+
# ### die
|
97
|
+
# Exits with a nice looking message.
|
98
|
+
# Returns nothing.
|
99
|
+
#
|
100
|
+
# die 2
|
101
|
+
# die 2, "Tests failed"
|
102
|
+
|
103
|
+
def die(code=1, msg=null)
|
104
|
+
str = "Failed with status #{code}"
|
105
|
+
str += " (#{msg})" if msg
|
106
|
+
err = Failed.new(str)
|
107
|
+
err.exitstatus = code
|
108
|
+
raise err
|
109
|
+
end
|
110
|
+
|
111
|
+
# ### error
|
112
|
+
# __Internal:__ Prints to stdout.
|
113
|
+
# Consider using `print_error` instead.
|
114
|
+
|
115
|
+
def error(str)
|
116
|
+
$stderr.write "#{str}\n"
|
117
|
+
end
|
118
|
+
|
119
|
+
# ## Queueing
|
120
|
+
|
121
|
+
# ### queue
|
122
|
+
# Queues code to be ran.
|
123
|
+
#
|
124
|
+
# This queues code to be ran to the current code bucket (defaults to `:default`).
|
125
|
+
# To get the things that have been queued, use commands[:default]
|
126
|
+
#
|
127
|
+
# Returns nothing.
|
128
|
+
#
|
129
|
+
# queue "sudo restart"
|
130
|
+
# queue "true"
|
131
|
+
#
|
132
|
+
# commands == ['sudo restart', 'true']
|
133
|
+
|
134
|
+
def queue(code)
|
135
|
+
commands
|
136
|
+
commands(@to) << unindent(code)
|
137
|
+
end
|
138
|
+
|
139
|
+
# ### queue!
|
140
|
+
# Shortcut for `queue`ing a command that shows up in verbose mode.
|
141
|
+
|
142
|
+
def queue!(code)
|
143
|
+
queue echo_cmd(code)
|
144
|
+
end
|
145
|
+
|
146
|
+
# ### echo_cmd
|
147
|
+
# Converts a bash command to a command that echoes before execution.
|
148
|
+
# Used to show commands in verbose mode. This does nothing unless verbose mode is on.
|
149
|
+
#
|
150
|
+
# Returns a string of the compound bash command, typically in the format of
|
151
|
+
# `echo xx && xx`. However, if `verbose_mode?` is false, it returns the
|
152
|
+
# input string unharmed.
|
153
|
+
#
|
154
|
+
# echo_cmd("ln -nfs releases/2 current")
|
155
|
+
# #=> echo "$ ln -nfs releases/2 current" && ln -nfs releases/2 current
|
156
|
+
|
157
|
+
def echo_cmd(str)
|
158
|
+
if verbose_mode?
|
159
|
+
require 'shellwords'
|
160
|
+
"echo #{Shellwords.escape("$ " + str)} &&\n#{str}"
|
161
|
+
else
|
162
|
+
str
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# ## Commands
|
167
|
+
|
168
|
+
# ### commands
|
169
|
+
# Returns an array of queued code strings.
|
170
|
+
#
|
171
|
+
# You may give an optional `aspect`.
|
172
|
+
#
|
173
|
+
# Returns an array of strings.
|
174
|
+
#
|
175
|
+
# queue "sudo restart"
|
176
|
+
# queue "true"
|
177
|
+
#
|
178
|
+
# to :clean do
|
179
|
+
# queue "rm"
|
180
|
+
# end
|
181
|
+
#
|
182
|
+
# commands == ["sudo restart", "true"]
|
183
|
+
# commands(:clean) == ["rm"]
|
184
|
+
|
185
|
+
def commands(aspect=:default)
|
186
|
+
(@commands ||= begin
|
187
|
+
@to = :default
|
188
|
+
Hash.new { |h, k| h[k] = Array.new }
|
189
|
+
end)[aspect]
|
190
|
+
end
|
191
|
+
|
192
|
+
# ### isolate
|
193
|
+
# Starts a new block where new `commands` are collected.
|
194
|
+
#
|
195
|
+
# Returns nothing.
|
196
|
+
#
|
197
|
+
# queue "sudo restart"
|
198
|
+
# queue "true"
|
199
|
+
# commands.should == ['sudo restart', 'true']
|
200
|
+
#
|
201
|
+
# isolate do
|
202
|
+
# queue "reload"
|
203
|
+
# commands.should == ['reload']
|
204
|
+
# end
|
205
|
+
#
|
206
|
+
# commands.should == ['sudo restart', 'true']
|
207
|
+
|
208
|
+
def isolate(&blk)
|
209
|
+
old, @commands = @commands, nil
|
210
|
+
result = yield
|
211
|
+
new_code, @commands = @commands, old
|
212
|
+
result
|
213
|
+
end
|
214
|
+
|
215
|
+
# ### in_directory
|
216
|
+
# Starts a new block where #commands are collected, to be executed inside `path`.
|
217
|
+
#
|
218
|
+
# Returns nothing.
|
219
|
+
#
|
220
|
+
# in_directory './webapp' do
|
221
|
+
# queue "./reload"
|
222
|
+
# end
|
223
|
+
#
|
224
|
+
# commands.should == ['cd ./webapp && (./reload && true)']
|
225
|
+
|
226
|
+
def in_directory(path, &blk)
|
227
|
+
isolated_commands = isolate { yield; commands }
|
228
|
+
isolated_commands.each { |cmd| queue "(cd #{path} && (#{cmd}))" }
|
229
|
+
end
|
230
|
+
|
231
|
+
# ### to
|
232
|
+
# Defines instructions on how to do a certain thing.
|
233
|
+
# This makes the commands that are `queue`d go into a different bucket in commands.
|
234
|
+
#
|
235
|
+
# Returns nothing.
|
236
|
+
#
|
237
|
+
# to :prepare do
|
238
|
+
# run "bundle install"
|
239
|
+
# end
|
240
|
+
# to :launch do
|
241
|
+
# run "nginx -s restart"
|
242
|
+
# end
|
243
|
+
#
|
244
|
+
# commands(:prepare) == ["bundle install"]
|
245
|
+
# commands(:restart) == ["nginx -s restart"]
|
246
|
+
|
247
|
+
def to(name, &blk)
|
248
|
+
old, @to = @to, name
|
249
|
+
yield
|
250
|
+
ensure
|
251
|
+
@to = old
|
252
|
+
end
|
253
|
+
|
254
|
+
# ## Settings helpers
|
255
|
+
|
256
|
+
# ### set
|
257
|
+
# Sets settings.
|
258
|
+
# Sets given symbol `key` to value in `value`.
|
259
|
+
#
|
260
|
+
# Returns the value.
|
261
|
+
#
|
262
|
+
# set :domain, 'kickflip.me'
|
263
|
+
|
264
|
+
def set(key, value)
|
265
|
+
settings.send :"#{key}=", value
|
266
|
+
end
|
267
|
+
|
268
|
+
# ### set_default
|
269
|
+
# Sets default settings.
|
270
|
+
# Sets given symbol `key` to value in `value` only if the key isn't set yet.
|
271
|
+
#
|
272
|
+
# Returns the value.
|
273
|
+
#
|
274
|
+
# set_default :term_mode, :pretty
|
275
|
+
# set :term_mode, :system
|
276
|
+
# settings.term_mode.should == :system
|
277
|
+
#
|
278
|
+
# set :term_mode, :system
|
279
|
+
# set_default :term_mode, :pretty
|
280
|
+
# settings.term_mode.should == :system
|
281
|
+
|
282
|
+
def set_default(key, value)
|
283
|
+
settings.send :"#{key}=", value unless settings.send(:"#{key}?")
|
284
|
+
end
|
285
|
+
|
286
|
+
# ### settings
|
287
|
+
# Accesses the settings hash.
|
288
|
+
#
|
289
|
+
# set :domain, 'kickflip.me'
|
290
|
+
#
|
291
|
+
# settings.domain #=> 'kickflip.me'
|
292
|
+
# domain #=> 'kickflip.me'
|
293
|
+
|
294
|
+
def settings
|
295
|
+
@settings ||= Settings.new
|
296
|
+
end
|
297
|
+
|
298
|
+
# ### method_missing
|
299
|
+
# Hook to get settings.
|
300
|
+
# See #settings for an explanation.
|
301
|
+
#
|
302
|
+
# Returns things.
|
303
|
+
|
304
|
+
def method_missing(meth, *args, &blk)
|
305
|
+
settings.send meth, *args
|
306
|
+
end
|
307
|
+
|
308
|
+
# ## Command line mode helpers
|
309
|
+
|
310
|
+
# ### verbose_mode?
|
311
|
+
# Checks if Rake was invoked with --verbose.
|
312
|
+
#
|
313
|
+
# Returns true or false.
|
314
|
+
#
|
315
|
+
# if verbose_mode?
|
316
|
+
# queue %[echo "-----> Starting a new process"]
|
317
|
+
# end
|
318
|
+
|
319
|
+
def verbose_mode?
|
320
|
+
if Rake.respond_to?(:verbose)
|
321
|
+
#- Rake 0.9.x
|
322
|
+
Rake.verbose == true
|
323
|
+
else
|
324
|
+
#- Rake 0.8.x
|
325
|
+
RakeFileUtils.verbose_flag != :default
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
# ### simulate_mode?
|
330
|
+
# Checks if Rake was invoked with --simulate.
|
331
|
+
#
|
332
|
+
# Returns true or false.
|
333
|
+
|
334
|
+
def simulate_mode?
|
335
|
+
!! ENV['simulate']
|
336
|
+
end
|
337
|
+
|
338
|
+
# ## Internal helpers
|
339
|
+
|
340
|
+
# ### indent
|
341
|
+
# Indents a given code block with `count` spaces before it.
|
342
|
+
|
343
|
+
def indent(count, str)
|
344
|
+
str.gsub(/^/, " "*count)
|
345
|
+
end
|
346
|
+
|
347
|
+
# ### unindent
|
348
|
+
# __Internal:__ Normalizes indentation on a given string.
|
349
|
+
#
|
350
|
+
# Returns the normalized string without extraneous indentation.
|
351
|
+
#
|
352
|
+
# puts unindent %{
|
353
|
+
# Hello
|
354
|
+
# There
|
355
|
+
# }
|
356
|
+
# # Output:
|
357
|
+
# # Hello
|
358
|
+
# # There
|
359
|
+
|
360
|
+
def unindent(code)
|
361
|
+
if code =~ /^\n([ \t]+)/
|
362
|
+
code = code.gsub(/^#{$1}/, '')
|
363
|
+
end
|
364
|
+
|
365
|
+
code.strip
|
366
|
+
end
|
367
|
+
|
368
|
+
# ### reindent
|
369
|
+
# Resets the indentation on a given code block.
|
370
|
+
|
371
|
+
def reindent(n, code)
|
372
|
+
indent n, unindent(code)
|
373
|
+
end
|
374
|
+
|
375
|
+
# ### capture
|
376
|
+
# Returns the output of command via SSH.
|
377
|
+
|
378
|
+
def capture(cmd, options={})
|
379
|
+
ssh cmd, options.merge(:return => true)
|
380
|
+
end
|
381
|
+
|
382
|
+
end
|
383
|
+
end
|