foreman-capistrano 0.51.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/README.md +46 -0
  2. data/bin/foreman +7 -0
  3. data/bin/foreman-runner +32 -0
  4. data/bin/taskman +8 -0
  5. data/data/example/Procfile +4 -0
  6. data/data/example/Procfile.without_colon +2 -0
  7. data/data/example/error +7 -0
  8. data/data/example/log/neverdie.log +4 -0
  9. data/data/example/spawnee +14 -0
  10. data/data/example/spawner +7 -0
  11. data/data/example/ticker +14 -0
  12. data/data/example/utf8 +11 -0
  13. data/data/export/bluepill/master.pill.erb +28 -0
  14. data/data/export/launchd/launchd.plist.erb +22 -0
  15. data/data/export/runit/log/run.erb +7 -0
  16. data/data/export/runit/run.erb +3 -0
  17. data/data/export/supervisord/app.conf.erb +27 -0
  18. data/data/export/upstart/master.conf.erb +8 -0
  19. data/data/export/upstart/process.conf.erb +7 -0
  20. data/data/export/upstart/process_master.conf.erb +2 -0
  21. data/lib/foreman/capistrano.rb +55 -0
  22. data/lib/foreman/cli.rb +140 -0
  23. data/lib/foreman/distribution.rb +9 -0
  24. data/lib/foreman/engine/cli.rb +104 -0
  25. data/lib/foreman/engine.rb +313 -0
  26. data/lib/foreman/env.rb +27 -0
  27. data/lib/foreman/export/base.rb +146 -0
  28. data/lib/foreman/export/bluepill.rb +12 -0
  29. data/lib/foreman/export/inittab.rb +33 -0
  30. data/lib/foreman/export/launchd.rb +15 -0
  31. data/lib/foreman/export/runit.rb +34 -0
  32. data/lib/foreman/export/supervisord.rb +16 -0
  33. data/lib/foreman/export/upstart.rb +25 -0
  34. data/lib/foreman/export.rb +34 -0
  35. data/lib/foreman/helpers.rb +45 -0
  36. data/lib/foreman/process.rb +102 -0
  37. data/lib/foreman/procfile.rb +92 -0
  38. data/lib/foreman/version.rb +5 -0
  39. data/lib/foreman.rb +23 -0
  40. data/man/foreman.1 +244 -0
  41. data/spec/foreman/cli_spec.rb +87 -0
  42. data/spec/foreman/engine_spec.rb +104 -0
  43. data/spec/foreman/export/base_spec.rb +19 -0
  44. data/spec/foreman/export/bluepill_spec.rb +37 -0
  45. data/spec/foreman/export/inittab_spec.rb +40 -0
  46. data/spec/foreman/export/launchd_spec.rb +21 -0
  47. data/spec/foreman/export/runit_spec.rb +36 -0
  48. data/spec/foreman/export/supervisord_spec.rb +36 -0
  49. data/spec/foreman/export/upstart_spec.rb +88 -0
  50. data/spec/foreman/export_spec.rb +24 -0
  51. data/spec/foreman/helpers_spec.rb +26 -0
  52. data/spec/foreman/process_spec.rb +48 -0
  53. data/spec/foreman/procfile_spec.rb +41 -0
  54. data/spec/foreman_spec.rb +16 -0
  55. data/spec/helper_spec.rb +18 -0
  56. data/spec/resources/Procfile +4 -0
  57. data/spec/resources/bin/echo +2 -0
  58. data/spec/resources/bin/env +2 -0
  59. data/spec/resources/bin/test +2 -0
  60. data/spec/resources/bin/utf8 +2 -0
  61. data/spec/resources/export/bluepill/app-concurrency.pill +49 -0
  62. data/spec/resources/export/bluepill/app.pill +46 -0
  63. data/spec/resources/export/inittab/inittab.concurrency +4 -0
  64. data/spec/resources/export/inittab/inittab.default +4 -0
  65. data/spec/resources/export/launchd/launchd-a.default +22 -0
  66. data/spec/resources/export/launchd/launchd-b.default +22 -0
  67. data/spec/resources/export/runit/app-alpha-1/log/run +7 -0
  68. data/spec/resources/export/runit/app-alpha-1/run +3 -0
  69. data/spec/resources/export/runit/app-alpha-2/log/run +7 -0
  70. data/spec/resources/export/runit/app-alpha-2/run +3 -0
  71. data/spec/resources/export/runit/app-bravo-1/log/run +7 -0
  72. data/spec/resources/export/runit/app-bravo-1/run +3 -0
  73. data/spec/resources/export/supervisord/app-alpha-1.conf +24 -0
  74. data/spec/resources/export/supervisord/app-alpha-2.conf +24 -0
  75. data/spec/resources/export/upstart/app-alpha-1.conf +5 -0
  76. data/spec/resources/export/upstart/app-alpha-2.conf +5 -0
  77. data/spec/resources/export/upstart/app-alpha.conf +2 -0
  78. data/spec/resources/export/upstart/app-bravo-1.conf +5 -0
  79. data/spec/resources/export/upstart/app-bravo.conf +2 -0
  80. data/spec/resources/export/upstart/app.conf +8 -0
  81. data/spec/spec_helper.rb +153 -0
  82. metadata +145 -0
data/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # Foreman
2
+
3
+ Manage Procfile-based applications
4
+
5
+ <table>
6
+ <tr>
7
+ <th>If you have...</th>
8
+ <th>Install with...</th>
9
+ </tr>
10
+ <tr>
11
+ <td>Ruby (MRI, JRuby, Windows)</td>
12
+ <td><pre>$ gem install foreman</pre></td>
13
+ </tr>
14
+ <tr>
15
+ <td>Mac OS X</td>
16
+ <td><a href="http://assets.foreman.io/foreman/foreman.pkg">foreman.pkg</a></td>
17
+ </tr>
18
+ </table>
19
+
20
+ ## Getting Started
21
+
22
+ * http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
23
+
24
+ ## Documentation
25
+
26
+ * [man page](http://ddollar.github.com/foreman)
27
+ * [wiki](http://github.com/ddollar/foreman/wiki)
28
+ * [changelog](https://github.com/ddollar/foreman/blob/master/Changelog.md)
29
+
30
+ ## Ports
31
+
32
+ * [shoreman](https://github.com/hecticjeff/shoreman) - shell
33
+ * [honcho](https://github.com/nickstenning/honcho) - python
34
+ * [norman](https://github.com/josh/norman) - node.js
35
+
36
+ ## Authors
37
+
38
+ #### Created and maintained by
39
+ David Dollar
40
+
41
+ #### Patches contributed by
42
+ [Contributor List](https://github.com/ddollar/foreman/contributors)
43
+
44
+ ## License
45
+
46
+ MIT
data/bin/foreman ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path("../../lib", __FILE__)
4
+
5
+ require "foreman/cli"
6
+
7
+ Foreman::CLI.start
@@ -0,0 +1,32 @@
1
+ #!/bin/sh
2
+ #
3
+ #/ Usage: foreman-runner [-d <dir>] <command> [<args>...]
4
+ #/
5
+ #/ Run a command with exec, optionally changing directory first
6
+
7
+ set -e
8
+
9
+ error() {
10
+ echo $@ >&2
11
+ exit 1
12
+ }
13
+
14
+ usage() {
15
+ cat $0 | grep '^#/' | cut -c4-
16
+ exit
17
+ }
18
+
19
+ while getopts ":hd:" OPT; do
20
+ case $OPT in
21
+ d) cd "$OPTARG" ;;
22
+ h) usage ;;
23
+ \?) error "invalid option: -$OPTARG" ;;
24
+ :) error "option -$OPTARG requires an argument" ;;
25
+ esac
26
+ done
27
+
28
+ shift $((OPTIND-1))
29
+
30
+ [ -z "$1" ] && usage
31
+
32
+ exec "$@"
data/bin/taskman ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path("../../lib", __FILE__)
4
+
5
+ require "foreman/cli"
6
+
7
+ Foreman::CLI.engine_class = Foreman::TmuxEngine
8
+ Foreman::CLI.start
@@ -0,0 +1,4 @@
1
+ ticker: ruby ./ticker $PORT
2
+ error: ruby ./error
3
+ utf8: ruby ./utf8
4
+ spawner: ./spawner
@@ -0,0 +1,2 @@
1
+ ticker ./ticker $PORT
2
+ error ./error
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $stdout.sync = true
4
+
5
+ puts "will error in 10s"
6
+ sleep 5
7
+ raise "Dying"
@@ -0,0 +1,4 @@
1
+ tick
2
+ tick
3
+ ./never_die:6:in `sleep': Interrupt
4
+ from ./never_die:6
@@ -0,0 +1,14 @@
1
+ #!/bin/sh
2
+
3
+ NAME="$1"
4
+
5
+ sigterm() {
6
+ echo "$NAME: got sigterm"
7
+ }
8
+
9
+ #trap sigterm SIGTERM
10
+
11
+ while true; do
12
+ echo "$NAME: ping $$"
13
+ sleep 1
14
+ done
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+
3
+ ./spawnee A &
4
+ ./spawnee B &
5
+ ./spawnee C &
6
+
7
+ wait
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $stdout.sync = true
4
+
5
+ %w( SIGINT SIGTERM ).each do |signal|
6
+ trap(signal) do
7
+ puts "received #{signal} but i'm ignoring it!"
8
+ end
9
+ end
10
+
11
+ while true
12
+ puts "tick: #{ARGV.inspect} -- FOO:#{ENV["FOO"]}"
13
+ sleep 1
14
+ end
data/data/example/utf8 ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: BINARY
3
+
4
+ $stdout.sync = true
5
+
6
+ while true
7
+ puts "\u65e5\u672c\u8a9e\u6587\u5b57\u5217"
8
+ puts "\u0915\u0932\u094d\u0907\u0928\u0643\u0637\u0628\u041a\u0430\u043b\u0438\u043d\u0430"
9
+ puts "\xff\x03"
10
+ sleep 1
11
+ end
@@ -0,0 +1,28 @@
1
+ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/bluepill.log") do |app|
2
+
3
+ app.uid = "<%= user %>"
4
+ app.gid = "<%= user %>"
5
+
6
+ <% engine.each_process do |name, process| %>
7
+ <% 1.upto(engine.formation[name]) do |num| %>
8
+ <% port = engine.port_for(process, num) %>
9
+ app.process("<%= name %>-<%= num %>") do |process|
10
+ process.start_command = "<%= process.command %>"
11
+
12
+ process.working_dir = "<%= engine.root %>"
13
+ process.daemonize = true
14
+ process.environment = <%= engine.env.merge("PORT" => port.to_s).inspect %>
15
+ process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
16
+ process.stop_grace_time = 45.seconds
17
+
18
+ process.stdout = process.stderr = "<%= log %>/<%= app %>-<%= name %>-<%= num %>.log"
19
+
20
+ process.monitor_children do |children|
21
+ children.stop_command "kill {{PID}}"
22
+ end
23
+
24
+ process.group = "<%= app %>-<%= name %>"
25
+ end
26
+ <% end %>
27
+ <% end %>
28
+ end
@@ -0,0 +1,22 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>Label</key>
6
+ <string><%= "#{app}-#{name}-#{num}" %></string>
7
+ <key>ProgramArguments</key>
8
+ <array>
9
+ <string><%= process.command %></string>
10
+ </array>
11
+ <key>KeepAlive</key>
12
+ <true/>
13
+ <key>RunAtLoad</key>
14
+ <true/>
15
+ <key>StandardErrorPath</key>
16
+ <string><%= log %>/<%= app %>-<%= name %>-<%=num%>.log</string>
17
+ <key>UserName</key>
18
+ <string><%= user %></string>
19
+ <key>WorkingDirectory</key>
20
+ <string><%= engine.root %></string>
21
+ </dict>
22
+ </plist>
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+ set -e
3
+
4
+ LOG=<%= log %>/<%= name %>-<%= num %>
5
+
6
+ test -d "$LOG" || mkdir -p m2750 "$LOG" && chown <%= user %> "$LOG"
7
+ exec chpst -u <%= user %> svlogd "$LOG"
@@ -0,0 +1,3 @@
1
+ #!/bin/sh
2
+ cd <%= engine.root %>
3
+ exec chpst -u <%= user %> -e <%= File.join(location, "#{process_directory}/env") %> <%= process.command %>
@@ -0,0 +1,27 @@
1
+ <%
2
+ app_names = []
3
+ engine.each_process do |name, process|
4
+ 1.upto(engine.formation[name]) do |num|
5
+ port = engine.port_for(process, num)
6
+ full_name = "#{app}-#{name}-#{num}"
7
+ environment = engine.env.merge("PORT" => port.to_s).map do |key, value|
8
+ "#{key}=#{shell_quote(value)}"
9
+ end
10
+ app_names << full_name
11
+ %>
12
+ [program:<%= full_name %>]
13
+ command=<%= process.command %>
14
+ autostart=true
15
+ autorestart=true
16
+ stopsignal=QUIT
17
+ stdout_logfile=<%= log %>/<%= name %>-<%= num %>.log
18
+ stderr_logfile=<%= log %>/<%= name %>-<%= num %>.error.log
19
+ user=<%= user %>
20
+ directory=<%= engine.root %>
21
+ environment=<%= environment.join(',') %><%
22
+ end
23
+ end
24
+ %>
25
+
26
+ [group:<%= app %>]
27
+ programs=<%= app_names.join(',') %>
@@ -0,0 +1,8 @@
1
+ pre-start script
2
+
3
+ bash << "EOF"
4
+ mkdir -p <%= log %>
5
+ chown -R <%= user %> <%= log %>
6
+ EOF
7
+
8
+ end script
@@ -0,0 +1,7 @@
1
+ start on starting <%= app %>-<%= name %>
2
+ stop on stopping <%= app %>-<%= name %>
3
+ respawn
4
+
5
+ exec su - <%= user %> --shell=/bin/bash -c 'cd <%= engine.root %>; trap '\''kill -QUIT $(jobs -p)'\'' EXIT; export PORT=<%= port %>;<% engine.env.each_pair do |var,env| %> export <%= var.upcase %>=<%= shell_quote(env) %>; <% end %> <%= process.command %> >> <%= log %>/<%=name%>-<%=num%>.log 2>&1'
6
+
7
+
@@ -0,0 +1,2 @@
1
+ start on starting <%= app %>
2
+ stop on stopping <%= app %>
@@ -0,0 +1,55 @@
1
+ if defined?(Capistrano)
2
+ Capistrano::Configuration.instance(:must_exist).load do
3
+
4
+ namespace :foreman do
5
+ desc <<-DESC
6
+ Export the Procfile to upstart. Will use sudo if available.
7
+
8
+ You can override any of these defaults by setting the variables shown below.
9
+
10
+ set :foreman_format, "upstart"
11
+ set :foreman_location, "/etc/init"
12
+ set :foreman_procfile, "Procfile"
13
+ set :foreman_app, application
14
+ set :foreman_user, user
15
+ set :foreman_log, "#{shared_path}/log"
16
+ set :foreman_concurrency, false
17
+ DESC
18
+ task :export, :roles => :app do
19
+ bundle_cmd = fetch(:bundle_cmd, "bundle")
20
+ foreman_format = fetch(:foreman_format, "upstart")
21
+ foreman_location = fetch(:foreman_location, "/etc/init")
22
+ foreman_procfile = fetch(:foreman_procfile, "Procfile")
23
+ foreman_app = fetch(:foreman_app, application)
24
+ foreman_user = fetch(:foreman_user, user)
25
+ foreman_log = fetch(:foreman_log, "#{shared_path}/log")
26
+ foreman_concurrency = fetch(:foreman_concurrency, false)
27
+
28
+ args = ["#{foreman_format} #{foreman_location}"]
29
+ args << "-f #{foreman_procfile}"
30
+ args << "-a #{foreman_app}"
31
+ args << "-u #{foreman_user}"
32
+ args << "-l #{foreman_log}"
33
+ args << "-c #{foreman_concurrency}" if foreman_concurrency
34
+ run "cd #{release_path} && #{sudo} #{bundle_cmd} exec foreman export #{args.join(' ')}"
35
+ end
36
+
37
+ desc "Start the application services"
38
+ task :start, :roles => :app do
39
+ run "#{sudo} start #{application}"
40
+ end
41
+
42
+ desc "Stop the application services"
43
+ task :stop, :roles => :app do
44
+ run "#{sudo} stop #{application}"
45
+ end
46
+
47
+ desc "Restart the application services"
48
+ task :restart, :roles => :app do
49
+ run "#{sudo} start #{application} || #{sudo} restart #{application}"
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+
@@ -0,0 +1,140 @@
1
+ require "foreman"
2
+ require "foreman/helpers"
3
+ require "foreman/engine"
4
+ require "foreman/engine/cli"
5
+ require "foreman/export"
6
+ require "foreman/version"
7
+ require "shellwords"
8
+ require "yaml"
9
+ require "thor"
10
+
11
+ class Foreman::CLI < Thor
12
+
13
+ include Foreman::Helpers
14
+
15
+ map ["-v", "--version"] => :version
16
+
17
+ class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
18
+ class_option :root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"
19
+
20
+ desc "start [PROCESS]", "Start the application (or a specific PROCESS)"
21
+
22
+ method_option :color, :type => :boolean, :aliases => "-c", :desc => "Force color to be enabled"
23
+ method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
24
+ method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"'
25
+ method_option :port, :type => :numeric, :aliases => "-p"
26
+
27
+ class << self
28
+ # Hackery. Take the run method away from Thor so that we can redefine it.
29
+ def is_thor_reserved_word?(word, type)
30
+ return false if word == "run"
31
+ super
32
+ end
33
+ end
34
+
35
+ def start(process=nil)
36
+ check_procfile!
37
+ load_environment!
38
+ engine.load_procfile(procfile)
39
+ engine.options[:formation] = "#{process}=1" if process
40
+ engine.start
41
+ end
42
+
43
+ desc "export FORMAT LOCATION", "Export the application to another process management format"
44
+
45
+ method_option :app, :type => :string, :aliases => "-a"
46
+ method_option :log, :type => :string, :aliases => "-l"
47
+ method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
48
+ method_option :port, :type => :numeric, :aliases => "-p"
49
+ method_option :user, :type => :string, :aliases => "-u"
50
+ method_option :template, :type => :string, :aliases => "-t"
51
+ method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
52
+
53
+ def export(format, location=nil)
54
+ check_procfile!
55
+ load_environment!
56
+ engine.load_procfile(procfile)
57
+ formatter = Foreman::Export.formatter(format)
58
+ formatter.new(location, engine, options).export
59
+ rescue Foreman::Export::Exception => ex
60
+ error ex.message
61
+ end
62
+
63
+ desc "check", "Validate your application's Procfile"
64
+
65
+ def check
66
+ check_procfile!
67
+ engine.load_procfile(procfile)
68
+ error "no processes defined" unless engine.processes.length > 0
69
+ puts "valid procfile detected (#{engine.process_names.join(', ')})"
70
+ end
71
+
72
+ desc "run COMMAND [ARGS...]", "Run a command using your application's environment"
73
+
74
+ method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
75
+
76
+ def run(*args)
77
+ load_environment!
78
+ begin
79
+ exec engine.env, args.shelljoin
80
+ rescue Errno::EACCES
81
+ error "not executable: #{args.first}"
82
+ rescue Errno::ENOENT
83
+ error "command not found: #{args.first}"
84
+ end
85
+ end
86
+
87
+ desc "version", "Display Foreman gem version"
88
+
89
+ def version
90
+ puts Foreman::VERSION
91
+ end
92
+
93
+ no_tasks do
94
+ def engine
95
+ @engine ||= begin
96
+ engine_class = Foreman::Engine::CLI
97
+ engine = engine_class.new(options)
98
+ engine
99
+ end
100
+ end
101
+ end
102
+
103
+ private ######################################################################
104
+
105
+ def error(message)
106
+ puts "ERROR: #{message}"
107
+ exit 1
108
+ end
109
+
110
+ def check_procfile!
111
+ error("#{procfile} does not exist.") unless File.exist?(procfile)
112
+ end
113
+
114
+ def load_environment!
115
+ if options[:env]
116
+ options[:env].split(",").each do |file|
117
+ engine.load_env file
118
+ end
119
+ else
120
+ default_env = File.join(engine.root, ".env")
121
+ engine.load_env default_env if File.exists?(default_env)
122
+ end
123
+ end
124
+
125
+ def procfile
126
+ case
127
+ when options[:procfile] then options[:procfile]
128
+ when options[:root] then File.expand_path(File.join(options[:app_root], "Procfile"))
129
+ else "Procfile"
130
+ end
131
+ end
132
+
133
+ def options
134
+ original_options = super
135
+ return original_options unless File.exists?(".foreman")
136
+ defaults = ::YAML::load_file(".foreman") || {}
137
+ Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
138
+ end
139
+
140
+ end
@@ -0,0 +1,9 @@
1
+ module Foreman
2
+ module Distribution
3
+ def self.files
4
+ Dir[File.expand_path("../../../{bin,data,lib}/**/*", __FILE__)].select do |file|
5
+ File.file?(file)
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,104 @@
1
+ require "foreman/engine"
2
+
3
+ class Foreman::Engine::CLI < Foreman::Engine
4
+
5
+ module Color
6
+
7
+ ANSI = {
8
+ :reset => 0,
9
+ :black => 30,
10
+ :red => 31,
11
+ :green => 32,
12
+ :yellow => 33,
13
+ :blue => 34,
14
+ :magenta => 35,
15
+ :cyan => 36,
16
+ :white => 37,
17
+ :bright_black => 30,
18
+ :bright_red => 31,
19
+ :bright_green => 32,
20
+ :bright_yellow => 33,
21
+ :bright_blue => 34,
22
+ :bright_magenta => 35,
23
+ :bright_cyan => 36,
24
+ :bright_white => 37,
25
+ }
26
+
27
+ def self.enable(io, force=false)
28
+ io.extend(self)
29
+ @@color_force = force
30
+ end
31
+
32
+ def color?
33
+ return true if @@color_force
34
+ return false if Foreman.windows?
35
+ return false unless self.respond_to?(:isatty)
36
+ self.isatty && ENV["TERM"]
37
+ end
38
+
39
+ def color(name)
40
+ return "" unless color?
41
+ return "" unless ansi = ANSI[name.to_sym]
42
+ "\e[#{ansi}m"
43
+ end
44
+
45
+ end
46
+
47
+ FOREMAN_COLORS = %w( cyan yellow green magenta red blue intense_cyan intense_yellow
48
+ intense_green intense_magenta intense_red, intense_blue )
49
+
50
+ def startup
51
+ @colors = map_colors
52
+ proctitle "foreman: master" unless Foreman.windows?
53
+ Color.enable($stdout, options[:color])
54
+ end
55
+
56
+ def output(name, data)
57
+ data.to_s.chomp.split("\n").each do |message|
58
+ output = ""
59
+ output += $stdout.color(@colors[name.split(".").first].to_sym)
60
+ output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
61
+ output += $stdout.color(:reset)
62
+ output += message
63
+ $stdout.puts output
64
+ $stdout.flush
65
+ end
66
+ rescue Errno::EPIPE
67
+ terminate_gracefully
68
+ end
69
+
70
+ def shutdown
71
+ end
72
+
73
+ private
74
+
75
+ def name_padding
76
+ @name_padding ||= begin
77
+ index_padding = @names.values.map { |n| formation[n] }.max.to_s.length + 1
78
+ name_padding = @names.values.map { |n| n.length + index_padding }.sort.last
79
+ [ 6, name_padding ].max
80
+ end
81
+ end
82
+
83
+ def pad_process_name(name)
84
+ name.ljust(name_padding, " ")
85
+ end
86
+
87
+ def map_colors
88
+ colors = Hash.new("white")
89
+ @names.values.each_with_index do |name, index|
90
+ colors[name] = FOREMAN_COLORS[index % FOREMAN_COLORS.length]
91
+ end
92
+ colors["system"] = "intense_white"
93
+ colors
94
+ end
95
+
96
+ def proctitle(title)
97
+ $0 = title
98
+ end
99
+
100
+ def termtitle(title)
101
+ printf("\033]0;#{title}\007") unless Foreman.windows?
102
+ end
103
+
104
+ end