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.
Files changed (62) hide show
  1. data/.gitignore +10 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +12 -0
  4. data/CONTRIBUTING.md +124 -0
  5. data/Gemfile +10 -0
  6. data/HISTORY.md +304 -0
  7. data/LICENSE +23 -0
  8. data/Makefile +29 -0
  9. data/Notes.md +72 -0
  10. data/Rakefile +20 -0
  11. data/Readme.md +1009 -0
  12. data/bin/mina +65 -0
  13. data/data/deploy.rb +74 -0
  14. data/data/deploy.sh.erb +120 -0
  15. data/lib/mina.rb +23 -0
  16. data/lib/mina/bundler.rb +44 -0
  17. data/lib/mina/chruby.rb +49 -0
  18. data/lib/mina/default.rb +144 -0
  19. data/lib/mina/deploy.rb +138 -0
  20. data/lib/mina/deploy_helpers.rb +34 -0
  21. data/lib/mina/exec_helpers.rb +104 -0
  22. data/lib/mina/foreman.rb +78 -0
  23. data/lib/mina/git.rb +62 -0
  24. data/lib/mina/helpers.rb +383 -0
  25. data/lib/mina/output_helpers.rb +92 -0
  26. data/lib/mina/rails.rb +206 -0
  27. data/lib/mina/rake.rb +9 -0
  28. data/lib/mina/rbenv.rb +47 -0
  29. data/lib/mina/rvm.rb +88 -0
  30. data/lib/mina/settings.rb +32 -0
  31. data/lib/mina/ssh_helpers.rb +122 -0
  32. data/lib/mina/tools.rb +20 -0
  33. data/lib/mina/version.rb +5 -0
  34. data/lib/mina/whenever.rb +27 -0
  35. data/manual/index.md +15 -0
  36. data/manual/modules.md +2 -0
  37. data/mina.gemspec +17 -0
  38. data/spec/command_helper.rb +52 -0
  39. data/spec/commands/cleanup_spec.rb +16 -0
  40. data/spec/commands/command_spec.rb +71 -0
  41. data/spec/commands/custom_config_spec.rb +20 -0
  42. data/spec/commands/deploy_spec.rb +40 -0
  43. data/spec/commands/outside_project_spec.rb +35 -0
  44. data/spec/commands/real_deploy_spec.rb +54 -0
  45. data/spec/commands/ssh_spec.rb +14 -0
  46. data/spec/commands/verbose_spec.rb +21 -0
  47. data/spec/dsl/invoke_spec.rb +33 -0
  48. data/spec/dsl/queue_spec.rb +49 -0
  49. data/spec/dsl/settings_in_rake_spec.rb +39 -0
  50. data/spec/dsl/settings_spec.rb +55 -0
  51. data/spec/dsl/to_spec.rb +20 -0
  52. data/spec/fixtures/custom_file_env/custom_deploy.rb +15 -0
  53. data/spec/fixtures/empty_env/config/deploy.rb +15 -0
  54. data/spec/helpers/output_helper_spec.rb +38 -0
  55. data/spec/spec_helper.rb +21 -0
  56. data/support/Readme-footer.md +32 -0
  57. data/support/Readme-header.md +17 -0
  58. data/support/guide.md +297 -0
  59. data/support/index.html +53 -0
  60. data/support/to_md.rb +11 -0
  61. data/test_env/config/deploy.rb +72 -0
  62. metadata +157 -0
@@ -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
@@ -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
@@ -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
@@ -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