kstrano 1.0.12 → 1.1.0.alpha.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.
data/bin/kumafy CHANGED
@@ -9,86 +9,108 @@ require 'uri'
9
9
 
10
10
  HighLine.track_eof = false
11
11
 
12
- DEPLOY_GIST = "https://raw.github.com/Kunstmaan/kStrano/master/config/deploy.rb"
13
- PRODUCTION_GIST = "https://raw.github.com/Kunstmaan/kStrano/master/config/production.rb"
14
- STAGING_GIST = "https://raw.github.com/Kunstmaan/kStrano/master/config/staging.rb"
15
-
16
- BUILD_GIST = "https://raw.github.com/Kunstmaan/kStrano/master/config/build.xml"
17
- PHPCS_GIST = "https://raw.github.com/Kunstmaan/kStrano/master/config/phpcs.xml"
18
- PHPMD_GIST = "https://raw.github.com/Kunstmaan/kStrano/master/config/phpmd.xml"
19
- PHPDOX_GIST = "https://raw.github.com/Kunstmaan/kStrano/master/config/phpdox.xml"
20
- PHPUNIT_GIST = "https://raw.github.com/Kunstmaan/kStrano/master/config/phpunit.xml.dist"
21
- APPTEST_GIST = "https://raw.github.com/Kunstmaan/kStrano/master/config/app_test.php"
22
- BEHAT_GIST = "https://raw.github.com/Kunstmaan/kStrano/master/config/behat.yml-dist"
23
-
24
- def update_capfile(base, context, force)
12
+ BASE_PATH = File.realpath(File.dirname(Gem.find_files('kstrano.rb').last.to_s) + '/../') # the directory of this GEM
13
+ RESOURCES_BASE_PATH = "#{BASE_PATH}/resources" # the directory of the resources folder
14
+ RECIPES = {
15
+ "symfony2" => {
16
+ "config_path" => "app/config"
17
+ },
18
+ "play" => {
19
+ "config_path" => "conf"
20
+ },
21
+ "drupal" => {
22
+ "config_path" => "app/config"
23
+ },
24
+ "magento" => {
25
+ "config_path" => "app/config"
26
+ }
27
+ } # all the supported recipes
28
+
29
+ def update_capfile(ui, base, context, force)
25
30
  file = File.join(base, "Capfile")
26
31
 
27
- if !File.exists?("Capfile")
28
- abort "Make sure the project has been capified or capifonied."
29
- else
30
- includestr = "load Gem.find_files('kstrano.rb').last.to_s"
31
- fcontent = ""
32
+ if File.exists?(file)
33
+ warn "[skip] #{file} already exists"
34
+
35
+ content = ""
32
36
  File.open(file, "r") do |f|
33
37
  f.each do |line|
34
- fcontent << line
35
- if line.include? includestr
36
- abort "This project is already kumafied!" unless force
37
- return
38
- end
38
+ content << line
39
39
  end
40
40
  end
41
41
 
42
- File.open(file, "w") do |f|
43
- fcontent.each_line do |line|
44
- if line.include? "load 'app/config/deploy'"
45
- f.print includestr + "\r\n"
46
- end
47
- f.print line
48
- end
42
+ context["recipe"] = content.match("kstrano_(#{RECIPES.keys.join('|')})")[1]
43
+ else
44
+ context["recipe"] ||= ui.ask("What type of application is it? (#{RECIPES.keys.join(', ')})").downcase
45
+
46
+ if !RECIPES.include? context['recipe']
47
+ abort "No such recipe (#{context['recipe']}"
49
48
  end
49
+
50
+ recipe = context["recipe"]
51
+ content = unindent(<<-FILE)
52
+ load 'deploy' if respond_to?(:namespace) # cap2 differentiator
53
+
54
+ require 'kstrano_#{recipe}'
55
+ load '#{RECIPES[recipe]['config_path']}/deploy'
56
+ FILE
57
+
58
+ File.open(file, "w") { |f| f.write(content)}
50
59
  end
51
60
  end
52
61
 
53
62
  def update_deploy_config(ui, base, context, force)
54
-
55
- deploy = File.join(base, "app", "config", "deploy.rb")
63
+
64
+ deploy = File.join(base, RECIPES[context['recipe']]['config_path'], "deploy.rb")
56
65
  write_deploy = true
57
-
66
+
58
67
  if write_deploy
59
- dirname = File.basename(Dir.getwd)
60
- deploy_gist = get_plain_secure(DEPLOY_GIST).body
68
+ deploy_config_file = read_resource_file("deploy.rb")
61
69
  context["app_name"] ||= ui.ask("What's the name of the application?")
62
70
  app_name = context["app_name"]
63
- deploy_gist.gsub!(/(:application,\s)(\"\")/, '\1' + "\"#{app_name}\"")
64
- deploy_gist.gsub!(/(:admin_runner,\s)(\"\")/, '\1' + "\"#{app_name}\"")
71
+ deploy_config_file.gsub!(/(:application,\s)(\"\")/, '\1' + "\"#{app_name}\"")
72
+ deploy_config_file.gsub!(/(:admin_runner,\s)(\"\")/, '\1' + "\"#{app_name}\"")
73
+ deploy_config_file.gsub!(/(:stage_dir,\s)(\"\")/, '\1' + "\"#{RECIPES[context['recipe']]['config_path']}/deploy\"")
65
74
 
66
75
  newrelic_appname = ui.ask("What's the name of the application in new relic?")
67
76
  if !newrelic_appname.nil? && !newrelic_appname.empty?
68
- deploy_gist.gsub!(/(:newrelic_appname,\s)(\"\")/, '\1' + "\"#{newrelic_appname}\"")
77
+ deploy_config_file.gsub!(/(:newrelic_appname,\s)(\"\")/, '\1' + "\"#{newrelic_appname}\"")
69
78
  context["newrelic_appname"] = newrelic_appname
70
79
  end
71
80
 
72
81
  newrelic_license_key = ui.ask("What's the license key of your newrelic account (can be found under 'Account settings')?")
73
82
  if !newrelic_license_key.nil? && !newrelic_license_key.empty?
74
- deploy_gist.gsub!(/(:newrelic_license_key,\s)(\"\")/, '\1' + "\"#{newrelic_license_key}\"")
83
+ deploy_config_file.gsub!(/(:newrelic_license_key,\s)(\"\")/, '\1' + "\"#{newrelic_license_key}\"")
75
84
  context["newrelic_license_key"] = newrelic_license_key
76
85
  end
77
-
86
+
87
+ extra_config_file = "#{RESOURCES_BASE_PATH}/#{context["recipe"]}/deploy.rb"
88
+ if File.exists? extra_config_file
89
+ deploy_config_file << "\n\n"
90
+ deploy_config_file << "# #{context['recipe'].capitalize} config"
91
+ deploy_config_file << "\n"
92
+
93
+ File.open(extra_config_file, "r") do |f|
94
+ f.each do |line|
95
+ deploy_config_file << line
96
+ end
97
+ end
98
+ end
99
+
78
100
  File.open(deploy, "w") do |f|
79
- deploy_gist.each_line do |line|
101
+ deploy_config_file.each_line do |line|
80
102
  f.print line
81
103
  end
82
104
  end
83
105
  end
84
-
85
- deploy_dir = File.join(base, "app", "config", "deploy")
106
+
107
+ deploy_dir = File.join(base, RECIPES[context['recipe']]['config_path'], "deploy")
86
108
  Dir.mkdir(deploy_dir) unless File.directory?(deploy_dir)
87
-
109
+
88
110
  {
89
- "production" => PRODUCTION_GIST,
90
- "staging" => STAGING_GIST
91
- }.each do |env, gist|
111
+ "production" => "deploy_production.rb",
112
+ "staging" => "deploy_staging.rb"
113
+ }.each do |env, source_file|
92
114
  file = File.join(deploy_dir, "#{env}.rb")
93
115
  write = true
94
116
 
@@ -100,25 +122,25 @@ def update_deploy_config(ui, base, context, force)
100
122
  end
101
123
 
102
124
  if write
103
- gist_body = get_plain_secure(gist).body
125
+ content = read_resource_file(source_file)
104
126
  server = ui.ask("On which server is the #{env} environment deployed?")
105
- if !server.match(/^.*\.kunstmaan\.be$/) && !server.match(/^.*\.kunstmaan\.com$/)
127
+ if !server.match(/^.*\.kunstmaan\.be$/) && !server.match(/^.*\.kunstmaan\.com$/)
106
128
  server = "#{server}.cloud.kunstmaan.com"
107
129
  end
108
130
 
109
- gist_body.gsub!(/(:domain,\s)(\"\")/, '\1' + "\"#{server}\"")
131
+ content.gsub!(/(:domain,\s)(\"\")/, '\1' + "\"#{server}\"")
110
132
 
111
133
  File.open(file, "w") do |f|
112
- gist_body.each_line do |line|
134
+ content.each_line do |line|
113
135
  f.print line
114
136
  end
115
137
  end
116
138
  end
117
139
  end
118
-
140
+
119
141
  end
120
142
 
121
- def update_jenkins_config(ui, base, context, force)
143
+ def update_symfony2(ui, base, context, force)
122
144
  file = File.join(base, "build.xml")
123
145
  write = true
124
146
 
@@ -130,52 +152,82 @@ def update_jenkins_config(ui, base, context, force)
130
152
  end
131
153
 
132
154
  if write
133
- gist_body = get_plain_secure(BUILD_GIST).body
134
-
155
+ build_file = read_resource_file("#{context['recipe']}/build.xml")
156
+
135
157
  context["app_name"] ||= ui.ask("What's the name of the application?")
136
158
  app_name = context["app_name"]
137
- gist_body.gsub!(/(\<project\sname=)(\"\")/, '\1' + "\"#{app_name}\"")
138
-
159
+ build_file.gsub!(/(\<project\sname=)(\"\")/, '\1' + "\"#{app_name}\"")
160
+
139
161
  File.open(file, "w") do |f|
140
- gist_body.each_line do |line|
162
+ build_file.each_line do |line|
141
163
  f.print line
142
164
  end
143
165
  end
144
166
  end
145
-
167
+
146
168
  build_dir = File.join(base, "build")
147
169
  web_dir = File.join(base, "web")
148
170
  Dir.mkdir(build_dir) unless File.directory?(build_dir)
149
171
  Dir.mkdir(web_dir) unless File.directory?(web_dir)
150
-
151
- {
152
- File.join(build_dir, "phpcs.xml") => PHPCS_GIST,
153
- File.join(build_dir, "phpmd.xml") => PHPMD_GIST,
154
- File.join(build_dir, "phpdox.xml") => PHPDOX_GIST,
155
- File.join(base, "app", "phpunit.xml.dist") => PHPUNIT_GIST,
156
- File.join(web_dir, "app_test.php") => APPTEST_GIST,
157
- File.join(base, "behat.yml-dist") => BEHAT_GIST
158
- }.each do |file, gist|
172
+
173
+ copy_resources({
174
+ File.join(build_dir, "phpcs.xml") => "#{context['recipe']}/phpcs.xml",
175
+ File.join(build_dir, "phpmd.xml") => "#{context['recipe']}/phpmd.xml",
176
+ File.join(build_dir, "phpdox.xml") => "#{context['recipe']}/phpdox.xml",
177
+ File.join(base, "app", "phpunit.xml.dist") => "#{context['recipe']}/phpunit.xml.dist",
178
+ File.join(web_dir, "app_test.php") => "#{context['recipe']}/app_test.php",
179
+ File.join(base, "behat.yml-dist") => "#{context['recipe']}/behat.yml-dist"
180
+ }, ui, context)
181
+ end
182
+
183
+ def update_play(ui, base, context, force)
184
+ copy_resources({
185
+ File.join(base, "start.sh") => "#{context['recipe']}/start.sh",
186
+ File.join(base, "stop.sh") => "#{context['recipe']}/stop.sh"
187
+ }, ui, context)
188
+ end
189
+
190
+ def copy_resources(resource_map, ui, context)
191
+ resource_map.each do |destination_file, source_file|
159
192
  write = true
160
-
161
- if File.exists?(file)
162
- overwrite = ui.ask("The file #{file} already exists, do you want to override it? ") { |q| q.default = 'n' }
193
+
194
+ if File.exists?(destination_file)
195
+ overwrite = ui.ask("The file #{destination_file} already exists, do you want to override it? ") { |q| q.default = 'n' }
163
196
  if !overwrite.match(/^y/)
164
197
  write = false
165
198
  end
166
199
  end
167
-
200
+
168
201
  if write
169
- gist_body = get_plain_secure(gist).body
170
-
171
- File.open(file, "w") do |f|
172
- gist_body.each_line do |line|
173
- f.print line
202
+ content = read_resource_file("#{source_file}")
203
+
204
+ File.open(destination_file, "w") do |f|
205
+ content.each_line do |line|
206
+ f.print line.sub("{{application_name}}", context['app_name'])
174
207
  end
175
208
  end
176
209
  end
177
210
  end
178
-
211
+ end
212
+
213
+ def read_resource_file(filename)
214
+ read_file("#{RESOURCES_BASE_PATH}/#{filename}")
215
+ end
216
+
217
+ def read_file(file)
218
+ content = ""
219
+ File.open(file, "r") do |f|
220
+ f.each do |line|
221
+ content << line
222
+ end
223
+ end
224
+
225
+ content
226
+ end
227
+
228
+ def unindent(string)
229
+ indentation = string[/\A\s*/]
230
+ string.strip.gsub(/^#{indentation}/, "")
179
231
  end
180
232
 
181
233
  def get_plain_secure(url)
@@ -198,27 +250,6 @@ def validate_path
198
250
  end
199
251
  end
200
252
 
201
- def update_vendors(base)
202
- vendors = File.join(base, "bin", "vendors")
203
- if File.directory?(vendors)
204
- fcontent = ""
205
- File.open(vendors, "r") do |f|
206
- f.each do |line|
207
- fcontent << line
208
- end
209
- end
210
-
211
- File.open(vendors, "w") do |f|
212
- fcontent.each_line do |line|
213
- f.print line
214
- if line.include? "git clone %s %s"
215
- f.print " system(sprintf('cd %s && git config core.filemode false', escapeshellarg($installDir)));" + "\r\n"
216
- end
217
- end
218
- end
219
- end
220
- end
221
-
222
253
  force = false
223
254
 
224
255
  OptionParser.new do |opts|
@@ -228,7 +259,7 @@ OptionParser.new do |opts|
228
259
  puts opts
229
260
  exit 0
230
261
  end
231
-
262
+
232
263
  opts.on("-c", "--config", "Creates the configuration files needed for deployment") do
233
264
  validate_path
234
265
  ui = HighLine.new
@@ -237,16 +268,7 @@ OptionParser.new do |opts|
237
268
  update_deploy_config ui, base, context, force
238
269
  exit 0
239
270
  end
240
-
241
- opts.on("-j", "--jenkins", "Creates the jenkins configuration files for this project") do
242
- validate_path
243
- ui = HighLine.new
244
- base = ARGV.shift
245
- context = Hash.new
246
- update_jenkins_config ui, base, context, force
247
- exit 0
248
- end
249
-
271
+
250
272
  opts.on("-f", "--force", "This will force the kumafying of the project") do
251
273
  force = true
252
274
  end
@@ -270,13 +292,13 @@ if force && File.exists?("Capfile")
270
292
  File.delete("Capfile")
271
293
  end
272
294
 
273
- puts "[start] capifony"
274
- %x(capifony .)
275
- puts "[end] capifony"
276
-
277
- update_capfile base, context, force
295
+ update_capfile ui, base, context, force
278
296
  update_deploy_config ui, base, context, force
279
- update_jenkins_config ui, base, context, force
280
- update_vendors base
297
+
298
+ begin
299
+ send("update_#{context['recipe']}", ui, base, context, force)
300
+ rescue NoMethodError => e
301
+ # Do nothing
302
+ end
281
303
 
282
304
  puts "[done] project kumafied!"
@@ -1,15 +1,15 @@
1
- module Kumastrano
1
+ module KStrano
2
2
  # Using the gem https://github.com/airbrake/airbrake doesn't work because it's made for rails apps, it needs rake etc. + you need to have the i18n gem
3
3
  # This will integrate a very easy command to tell airbrake a deploy has been done
4
4
  class AirbrakeHelper
5
-
5
+
6
6
  require 'net/http'
7
7
  require 'uri'
8
8
  require 'etc'
9
-
9
+
10
10
  def self.notify(api_key, revision, repository, environment = 'production', username = Etc.getlogin.capitalize)
11
11
  uri = URI.parse("http://airbrake.io")
12
-
12
+
13
13
  params = {
14
14
  'api_key' => api_key,
15
15
  'deploy[rails_env]' => environment, # Environment of the deploy (production, staging), this needs to be the current environment
@@ -20,9 +20,9 @@ module Kumastrano
20
20
 
21
21
  post = Net::HTTP::Post.new("/deploys")
22
22
  post.set_form_data(params)
23
-
23
+
24
24
  res = Net::HTTP.start(uri.host, uri.port) {|http| http.request(post)}
25
-
25
+
26
26
  if res.code.to_i == 200
27
27
  return true
28
28
  else
@@ -1,21 +1,21 @@
1
- module Kumastrano
1
+ module KStrano
2
2
  class CampfireHelper
3
-
3
+
4
4
  require 'broach'
5
-
5
+
6
6
  def self.speak(campfire_account, campfire_token, campfire_room, message="")
7
7
  ## extracted this to here, because i don't know how to call capistrano tasks with arguments
8
8
  ## else i just had to make one capistrano task which i could call
9
9
  if !campfire_account.nil? && !campfire_token.nil? && !campfire_room.nil?
10
-
10
+
11
11
  Broach.settings = {
12
12
  'account' => campfire_account,
13
13
  'token' => campfire_token,
14
14
  'use_ssl' => true
15
15
  }
16
-
16
+
17
17
  room = Broach::Room.find_by_name campfire_room
18
-
18
+
19
19
  if !room.nil?
20
20
  room.speak message
21
21
  end
@@ -1,36 +1,36 @@
1
- module Kumastrano
1
+ module KStrano
2
2
  class GitHelper
3
-
3
+
4
4
  require 'cgi'
5
-
5
+
6
6
  def self.fetch
7
7
  %x(git fetch)
8
8
  end
9
-
9
+
10
10
  def self.merge_base(commit1, commit2 = "HEAD")
11
11
  base = %x(git merge-base #{commit1} #{commit2})
12
12
  base.strip
13
13
  end
14
-
14
+
15
15
  def self.commit_hash(commit = "HEAD")
16
16
  hash = %x(git rev-parse #{commit})
17
17
  hash.strip
18
18
  end
19
-
19
+
20
20
  def self.branch_name(commit = "HEAD")
21
21
  name = %x(git name-rev --name-only #{commit})
22
22
  name.strip
23
23
  end
24
-
24
+
25
25
  def self.origin_refspec
26
26
  refspec = %x(git config remote.origin.fetch)
27
27
  refspec.strip
28
28
  end
29
-
29
+
30
30
  def self.origin_url
31
31
  url = %x(git config remote.origin.url)
32
32
  url.strip
33
33
  end
34
-
34
+
35
35
  end
36
36
  end
@@ -1,24 +1,24 @@
1
- module Kumastrano
1
+ module KStrano
2
2
  class JenkinsHelper
3
-
3
+
4
4
  require 'cgi'
5
5
  require 'net/http'
6
6
  require 'uri'
7
7
  require 'json'
8
-
8
+
9
9
  def self.available?(base_uri)
10
10
  res = get_plain("#{base_uri}")
11
11
  res.code.to_i == 200
12
12
  end
13
-
13
+
14
14
  def self.build_and_wait(job_uri, timeout=300, interval=2)
15
15
  success = false
16
16
  last_build_info = nil
17
- prev_build = Kumastrano::JenkinsHelper.last_build_number(job_uri)
18
- Kumastrano::JenkinsHelper.build_job(job_uri)
19
- Kumastrano.poll("A timeout occured", timeout, interval) do
17
+ prev_build = KStrano::JenkinsHelper.last_build_number(job_uri)
18
+ KStrano::JenkinsHelper.build_job(job_uri)
19
+ KStrano.poll("A timeout occured", timeout, interval) do
20
20
  ## wait for the building to be finished
21
- last_build_info = Kumastrano::JenkinsHelper.build_info(job_uri)
21
+ last_build_info = KStrano::JenkinsHelper.build_info(job_uri)
22
22
  result = last_build_info['result'] ## SUCCESS or FAILURE
23
23
  building = last_build_info['building']
24
24
  number = last_build_info['number']
@@ -35,7 +35,7 @@ module Kumastrano
35
35
  end
36
36
  return success, last_build_info
37
37
  end
38
-
38
+
39
39
  def self.fetch_build_hash_from_build_info(build_info, branch_name)
40
40
  actions = build_info['actions']
41
41
 
@@ -47,18 +47,18 @@ module Kumastrano
47
47
  break
48
48
  end
49
49
  end
50
-
50
+
51
51
  build_hash
52
52
  end
53
-
53
+
54
54
  def self.make_safe_job_name(app_name, branch_name)
55
55
  job_name = "#{app_name} (#{branch_name})"
56
56
  job_name.gsub(/[#*\/\\]/, "-") # \/#* is unsafe for jenkins job name, because not uri safe
57
57
  end
58
-
58
+
59
59
  def self.job_url_for_branch(jenkins_base_uri, branch_name)
60
60
  current_job_url = nil
61
- Kumastrano::JenkinsHelper.list_jobs(jenkins_base_uri).each do |job|
61
+ KStrano::JenkinsHelper.list_jobs(jenkins_base_uri).each do |job|
62
62
  name = job["name"]
63
63
  url = job["url"]
64
64
  if /.*\(#{branch_name}\)/.match(name)
@@ -68,10 +68,10 @@ module Kumastrano
68
68
  end
69
69
  current_job_url
70
70
  end
71
-
71
+
72
72
  def self.job_url_for_name(jenkins_base_uri, job_name)
73
73
  current_job_url = nil
74
- Kumastrano::JenkinsHelper.list_jobs(jenkins_base_uri).each do |job|
74
+ KStrano::JenkinsHelper.list_jobs(jenkins_base_uri).each do |job|
75
75
  name = job["name"]
76
76
  url = job["url"]
77
77
  if job_name == name
@@ -81,12 +81,12 @@ module Kumastrano
81
81
  end
82
82
  current_job_url
83
83
  end
84
-
84
+
85
85
  def self.list_jobs(base_uri)
86
86
  res = get_plain("#{base_uri}/api/json?tree=jobs[name,url]")
87
87
  parsed_res = JSON.parse(res.body)["jobs"]
88
88
  end
89
-
89
+
90
90
  def self.create_new_job(base_uri, job_name, config)
91
91
  uri = URI.parse("#{base_uri}/createItem/api/json")
92
92
  request = Net::HTTP::Post.new(uri.path + "?name=#{CGI.escape(job_name)}")
@@ -100,33 +100,33 @@ module Kumastrano
100
100
  puts res.body
101
101
  end
102
102
  end
103
-
103
+
104
104
  def self.retrieve_config_xml(job_uri)
105
105
  res = get_plain("#{job_uri}/config.xml").body
106
106
  end
107
-
107
+
108
108
  def self.build_job(job_uri)
109
109
  res = get_plain("#{job_uri}/build")
110
110
  res.code.to_i == 302
111
111
  end
112
-
112
+
113
113
  def self.last_build_number(job_uri)
114
114
  res = get_plain("#{job_uri}/api/json?tree=lastBuild[number]")
115
115
  parsed_res = JSON.parse(res.body)
116
116
  parsed_res['lastBuild']['number']
117
117
  end
118
-
118
+
119
119
  def self.build_info(job_uri, build="lastBuild")
120
120
  res = get_plain("#{job_uri}/#{build}/api/json")
121
121
  parsed_res = JSON.parse(res.body)
122
122
  end
123
-
123
+
124
124
  private
125
-
125
+
126
126
  def self.get_plain(uri)
127
127
  uri = URI.parse uri
128
128
  res = Net::HTTP.start(uri.host, uri.port) { |http| http.get(uri.path, {}) }
129
129
  end
130
-
131
- end
130
+
131
+ end
132
132
  end
@@ -1,15 +1,15 @@
1
- module Kumastrano
2
-
3
- def poll(msg=nil, seconds=10.0, interval_seconds=1.0)
1
+ module KStrano
2
+
3
+ def poll(msg=nil, seconds=10.0, interval_seconds=1.0)
4
4
  (seconds / interval_seconds).to_i.times do
5
5
  result = yield
6
6
  return if result
7
7
  sleep interval_seconds
8
8
  end
9
- msg ||= "polling failed after #{seconds} seconds"
9
+ msg ||= "polling failed after #{seconds} seconds"
10
10
  raise msg
11
11
  end
12
-
12
+
13
13
  def say(text, prefix='--> ')
14
14
  Capistrano::CLI.ui.say("#{prefix}#{text}")
15
15
  end
@@ -23,5 +23,5 @@ module Kumastrano
23
23
  end
24
24
 
25
25
  module_function :poll, :say, :ask
26
-
26
+
27
27
  end