foreman 0.46.0 → 0.49.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 (68) hide show
  1. data/README.md +6 -0
  2. data/bin/foreman-runner +3 -7
  3. data/bin/taskman +8 -0
  4. data/data/example/Procfile +4 -3
  5. data/data/example/spawnee +14 -0
  6. data/data/example/spawner +7 -0
  7. data/data/export/bluepill/master.pill.erb +11 -10
  8. data/data/export/launchd/launchd.plist.erb +22 -0
  9. data/data/export/runit/log/run.erb +7 -0
  10. data/data/export/runit/run.erb +2 -2
  11. data/data/export/supervisord/app.conf.erb +12 -12
  12. data/data/export/upstart/master.conf.erb +2 -2
  13. data/data/export/upstart/process.conf.erb +3 -3
  14. data/lib/foreman/cli.rb +59 -23
  15. data/lib/foreman/engine/cli.rb +103 -0
  16. data/lib/foreman/engine.rb +216 -148
  17. data/lib/foreman/env.rb +27 -0
  18. data/lib/foreman/export/base.rb +107 -12
  19. data/lib/foreman/export/bluepill.rb +3 -17
  20. data/lib/foreman/export/inittab.rb +8 -11
  21. data/lib/foreman/export/launchd.rb +15 -0
  22. data/lib/foreman/export/runit.rb +14 -39
  23. data/lib/foreman/export/supervisord.rb +3 -13
  24. data/lib/foreman/export/upstart.rb +9 -27
  25. data/lib/foreman/export.rb +2 -2
  26. data/lib/foreman/process.rb +65 -67
  27. data/lib/foreman/procfile.rb +59 -25
  28. data/lib/foreman/version.rb +1 -1
  29. data/lib/foreman.rb +4 -0
  30. data/man/foreman.1 +5 -1
  31. data/spec/foreman/cli_spec.rb +46 -150
  32. data/spec/foreman/engine_spec.rb +47 -74
  33. data/spec/foreman/export/base_spec.rb +4 -7
  34. data/spec/foreman/export/bluepill_spec.rb +7 -6
  35. data/spec/foreman/export/inittab_spec.rb +7 -7
  36. data/spec/foreman/export/launchd_spec.rb +21 -0
  37. data/spec/foreman/export/runit_spec.rb +12 -17
  38. data/spec/foreman/export/supervisord_spec.rb +7 -56
  39. data/spec/foreman/export/upstart_spec.rb +22 -21
  40. data/spec/foreman/process_spec.rb +27 -110
  41. data/spec/foreman/procfile_spec.rb +26 -16
  42. data/spec/resources/Procfile +4 -0
  43. data/spec/resources/bin/echo +2 -0
  44. data/spec/resources/bin/env +2 -0
  45. data/spec/resources/bin/test +2 -0
  46. data/spec/resources/export/bluepill/app-concurrency.pill +6 -4
  47. data/spec/resources/export/bluepill/app.pill +6 -4
  48. data/spec/resources/export/launchd/launchd-a.default +22 -0
  49. data/spec/resources/export/launchd/launchd-b.default +22 -0
  50. data/spec/resources/export/supervisord/app-alpha-1.conf +24 -0
  51. data/spec/resources/export/supervisord/app-alpha-2.conf +4 -4
  52. data/spec/spec_helper.rb +57 -6
  53. metadata +26 -25
  54. data/data/export/runit/log_run.erb +0 -7
  55. data/lib/foreman/color.rb +0 -40
  56. data/lib/foreman/procfile_entry.rb +0 -26
  57. data/lib/foreman/utils.rb +0 -18
  58. data/spec/foreman/color_spec.rb +0 -31
  59. data/spec/foreman/procfile_entry_spec.rb +0 -13
  60. data/spec/resources/export/supervisord/app-env-with-comma.conf +0 -24
  61. data/spec/resources/export/supervisord/app-env.conf +0 -21
  62. data/spec/resources/export/supervisord/app.conf +0 -24
  63. /data/spec/resources/export/runit/{app-alpha-1-log-run → app-alpha-1/log/run} +0 -0
  64. /data/spec/resources/export/runit/{app-alpha-1-run → app-alpha-1/run} +0 -0
  65. /data/spec/resources/export/runit/{app-alpha-2-log-run → app-alpha-2/log/run} +0 -0
  66. /data/spec/resources/export/runit/{app-alpha-2-run → app-alpha-2/run} +0 -0
  67. /data/spec/resources/export/runit/{app-bravo-1-log-run → app-bravo-1/log/run} +0 -0
  68. /data/spec/resources/export/runit/{app-bravo-1-run → app-bravo-1/run} +0 -0
@@ -1,8 +1,7 @@
1
1
  require "foreman"
2
- require "foreman/color"
2
+ require "foreman/env"
3
3
  require "foreman/process"
4
4
  require "foreman/procfile"
5
- require "foreman/utils"
6
5
  require "tempfile"
7
6
  require "timeout"
8
7
  require "fileutils"
@@ -10,218 +9,287 @@ require "thread"
10
9
 
11
10
  class Foreman::Engine
12
11
 
13
- attr_reader :environment
14
- attr_reader :procfile
15
- attr_reader :directory
12
+ attr_reader :env
16
13
  attr_reader :options
17
-
18
- COLORS = %w( cyan yellow green magenta red blue intense_cyan intense_yellow
19
- intense_green intense_magenta intense_red, intense_blue )
20
-
21
- Foreman::Color.enable($stdout)
22
-
23
- def initialize(procfile, options={})
24
- @procfile = Foreman::Procfile.new(procfile) if File.exists?(procfile)
25
- @directory = options[:app_root] || File.expand_path(File.dirname(procfile))
14
+ attr_reader :processes
15
+
16
+ # Create an +Engine+ for running processes
17
+ #
18
+ # @param [Hash] options
19
+ #
20
+ # @option options [String] :formation (all=1) The process formation to use
21
+ # @option options [Fixnum] :port (5000) The base port to assign to processes
22
+ # @option options [String] :root (Dir.pwd) The root directory from which to run processes
23
+ #
24
+ def initialize(options={})
26
25
  @options = options.dup
27
- @output_mutex = Mutex.new
28
26
 
29
- @options[:env] ||= default_env
30
- @environment = read_environment_files(@options[:env])
27
+ @options[:formation] ||= (options[:concurrency] || "all=1")
28
+
29
+ @env = {}
30
+ @mutex = Mutex.new
31
+ @names = {}
32
+ @processes = []
33
+ @running = {}
34
+ @readers = {}
31
35
  end
32
36
 
37
+ # Start the processes registered to this +Engine+
38
+ #
33
39
  def start
34
- proctitle "ruby: foreman master"
35
- termtitle "#{File.basename(@directory)} - foreman"
36
-
37
40
  trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
38
41
  trap("INT") { puts "SIGINT received"; terminate_gracefully }
42
+ trap("HUP") { puts "SIGHUP received"; terminate_gracefully } if ::Signal.list.keys.include? 'HUP'
39
43
 
40
- assign_colors
44
+ startup
41
45
  spawn_processes
42
46
  watch_for_output
43
- watch_for_termination
47
+ sleep 0.1
48
+ watch_for_termination { terminate_gracefully }
49
+ shutdown
44
50
  end
45
51
 
46
- def port_for(process, num, base_port=nil)
47
- base_port ||= 5000
48
- offset = procfile.process_names.index(process.name) * 100
49
- base_port.to_i + offset + num - 1
52
+ # Register a process to be run by this +Engine+
53
+ #
54
+ # @param [String] name A name for this process
55
+ # @param [String] command The command to run
56
+ # @param [Hash] options
57
+ #
58
+ # @option options [Hash] :env A custom environment for this process
59
+ #
60
+ def register(name, command, options={})
61
+ options[:env] ||= env
62
+ options[:cwd] ||= File.dirname(command.split(" ").first)
63
+ process = Foreman::Process.new(command, options)
64
+ @names[process] = name
65
+ @processes << process
50
66
  end
51
67
 
52
- def apply_environment!
53
- environment.each { |k,v| ENV[k] = v }
68
+ # Clear the processes registered to this +Engine+
69
+ #
70
+ def clear
71
+ @names = {}
72
+ @processes = []
54
73
  end
55
74
 
56
- def self.read_environment(filename)
57
- return {} unless File.exists?(filename)
75
+ # Register processes by reading a Procfile
76
+ #
77
+ # @param [String] filename A Procfile from which to read processes to register
78
+ #
79
+ def load_procfile(filename)
80
+ options[:root] ||= File.dirname(filename)
81
+ Foreman::Procfile.new(filename).entries do |name, command|
82
+ register name, command, :cwd => options[:root]
83
+ end
84
+ self
85
+ end
58
86
 
59
- File.read(filename).split("\n").inject({}) do |hash, line|
60
- if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
61
- key, val = [$1, $2]
62
- case val
63
- when /\A'(.*)'\z/ then hash[key] = $1
64
- when /\A"(.*)"\z/ then hash[key] = $1.gsub(/\\(.)/, '\1')
65
- else hash[key] = val
66
- end
67
- end
68
- hash
87
+ # Load a .env file into the +env+ for this +Engine+
88
+ #
89
+ # @param [String] filename A .env file to load into the environment
90
+ #
91
+ def load_env(filename)
92
+ Foreman::Env.new(filename).entries do |name, value|
93
+ @env[name] = value
69
94
  end
70
95
  end
71
96
 
72
- private ######################################################################
97
+ # Send a signal to all processesstarted by this +Engine+
98
+ #
99
+ # @param [String] signal The signal to send to each process
100
+ #
101
+ def killall(signal="SIGTERM")
102
+ Process.kill "-#{signal}", Process.pid
103
+ end
73
104
 
74
- def spawn_processes
75
- concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
105
+ # Get the process formation
106
+ #
107
+ # @returns [Fixnum] The formation count for the specified process
108
+ #
109
+ def formation
110
+ @formation ||= parse_formation(options[:formation])
111
+ end
76
112
 
77
- procfile.entries.each do |entry|
78
- reader, writer = (IO.method(:pipe).arity == 0 ? IO.pipe : IO.pipe("BINARY"))
79
- entry.spawn(concurrency[entry.name], writer, @directory, @environment, port_for(entry, 1, base_port)).each do |process|
80
- running_processes[process.pid] = process
81
- readers[process] = reader
82
- end
83
- end
113
+ # List the available process names
114
+ #
115
+ # @returns [Array] A list of process names
116
+ #
117
+ def process_names
118
+ @processes.map { |p| @names[p] }
84
119
  end
85
120
 
86
- def base_port
87
- options[:port] || 5000
121
+ # Get the +Process+ for a specifid name
122
+ #
123
+ # @param [String] name The process name
124
+ #
125
+ # @returns [Foreman::Process] The +Process+ for the specified name
126
+ #
127
+ def process(name)
128
+ @names.invert[name]
88
129
  end
89
130
 
90
- def kill_all(signal="SIGTERM")
91
- running_processes.each do |pid, process|
92
- info "sending #{signal} to pid #{pid}"
93
- process.kill signal
131
+ # Yield each +Process+ in order
132
+ #
133
+ def each_process
134
+ process_names.each do |name|
135
+ yield name, process(name)
94
136
  end
95
137
  end
96
138
 
97
- def terminate_gracefully
98
- return if @terminating
99
- @terminating = true
100
- info "sending SIGTERM to all processes"
101
- kill_all "SIGTERM"
102
- Timeout.timeout(5) do
103
- while running_processes.length > 0
104
- pid, status = Process.wait2
105
- process = running_processes.delete(pid)
106
- info "process terminated", process.name
107
- end
108
- end
109
- rescue Timeout::Error
110
- info "sending SIGKILL to all processes"
111
- kill_all "SIGKILL"
112
- end
113
-
114
- def poll_readers
115
- rs, ws = IO.select(readers.values, [], [], 1)
116
- (rs || []).each do |r|
117
- data = r.gets
118
- next unless data
119
- data.force_encoding("BINARY") if data.respond_to?(:force_encoding)
120
- ps, message = data.split(",", 2)
121
- color = colors[ps.split(".").first]
122
- info message, ps, color
123
- end
139
+ # Get the root directory for this +Engine+
140
+ #
141
+ # @returns [String] The root directory
142
+ #
143
+ def root
144
+ File.expand_path(options[:root] || Dir.pwd)
124
145
  end
125
146
 
126
- def watch_for_output
127
- Thread.new do
128
- require "win32console" if Foreman.windows?
129
- begin
130
- loop do
131
- poll_readers
132
- end
133
- rescue Exception => ex
134
- puts ex.message
135
- puts ex.backtrace
136
- end
147
+ # Get the port for a given process and offset
148
+ #
149
+ # @param [Foreman::Process] process A +Process+ associated with this engine
150
+ # @param [Fixnum] instance The instance of the process
151
+ #
152
+ # @returns [Fixnum] port The port to use for this instance of this process
153
+ #
154
+ def port_for(process, instance, base=nil)
155
+ if base
156
+ base + (@processes.index(process.process) * 100) + (instance - 1)
157
+ else
158
+ base_port + (@processes.index(process) * 100) + (instance - 1)
137
159
  end
138
160
  end
139
161
 
140
- def watch_for_termination
141
- pid, status = Process.wait2
142
- process = running_processes.delete(pid)
143
- info "process terminated", process.name
144
- terminate_gracefully
145
- rescue Errno::ECHILD
162
+ # Get the base port for this foreman instance
163
+ #
164
+ # @returns [Fixnum] port The base port
165
+ #
166
+ def base_port
167
+ (options[:port] || env["PORT"] || ENV["PORT"] || 5000).to_i
146
168
  end
147
169
 
148
- def info(message, name="system", color=:white)
149
- output = ""
150
- output += $stdout.color(color)
151
- output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
152
- output += $stdout.color(:reset)
153
- output += message.chomp
154
- puts output
170
+ # deprecated
171
+ def environment
172
+ env
155
173
  end
156
174
 
157
- def print(message=nil)
158
- @output_mutex.synchronize do
159
- $stdout.print message
160
- end
161
- end
175
+ private
162
176
 
163
- def puts(message=nil)
164
- @output_mutex.synchronize do
165
- $stdout.puts message
166
- end
177
+ ### Engine API ######################################################
178
+
179
+ def startup
180
+ raise TypeError, "must use a subclass of Foreman::Engine"
167
181
  end
168
182
 
169
- def longest_process_name
170
- @longest_process_name ||= begin
171
- longest = procfile.process_names.map { |name| name.length }.sort.last
172
- longest = 6 if longest < 6 # system
173
- longest
174
- end
183
+ def output(name, data)
184
+ raise TypeError, "must use a subclass of Foreman::Engine"
175
185
  end
176
186
 
177
- def pad_process_name(name="system")
178
- name.to_s.ljust(longest_process_name + 3) # add 3 for process number padding
187
+ def shutdown
188
+ raise TypeError, "must use a subclass of Foreman::Engine"
179
189
  end
180
190
 
181
- def proctitle(title)
182
- $0 = title
191
+ ## Helpers ##########################################################
192
+
193
+ def create_pipe
194
+ IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
183
195
  end
184
196
 
185
- def termtitle(title)
186
- printf("\033]0;#{title}\007") unless Foreman.windows?
197
+ def name_for(pid)
198
+ process, index = @running[pid]
199
+ [ @names[process], index.to_s ].compact.join(".")
187
200
  end
188
201
 
189
- def running_processes
190
- @running_processes ||= {}
202
+ def parse_formation(formation)
203
+ pairs = formation.to_s.gsub(/\s/, "").split(",")
204
+
205
+ pairs.inject(Hash.new(0)) do |ax, pair|
206
+ process, amount = pair.split("=")
207
+ process == "all" ? ax.default = amount.to_i : ax[process] = amount.to_i
208
+ ax
209
+ end
191
210
  end
192
211
 
193
- def readers
194
- @readers ||= {}
212
+ def output_with_mutex(name, message)
213
+ @mutex.synchronize do
214
+ output name, message
215
+ end
195
216
  end
196
217
 
197
- def colors
198
- @colors ||= {}
218
+ def system(message)
219
+ output_with_mutex "system", message
199
220
  end
200
221
 
201
- def assign_colors
202
- procfile.entries.each_with_index do |entry, idx|
203
- colors[entry.name] = COLORS[idx % COLORS.length]
222
+ def termination_message_for(status)
223
+ if status.exited?
224
+ "exited with code #{status.exitstatus}"
225
+ elsif status.signaled?
226
+ "terminated by SIG#{Signal.list.invert[status.termsig]}"
227
+ else
228
+ "died a mysterious death"
204
229
  end
205
230
  end
206
231
 
207
- def process_by_reader(reader)
208
- readers.invert[reader]
232
+ def flush_reader(reader)
233
+ until reader.eof?
234
+ data = reader.gets
235
+ output_with_mutex name_for(@readers.key(reader)), data
236
+ end
209
237
  end
210
238
 
211
- def read_environment_files(filenames)
212
- environment = {}
239
+ ## Engine ###########################################################
213
240
 
214
- (filenames || "").split(",").map(&:strip).each do |filename|
215
- error "No such file: #{filename}" unless File.exists?(filename)
216
- environment.merge!(Foreman::Engine.read_environment(filename))
241
+ def spawn_processes
242
+ @processes.each do |process|
243
+ 1.upto(formation[@names[process]]) do |n|
244
+ reader, writer = create_pipe
245
+ begin
246
+ pid = process.run(:output => writer, :env => { "PORT" => port_for(process, n).to_s })
247
+ writer.puts "started with pid #{pid}"
248
+ rescue Errno::ENOENT
249
+ writer.puts "unknown command: #{process.command}"
250
+ end
251
+ @running[pid] = [process, n]
252
+ @readers[pid] = reader
253
+ end
217
254
  end
255
+ end
218
256
 
219
- environment
257
+ def watch_for_output
258
+ Thread.new do
259
+ begin
260
+ loop do
261
+ (IO.select(@readers.values).first || []).each do |reader|
262
+ data = reader.gets
263
+ output_with_mutex name_for(@readers.invert[reader]), data
264
+ end
265
+ end
266
+ rescue Exception => ex
267
+ puts ex.message
268
+ puts ex.backtrace
269
+ end
270
+ end
220
271
  end
221
272
 
222
- def default_env
223
- env = File.join(directory, ".env")
224
- File.exists?(env) ? env : ""
273
+ def watch_for_termination
274
+ pid, status = Process.wait2
275
+ output_with_mutex name_for(pid), termination_message_for(status)
276
+ @running.delete(pid)
277
+ yield if block_given?
278
+ pid
279
+ rescue Errno::ECHILD
280
+ end
281
+
282
+ def terminate_gracefully
283
+ return if @terminating
284
+ @terminating = true
285
+ system "sending SIGTERM to all processes"
286
+ killall "SIGTERM"
287
+ Timeout.timeout(5) do
288
+ watch_for_termination while @running.length > 0
289
+ end
290
+ rescue Timeout::Error
291
+ system "sending SIGKILL to all processes"
292
+ killall "SIGKILL"
225
293
  end
226
294
 
227
295
  end
@@ -0,0 +1,27 @@
1
+ require "foreman"
2
+
3
+ class Foreman::Env
4
+
5
+ attr_reader :entries
6
+
7
+ def initialize(filename)
8
+ @entries = File.read(filename).split("\n").inject({}) do |ax, line|
9
+ if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
10
+ key = $1
11
+ case val = $2
12
+ when /\A'(.*)'\z/ then ax[key] = $1
13
+ when /\A"(.*)"\z/ then ax[key] = $1.gsub(/\\(.)/, '\1')
14
+ else ax[key] = val
15
+ end
16
+ end
17
+ ax
18
+ end
19
+ end
20
+
21
+ def entries
22
+ @entries.each do |key, value|
23
+ yield key, value
24
+ end
25
+ end
26
+
27
+ end
@@ -1,27 +1,81 @@
1
1
  require "foreman/export"
2
- require "foreman/utils"
2
+ require "ostruct"
3
+ require "pathname"
4
+ require "shellwords"
3
5
 
4
6
  class Foreman::Export::Base
5
7
 
6
- attr_reader :location, :engine, :app, :log, :port, :user, :template, :concurrency
8
+ attr_reader :location
9
+ attr_reader :engine
10
+ attr_reader :options
11
+ attr_reader :formation
12
+
13
+ # deprecated
14
+ attr_reader :port
7
15
 
8
16
  def initialize(location, engine, options={})
9
- @location = location
10
- @engine = engine
11
- @app = options[:app]
12
- @log = options[:log]
13
- @port = options[:port]
14
- @user = options[:user]
15
- @template = options[:template]
16
- @concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
17
+ @location = location
18
+ @engine = engine
19
+ @options = options.dup
20
+ @formation = engine.formation
21
+
22
+ # deprecated
23
+ def port
24
+ Foreman::Export::Base.warn_deprecation!
25
+ engine.base_port
26
+ end
27
+
28
+ # deprecated
29
+ def template
30
+ Foreman::Export::Base.warn_deprecation!
31
+ options[:template]
32
+ end
33
+
34
+ # deprecated
35
+ def @engine.procfile
36
+ Foreman::Export::Base.warn_deprecation!
37
+ @processes.map do |process|
38
+ OpenStruct.new(
39
+ :name => @names[process],
40
+ :process => process
41
+ )
42
+ end
43
+ end
17
44
  end
18
45
 
19
46
  def export
20
- raise "export method must be overridden"
47
+ error("Must specify a location") unless location
48
+ FileUtils.mkdir_p(location) rescue error("Could not create: #{location}")
49
+ FileUtils.mkdir_p(log) rescue error("Could not create: #{log}")
50
+ FileUtils.chown(user, nil, log) rescue error("Could not chown #{log} to #{user}")
51
+ end
52
+
53
+ def app
54
+ options[:app] || "app"
55
+ end
56
+
57
+ def log
58
+ options[:log] || "/var/log/#{app}"
59
+ end
60
+
61
+ def user
62
+ options[:user] || app
21
63
  end
22
64
 
23
65
  private ######################################################################
24
66
 
67
+ def self.warn_deprecation!
68
+ @@deprecation_warned ||= false
69
+ return if @@deprecation_warned
70
+ puts "WARNING: Using deprecated exporter interface. Please update your exporter"
71
+ puts "the interface shown in the upstart exporter:"
72
+ puts
73
+ puts "https://github.com/ddollar/foreman/blob/master/lib/foreman/export/upstart.rb"
74
+ puts "https://github.com/ddollar/foreman/blob/master/data/export/upstart/process.conf.erb"
75
+ puts
76
+ @@deprecation_warned = true
77
+ end
78
+
25
79
  def error(message)
26
80
  raise Foreman::Export::Exception.new(message)
27
81
  end
@@ -29,8 +83,19 @@ private ######################################################################
29
83
  def say(message)
30
84
  puts "[foreman export] %s" % message
31
85
  end
86
+
87
+ def clean(filename)
88
+ return unless File.exists?(filename)
89
+ say "cleaning up: #{filename}"
90
+ FileUtils.rm(filename)
91
+ end
92
+
93
+ def shell_quote(value)
94
+ '"' + Shellwords.escape(value) + '"'
95
+ end
32
96
 
33
- def export_template(exporter, file, template_root)
97
+ # deprecated
98
+ def old_export_template(exporter, file, template_root)
34
99
  if template_root && File.exist?(file_path = File.join(template_root, file))
35
100
  File.read(file_path)
36
101
  elsif File.exist?(file_path = File.expand_path(File.join("~/.foreman/templates", file)))
@@ -40,9 +105,39 @@ private ######################################################################
40
105
  end
41
106
  end
42
107
 
108
+ def export_template(name, file=nil, template_root=nil)
109
+ if file && template_root
110
+ old_export_template name, file, template_root
111
+ else
112
+ name_without_first = name.split("/")[1..-1].join("/")
113
+ matchers = []
114
+ matchers << File.join(options[:template], name_without_first) if options[:template]
115
+ matchers << File.expand_path("~/.foreman/templates/#{name}")
116
+ matchers << File.expand_path("../../../../data/export/#{name}", __FILE__)
117
+ File.read(matchers.detect { |m| File.exists?(m) })
118
+ end
119
+ end
120
+
121
+ def write_template(name, target, binding)
122
+ compiled = ERB.new(export_template(name)).result(binding)
123
+ write_file target, compiled
124
+ end
125
+
126
+ def chmod(mode, file)
127
+ say "setting #{file} to mode #{mode}"
128
+ FileUtils.chmod mode, File.join(location, file)
129
+ end
130
+
131
+ def create_directory(dir)
132
+ say "creating: #{dir}"
133
+ FileUtils.mkdir_p(File.join(location, dir))
134
+ end
135
+
43
136
  def write_file(filename, contents)
44
137
  say "writing: #{filename}"
45
138
 
139
+ filename = File.join(location, filename) unless Pathname.new(filename).absolute?
140
+
46
141
  File.open(filename, "w") do |file|
47
142
  file.puts contents
48
143
  end
@@ -4,23 +4,9 @@ require "foreman/export"
4
4
  class Foreman::Export::Bluepill < Foreman::Export::Base
5
5
 
6
6
  def export
7
- error("Must specify a location") unless location
8
-
9
- FileUtils.mkdir_p location
10
-
11
- app = self.app || File.basename(engine.directory)
12
- user = self.user || app
13
- log_root = self.log || "/var/log/#{app}"
14
- template_root = self.template
15
-
16
- Dir["#{location}/#{app}.pill"].each do |file|
17
- say "cleaning up: #{file}"
18
- FileUtils.rm(file)
19
- end
20
-
21
- master_template = export_template("bluepill", "master.pill.erb", template_root)
22
- master_config = ERB.new(master_template).result(binding)
23
- write_file "#{location}/#{app}.pill", master_config
7
+ super
8
+ clean "#{location}/#{app}.pill"
9
+ write_template "bluepill/master.pill.erb", "#{app}.pill", binding
24
10
  end
25
11
 
26
12
  end