colorful-mina 0.3.1

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