ypadlyak-foreman 0.81.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 (108) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +53 -0
  3. data/bin/foreman +7 -0
  4. data/bin/foreman-runner +41 -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/daemon/master.conf.erb +14 -0
  15. data/data/export/daemon/process.conf.erb +8 -0
  16. data/data/export/daemon/process_master.conf.erb +2 -0
  17. data/data/export/launchd/launchd.plist.erb +33 -0
  18. data/data/export/runit/log/run.erb +7 -0
  19. data/data/export/runit/run.erb +4 -0
  20. data/data/export/supervisord/app.conf.erb +32 -0
  21. data/data/export/systemd/master.target.erb +5 -0
  22. data/data/export/systemd/process.service.erb +15 -0
  23. data/data/export/systemd/process_master.target.erb +3 -0
  24. data/data/export/upstart/master.conf.erb +2 -0
  25. data/data/export/upstart/process.conf.erb +18 -0
  26. data/data/export/upstart/process_master.conf.erb +2 -0
  27. data/lib/foreman/cli.rb +160 -0
  28. data/lib/foreman/distribution.rb +9 -0
  29. data/lib/foreman/engine/cli.rb +104 -0
  30. data/lib/foreman/engine.rb +486 -0
  31. data/lib/foreman/env.rb +29 -0
  32. data/lib/foreman/export/base.rb +156 -0
  33. data/lib/foreman/export/bluepill.rb +12 -0
  34. data/lib/foreman/export/daemon.rb +28 -0
  35. data/lib/foreman/export/inittab.rb +42 -0
  36. data/lib/foreman/export/launchd.rb +22 -0
  37. data/lib/foreman/export/runit.rb +34 -0
  38. data/lib/foreman/export/supervisord.rb +16 -0
  39. data/lib/foreman/export/systemd.rb +32 -0
  40. data/lib/foreman/export/upstart.rb +43 -0
  41. data/lib/foreman/export.rb +36 -0
  42. data/lib/foreman/helpers.rb +45 -0
  43. data/lib/foreman/process.rb +80 -0
  44. data/lib/foreman/procfile.rb +94 -0
  45. data/lib/foreman/version.rb +5 -0
  46. data/lib/foreman.rb +17 -0
  47. data/man/foreman.1 +284 -0
  48. data/spec/foreman/cli_spec.rb +111 -0
  49. data/spec/foreman/engine_spec.rb +114 -0
  50. data/spec/foreman/export/base_spec.rb +19 -0
  51. data/spec/foreman/export/bluepill_spec.rb +37 -0
  52. data/spec/foreman/export/daemon_spec.rb +97 -0
  53. data/spec/foreman/export/inittab_spec.rb +40 -0
  54. data/spec/foreman/export/launchd_spec.rb +31 -0
  55. data/spec/foreman/export/runit_spec.rb +36 -0
  56. data/spec/foreman/export/supervisord_spec.rb +38 -0
  57. data/spec/foreman/export/systemd_spec.rb +91 -0
  58. data/spec/foreman/export/upstart_spec.rb +118 -0
  59. data/spec/foreman/export_spec.rb +24 -0
  60. data/spec/foreman/helpers_spec.rb +26 -0
  61. data/spec/foreman/process_spec.rb +71 -0
  62. data/spec/foreman/procfile_spec.rb +50 -0
  63. data/spec/foreman_spec.rb +16 -0
  64. data/spec/helper_spec.rb +19 -0
  65. data/spec/resources/Procfile +5 -0
  66. data/spec/resources/Procfile.bad +2 -0
  67. data/spec/resources/bin/echo +2 -0
  68. data/spec/resources/bin/env +2 -0
  69. data/spec/resources/bin/test +2 -0
  70. data/spec/resources/bin/utf8 +2 -0
  71. data/spec/resources/export/bluepill/app-concurrency.pill +49 -0
  72. data/spec/resources/export/bluepill/app.pill +81 -0
  73. data/spec/resources/export/daemon/app-alpha-1.conf +7 -0
  74. data/spec/resources/export/daemon/app-alpha-2.conf +7 -0
  75. data/spec/resources/export/daemon/app-alpha.conf +2 -0
  76. data/spec/resources/export/daemon/app-bravo-1.conf +7 -0
  77. data/spec/resources/export/daemon/app-bravo.conf +2 -0
  78. data/spec/resources/export/daemon/app.conf +14 -0
  79. data/spec/resources/export/inittab/inittab.concurrency +4 -0
  80. data/spec/resources/export/inittab/inittab.default +6 -0
  81. data/spec/resources/export/launchd/launchd-a.default +29 -0
  82. data/spec/resources/export/launchd/launchd-b.default +29 -0
  83. data/spec/resources/export/launchd/launchd-c.default +30 -0
  84. data/spec/resources/export/runit/app-alpha-1/log/run +7 -0
  85. data/spec/resources/export/runit/app-alpha-1/run +4 -0
  86. data/spec/resources/export/runit/app-alpha-2/log/run +7 -0
  87. data/spec/resources/export/runit/app-alpha-2/run +4 -0
  88. data/spec/resources/export/runit/app-bravo-1/log/run +7 -0
  89. data/spec/resources/export/runit/app-bravo-1/run +4 -0
  90. data/spec/resources/export/supervisord/app-alpha-1.conf +46 -0
  91. data/spec/resources/export/supervisord/app-alpha-2.conf +24 -0
  92. data/spec/resources/export/systemd/concurrency/app-alpha-1.service +14 -0
  93. data/spec/resources/export/systemd/concurrency/app-alpha-2.service +14 -0
  94. data/spec/resources/export/systemd/concurrency/app-alpha.target +3 -0
  95. data/spec/resources/export/systemd/concurrency/app.target +5 -0
  96. data/spec/resources/export/systemd/standard/app-alpha-1.service +14 -0
  97. data/spec/resources/export/systemd/standard/app-alpha.target +3 -0
  98. data/spec/resources/export/systemd/standard/app-bravo-1.service +14 -0
  99. data/spec/resources/export/systemd/standard/app-bravo.target +3 -0
  100. data/spec/resources/export/systemd/standard/app.target +5 -0
  101. data/spec/resources/export/upstart/app-alpha-1.conf +11 -0
  102. data/spec/resources/export/upstart/app-alpha-2.conf +11 -0
  103. data/spec/resources/export/upstart/app-alpha.conf +2 -0
  104. data/spec/resources/export/upstart/app-bravo-1.conf +11 -0
  105. data/spec/resources/export/upstart/app-bravo.conf +2 -0
  106. data/spec/resources/export/upstart/app.conf +2 -0
  107. data/spec/spec_helper.rb +180 -0
  108. metadata +164 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7295387a8caf93d8d5b19abcd78aa75b81c3cb757496cfce0031e71935e43ded
4
+ data.tar.gz: 8b092d5f7ac71bd544c61cd6a8592d2de9d7642c00c3d896e98d546ea8e77cca
5
+ SHA512:
6
+ metadata.gz: b910868bd8e1fa49314246654d5dbb4b747398f8aba4b90626958da6eab0291b7025d9528e4b4799d751d40a4a4f077e449d9cf122941b5cd0e2e47f2a13f724
7
+ data.tar.gz: 1ba0b3cfa6c651d84d183d01d0a5b9ae1489920c48f91c7948c099430eb964684d00cb9dee5648ce57f786435cafaf0e8597fbe59a3bb8c41b506532a072b547
data/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # Foreman
2
+
3
+ [![Build Status](https://travis-ci.org/ddollar/foreman.svg?branch=master)](https://travis-ci.org/ddollar/foreman)
4
+ [![Code Climate](https://codeclimate.com/github/ddollar/foreman.png)](https://codeclimate.com/github/ddollar/foreman)
5
+ [![Inline docs](http://inch-ci.org/github/ddollar/foreman.svg?branch=master)](http://inch-ci.org/github/ddollar/foreman)
6
+
7
+ Manage Procfile-based applications
8
+
9
+ ## Installation
10
+
11
+ $ gem install foreman
12
+
13
+ Ruby users should take care *not* to install foreman in their project's `Gemfile`.
14
+
15
+ ## Getting Started
16
+
17
+ * http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
18
+
19
+ ## Supported Ruby versions
20
+
21
+ See [.travis.yml](.travis.yml) for a list of Ruby versions against which Foreman is tested.
22
+
23
+ ## Documentation
24
+
25
+ * [man page](http://ddollar.github.io/foreman/)
26
+ * [wiki](https://github.com/ddollar/foreman/wiki)
27
+ * [changelog](https://github.com/ddollar/foreman/blob/master/Changelog.md)
28
+
29
+ ## Ports
30
+
31
+ * [forego](https://github.com/ddollar/forego) - Go
32
+ * [node-foreman](https://github.com/strongloop/node-foreman) - Node.js
33
+ * [gaffer](https://github.com/jingweno/gaffer) - Java/JVM
34
+ * [goreman](https://github.com/mattn/goreman) - Go
35
+ * [honcho](https://github.com/nickstenning/honcho) - python
36
+ * [proclet](https://github.com/kazeburo/Proclet) - Perl
37
+ * [shoreman](https://github.com/chrismytton/shoreman) - shell
38
+ * [crank](https://github.com/arktisklada/crank) - Crystal
39
+ * [houseman](https://github.com/fujimura/houseman) - Haskell
40
+
41
+ ## Authors
42
+
43
+ #### Created and maintained by
44
+ David Dollar
45
+
46
+ #### Patches contributed by
47
+ [Contributor List](https://github.com/ddollar/foreman/contributors)
48
+
49
+ ## License
50
+
51
+ Foreman is licensed under the MIT license.
52
+
53
+ See LICENSE for the full license text.
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,41 @@
1
+ #!/bin/sh
2
+ #
3
+ #/ Usage: foreman-runner [-d <dir>] [-p] <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
+ read_profile=""
20
+
21
+ while getopts ":hd:p" OPT; do
22
+ case $OPT in
23
+ d) cd "$OPTARG" ;;
24
+ p) read_profile="1" ;;
25
+ h) usage ;;
26
+ \?) error "invalid option: -$OPTARG" ;;
27
+ :) error "option -$OPTARG requires an argument" ;;
28
+ esac
29
+ done
30
+
31
+ shift $((OPTIND-1))
32
+
33
+ [ -z "$1" ] && usage
34
+
35
+ if [ "$read_profile" = "1" ]; then
36
+ if [ -f .profile ]; then
37
+ . ./.profile
38
+ fi
39
+ fi
40
+
41
+ exec "$@"
@@ -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 = %Q{<%= 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,14 @@
1
+ pre-start script
2
+
3
+ bash << "EOF"
4
+ mkdir -p <%= log %>
5
+ chown -R <%= user %> <%= log %>
6
+ mkdir -p <%= run %>
7
+ chown -R <%= user %> <%= run %>
8
+ EOF
9
+
10
+ end script
11
+
12
+ start on runlevel [2345]
13
+
14
+ stop on runlevel [016]
@@ -0,0 +1,8 @@
1
+ start on starting <%= app %>-<%= name.gsub('_', '-') %>
2
+ stop on stopping <%= app %>-<%= name.gsub('_', '-') %>
3
+ respawn
4
+
5
+ env PORT=<%= port %><% engine.env.each_pair do |var, env| %>
6
+ env <%= var.upcase %>=<%= env %><% end %>
7
+
8
+ exec start-stop-daemon --start --chuid <%= user %> --chdir <%= engine.root %> --make-pidfile --pidfile <%= run %>/<%= app %>-<%= name %>-<%= num %>.pid --exec <%= executable %><%= arguments %> >> <%= log %>/<%= app %>-<%= name %>-<%= num %>.log 2>&1
@@ -0,0 +1,2 @@
1
+ start on starting <%= app %>
2
+ stop on stopping <%= app %>
@@ -0,0 +1,33 @@
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>EnvironmentVariables</key>
8
+ <dict>
9
+ <%- engine.env.merge("PORT" => port).each_pair do |var,env| -%>
10
+ <key><%= var.upcase %></key>
11
+ <string><%= env %></string>
12
+ <%- end -%>
13
+ </dict>
14
+ <key>ProgramArguments</key>
15
+ <array>
16
+ <%- command_args.each do |command| -%>
17
+ <string><%= command %></string>
18
+ <%- end -%>
19
+ </array>
20
+ <key>KeepAlive</key>
21
+ <true/>
22
+ <key>RunAtLoad</key>
23
+ <true/>
24
+ <key>StandardOutPath</key>
25
+ <string><%= log %>/<%= app %>-<%= name %>-<%=num%>.log</string>
26
+ <key>StandardErrorPath</key>
27
+ <string><%= log %>/<%= app %>-<%= name %>-<%=num%>.log</string>
28
+ <key>UserName</key>
29
+ <string><%= user %></string>
30
+ <key>WorkingDirectory</key>
31
+ <string><%= engine.root %></string>
32
+ </dict>
33
+ </plist>
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+ set -e
3
+
4
+ LOG=<%= log %>/<%= name %>-<%= num %>
5
+
6
+ test -d "$LOG" || mkdir -p -m 2750 "$LOG" && chown <%= user %> "$LOG"
7
+ exec chpst -u <%= user %> svlogd "$LOG"
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ cd <%= engine.root %>
3
+ exec 2>&1
4
+ exec chpst -u <%= user %> -e <%= File.join(location, "#{process_directory}/env") %> <%= process.command %>
@@ -0,0 +1,32 @@
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
+ value = shell_quote(value)
9
+ value = value.gsub('\=', '=')
10
+ value = value.gsub('\&', '&')
11
+ value = value.gsub('\?', '?')
12
+ "#{key}=\"#{value}\""
13
+ end
14
+ app_names << full_name
15
+ -%>
16
+ [program:<%= full_name %>]
17
+ command=<%= process.command %>
18
+ autostart=true
19
+ autorestart=true
20
+ stopsignal=QUIT
21
+ stdout_logfile=<%= log %>/<%= name %>-<%= num %>.log
22
+ stderr_logfile=<%= log %>/<%= name %>-<%= num %>.error.log
23
+ user=<%= user %>
24
+ directory=<%= engine.root %>
25
+ environment=<%= environment.join(',') %>
26
+
27
+ <%
28
+ end
29
+ end
30
+ -%>
31
+ [group:<%= app %>]
32
+ programs=<%= app_names.join(',') %>
@@ -0,0 +1,5 @@
1
+ [Unit]
2
+ Wants=<%= process_master_names.join(' ') %>
3
+
4
+ [Install]
5
+ WantedBy=multi-user.target
@@ -0,0 +1,15 @@
1
+ [Unit]
2
+ PartOf=<%= app %>-<%= name %>.target
3
+
4
+ [Service]
5
+ User=<%= user %>
6
+ WorkingDirectory=<%= engine.root %>
7
+ Environment=PORT=<%= port %><% engine.env.each_pair do |var,env| %>
8
+ Environment=<%= var.upcase %>=<%= env %><% end %>
9
+ ExecStart=/bin/bash -lc '<%= process.command %>'
10
+ Restart=always
11
+ StandardInput=null
12
+ StandardOutput=syslog
13
+ StandardError=syslog
14
+ SyslogIdentifier=%n
15
+ KillMode=process
@@ -0,0 +1,3 @@
1
+ [Unit]
2
+ PartOf=<%= app %>.target
3
+ Wants=<%= process_names.join(' ') %>
@@ -0,0 +1,2 @@
1
+ start on runlevel [2345]
2
+ stop on runlevel [!2345]
@@ -0,0 +1,18 @@
1
+ start on starting <%= app %>-<%= name %>
2
+ stop on stopping <%= app %>-<%= name %>
3
+
4
+ <% if engine.env['custom_upstart_reload_signal'] %>
5
+ reload signal <%= engine.env['custom_upstart_reload_signal'] %>
6
+ <% end %>
7
+ respawn
8
+
9
+ env PORT=<%= port %>
10
+ <% engine.env.each do |name,value| -%>
11
+ env <%= name.upcase %>='<%= value.gsub(/'/, "'\"'\"'") %>'
12
+ <% end -%>
13
+
14
+ setuid <%= user %>
15
+
16
+ chdir <%= engine.root %>
17
+
18
+ exec <%= process.command %>
@@ -0,0 +1,2 @@
1
+ start on starting <%= app %>
2
+ stop on stopping <%= app %>
@@ -0,0 +1,160 @@
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"', :desc => 'Specify what processes will run and how many. Default: "all=1"'
25
+ method_option :port, :type => :numeric, :aliases => "-p"
26
+ method_option :timeout, :type => :numeric, :aliases => "-t", :desc => "Specify the amount of time (in seconds) processes have to shutdown gracefully before receiving a SIGKILL, defaults to 5."
27
+
28
+ class << self
29
+ # Hackery. Take the run method away from Thor so that we can redefine it.
30
+ def is_thor_reserved_word?(word, type)
31
+ return false if word == "run"
32
+ super
33
+ end
34
+ end
35
+
36
+ def start(process=nil)
37
+ check_procfile!
38
+ load_environment!
39
+ engine.load_procfile(procfile)
40
+ engine.options[:formation] = "#{process}=1" if process
41
+ engine.start
42
+ end
43
+
44
+ desc "export FORMAT LOCATION", "Export the application to another process management format"
45
+
46
+ method_option :app, :type => :string, :aliases => "-a"
47
+ method_option :log, :type => :string, :aliases => "-l"
48
+ method_option :run, :type => :string, :aliases => "-r", :desc => "Specify the pid file directory, defaults to /var/run/<application>"
49
+ method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
50
+ method_option :port, :type => :numeric, :aliases => "-p"
51
+ method_option :user, :type => :string, :aliases => "-u"
52
+ method_option :template, :type => :string, :aliases => "-t"
53
+ method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"', :desc => 'Specify what processes will run and how many. Default: "all=1"'
54
+
55
+ def export(format, location=nil)
56
+ check_procfile!
57
+ load_environment!
58
+ engine.load_procfile(procfile)
59
+ formatter = Foreman::Export.formatter(format)
60
+ formatter.new(location, engine, options).export
61
+ rescue Foreman::Export::Exception => ex
62
+ error ex.message
63
+ end
64
+
65
+ desc "check", "Validate your application's Procfile"
66
+
67
+ def check
68
+ check_procfile!
69
+ engine.load_procfile(procfile)
70
+ error "no processes defined" unless engine.processes.length > 0
71
+ puts "valid procfile detected (#{engine.process_names.join(', ')})"
72
+ end
73
+
74
+ desc "run COMMAND [ARGS...]", "Run a command using your application's environment"
75
+
76
+ method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
77
+ stop_on_unknown_option! :run
78
+
79
+ def run(*args)
80
+ load_environment!
81
+
82
+ if File.file?(procfile)
83
+ engine.load_procfile(procfile)
84
+ end
85
+
86
+ pid = fork do
87
+ begin
88
+ engine.env.each { |k,v| ENV[k] = v }
89
+ if args.size == 1 && process = engine.process(args.first)
90
+ process.exec(:env => engine.env)
91
+ else
92
+ exec args.shelljoin
93
+ end
94
+ rescue Errno::EACCES
95
+ error "not executable: #{args.first}"
96
+ rescue Errno::ENOENT
97
+ error "command not found: #{args.first}"
98
+ end
99
+ end
100
+ trap("INT") do
101
+ Process.kill(:INT, pid)
102
+ end
103
+ Process.wait(pid)
104
+ exit $?.exitstatus || 0
105
+ rescue Interrupt
106
+ end
107
+
108
+ desc "version", "Display Foreman gem version"
109
+
110
+ def version
111
+ puts Foreman::VERSION
112
+ end
113
+
114
+ no_tasks do
115
+ def engine
116
+ @engine ||= begin
117
+ engine_class = Foreman::Engine::CLI
118
+ engine = engine_class.new(options)
119
+ engine
120
+ end
121
+ end
122
+ end
123
+
124
+ private ######################################################################
125
+
126
+ def error(message)
127
+ puts "ERROR: #{message}"
128
+ exit 1
129
+ end
130
+
131
+ def check_procfile!
132
+ error("#{procfile} does not exist.") unless File.file?(procfile)
133
+ end
134
+
135
+ def load_environment!
136
+ if options[:env]
137
+ options[:env].split(",").each do |file|
138
+ engine.load_env file
139
+ end
140
+ else
141
+ default_env = File.join(engine.root, ".env")
142
+ engine.load_env default_env if File.file?(default_env)
143
+ end
144
+ end
145
+
146
+ def procfile
147
+ case
148
+ when options[:procfile] then options[:procfile]
149
+ when options[:root] then File.expand_path(File.join(options[:root], "Procfile"))
150
+ else "Procfile"
151
+ end
152
+ end
153
+
154
+ def options
155
+ original_options = super
156
+ return original_options unless File.file?(".foreman")
157
+ defaults = ::YAML::load_file(".foreman") || {}
158
+ Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
159
+ end
160
+ 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 bright_cyan bright_yellow
48
+ bright_green bright_magenta bright_red bright_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.lines.map(&:chomp).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"] = "bright_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