colorful-mina 0.3.1

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