mina-traackr 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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