fiveman 0.1.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 (136) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +11 -0
  3. data/bin/fiveman +7 -0
  4. data/bin/fiveman-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 +31 -0
  21. data/data/export/systemd/master.target.erb +5 -0
  22. data/data/export/systemd/process.service.erb +21 -0
  23. data/data/export/upstart/master.conf.erb +2 -0
  24. data/data/export/upstart/process.conf.erb +15 -0
  25. data/data/export/upstart/process_master.conf.erb +2 -0
  26. data/lib/fiveman/cli.rb +162 -0
  27. data/lib/fiveman/distribution.rb +9 -0
  28. data/lib/fiveman/engine/cli.rb +101 -0
  29. data/lib/fiveman/engine.rb +494 -0
  30. data/lib/fiveman/env.rb +29 -0
  31. data/lib/fiveman/export/base.rb +171 -0
  32. data/lib/fiveman/export/bluepill.rb +12 -0
  33. data/lib/fiveman/export/daemon.rb +28 -0
  34. data/lib/fiveman/export/inittab.rb +42 -0
  35. data/lib/fiveman/export/launchd.rb +22 -0
  36. data/lib/fiveman/export/runit.rb +34 -0
  37. data/lib/fiveman/export/supervisord.rb +16 -0
  38. data/lib/fiveman/export/systemd.rb +34 -0
  39. data/lib/fiveman/export/upstart.rb +46 -0
  40. data/lib/fiveman/export.rb +36 -0
  41. data/lib/fiveman/helpers.rb +45 -0
  42. data/lib/fiveman/process.rb +80 -0
  43. data/lib/fiveman/procfile.rb +94 -0
  44. data/lib/fiveman/vendor/thor/lib/thor/actions/create_file.rb +103 -0
  45. data/lib/fiveman/vendor/thor/lib/thor/actions/create_link.rb +59 -0
  46. data/lib/fiveman/vendor/thor/lib/thor/actions/directory.rb +118 -0
  47. data/lib/fiveman/vendor/thor/lib/thor/actions/empty_directory.rb +135 -0
  48. data/lib/fiveman/vendor/thor/lib/thor/actions/file_manipulation.rb +327 -0
  49. data/lib/fiveman/vendor/thor/lib/thor/actions/inject_into_file.rb +103 -0
  50. data/lib/fiveman/vendor/thor/lib/thor/actions.rb +318 -0
  51. data/lib/fiveman/vendor/thor/lib/thor/base.rb +656 -0
  52. data/lib/fiveman/vendor/thor/lib/thor/command.rb +133 -0
  53. data/lib/fiveman/vendor/thor/lib/thor/core_ext/hash_with_indifferent_access.rb +85 -0
  54. data/lib/fiveman/vendor/thor/lib/thor/core_ext/io_binary_read.rb +12 -0
  55. data/lib/fiveman/vendor/thor/lib/thor/core_ext/ordered_hash.rb +129 -0
  56. data/lib/fiveman/vendor/thor/lib/thor/error.rb +32 -0
  57. data/lib/fiveman/vendor/thor/lib/thor/group.rb +281 -0
  58. data/lib/fiveman/vendor/thor/lib/thor/invocation.rb +177 -0
  59. data/lib/fiveman/vendor/thor/lib/thor/line_editor/basic.rb +35 -0
  60. data/lib/fiveman/vendor/thor/lib/thor/line_editor/readline.rb +88 -0
  61. data/lib/fiveman/vendor/thor/lib/thor/line_editor.rb +17 -0
  62. data/lib/fiveman/vendor/thor/lib/thor/parser/argument.rb +70 -0
  63. data/lib/fiveman/vendor/thor/lib/thor/parser/arguments.rb +175 -0
  64. data/lib/fiveman/vendor/thor/lib/thor/parser/option.rb +146 -0
  65. data/lib/fiveman/vendor/thor/lib/thor/parser/options.rb +220 -0
  66. data/lib/fiveman/vendor/thor/lib/thor/parser.rb +4 -0
  67. data/lib/fiveman/vendor/thor/lib/thor/rake_compat.rb +71 -0
  68. data/lib/fiveman/vendor/thor/lib/thor/runner.rb +322 -0
  69. data/lib/fiveman/vendor/thor/lib/thor/shell/basic.rb +436 -0
  70. data/lib/fiveman/vendor/thor/lib/thor/shell/color.rb +149 -0
  71. data/lib/fiveman/vendor/thor/lib/thor/shell/html.rb +126 -0
  72. data/lib/fiveman/vendor/thor/lib/thor/shell.rb +81 -0
  73. data/lib/fiveman/vendor/thor/lib/thor/util.rb +268 -0
  74. data/lib/fiveman/vendor/thor/lib/thor/version.rb +3 -0
  75. data/lib/fiveman/vendor/thor/lib/thor.rb +492 -0
  76. data/lib/fiveman/version.rb +5 -0
  77. data/lib/fiveman.rb +17 -0
  78. data/man/fiveman.1 +284 -0
  79. data/spec/fiveman/cli_spec.rb +111 -0
  80. data/spec/fiveman/engine_spec.rb +114 -0
  81. data/spec/fiveman/export/base_spec.rb +19 -0
  82. data/spec/fiveman/export/bluepill_spec.rb +37 -0
  83. data/spec/fiveman/export/daemon_spec.rb +97 -0
  84. data/spec/fiveman/export/inittab_spec.rb +40 -0
  85. data/spec/fiveman/export/launchd_spec.rb +31 -0
  86. data/spec/fiveman/export/runit_spec.rb +36 -0
  87. data/spec/fiveman/export/supervisord_spec.rb +38 -0
  88. data/spec/fiveman/export/systemd_spec.rb +155 -0
  89. data/spec/fiveman/export/upstart_spec.rb +118 -0
  90. data/spec/fiveman/export_spec.rb +24 -0
  91. data/spec/fiveman/helpers_spec.rb +26 -0
  92. data/spec/fiveman/process_spec.rb +71 -0
  93. data/spec/fiveman/procfile_spec.rb +57 -0
  94. data/spec/fiveman_spec.rb +16 -0
  95. data/spec/helper_spec.rb +19 -0
  96. data/spec/resources/Procfile +5 -0
  97. data/spec/resources/Procfile.bad +2 -0
  98. data/spec/resources/bin/echo +2 -0
  99. data/spec/resources/bin/env +2 -0
  100. data/spec/resources/bin/test +2 -0
  101. data/spec/resources/bin/utf8 +2 -0
  102. data/spec/resources/export/bluepill/app-concurrency.pill +49 -0
  103. data/spec/resources/export/bluepill/app.pill +81 -0
  104. data/spec/resources/export/daemon/app-alpha-1.conf +7 -0
  105. data/spec/resources/export/daemon/app-alpha-2.conf +7 -0
  106. data/spec/resources/export/daemon/app-alpha.conf +2 -0
  107. data/spec/resources/export/daemon/app-bravo-1.conf +7 -0
  108. data/spec/resources/export/daemon/app-bravo.conf +2 -0
  109. data/spec/resources/export/daemon/app.conf +14 -0
  110. data/spec/resources/export/inittab/inittab.concurrency +4 -0
  111. data/spec/resources/export/inittab/inittab.default +6 -0
  112. data/spec/resources/export/launchd/launchd-a.default +29 -0
  113. data/spec/resources/export/launchd/launchd-b.default +29 -0
  114. data/spec/resources/export/launchd/launchd-c.default +30 -0
  115. data/spec/resources/export/runit/app-alpha-1/log/run +7 -0
  116. data/spec/resources/export/runit/app-alpha-1/run +4 -0
  117. data/spec/resources/export/runit/app-alpha-2/log/run +7 -0
  118. data/spec/resources/export/runit/app-alpha-2/run +4 -0
  119. data/spec/resources/export/runit/app-bravo-1/log/run +7 -0
  120. data/spec/resources/export/runit/app-bravo-1/run +4 -0
  121. data/spec/resources/export/supervisord/app-alpha-1.conf +42 -0
  122. data/spec/resources/export/supervisord/app-alpha-2.conf +22 -0
  123. data/spec/resources/export/systemd/app-alpha.1.service +18 -0
  124. data/spec/resources/export/systemd/app-alpha.2.service +18 -0
  125. data/spec/resources/export/systemd/app-alpha.target +2 -0
  126. data/spec/resources/export/systemd/app-bravo.1.service +18 -0
  127. data/spec/resources/export/systemd/app-bravo.target +2 -0
  128. data/spec/resources/export/systemd/app.target +5 -0
  129. data/spec/resources/export/upstart/app-alpha-1.conf +11 -0
  130. data/spec/resources/export/upstart/app-alpha-2.conf +11 -0
  131. data/spec/resources/export/upstart/app-alpha.conf +2 -0
  132. data/spec/resources/export/upstart/app-bravo-1.conf +11 -0
  133. data/spec/resources/export/upstart/app-bravo.conf +2 -0
  134. data/spec/resources/export/upstart/app.conf +2 -0
  135. data/spec/spec_helper.rb +177 -0
  136. metadata +177 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 5da022efab06e93613061a457c76c4382c679274f39768725a64f2951c22a61e
4
+ data.tar.gz: 92500760912ab6f866aba84b5111d71b64250632726a00074f363fc91f964eee
5
+ SHA512:
6
+ metadata.gz: 1e4d0704ccb1f5d5b33404adc3fb27fe07bb272ecffbe946f1d5ea15701d68da3142a74d07a8e9fc8513dd06f2ee9d7939bac3eb25f7765e1e41a0c84e5f5649
7
+ data.tar.gz: 9c7a6ed669e551237e67a56c5f0b957ed66c29b21f4d753be63b699e2a7cfb42b659ebd19183a96fa5bf9ac6f1d63c841f3e1fa378056976e2838912cf2eefe9
data/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Fiveman
2
+
3
+ > This project is a fork of [foreman](https://github.com/ddollar/foreman) because I don't like how it looks.
4
+
5
+ Manage Procfile-based applications
6
+
7
+ ## Installation
8
+
9
+ $ gem install fiveman
10
+
11
+ Ruby users should take care *not* to install fiveman in their project's `Gemfile`. See this [wiki article](https://github.com/ddollar/fiveman/wiki/Don't-Bundle-Fiveman) for more details.
data/bin/fiveman ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.expand_path("../../lib", __FILE__)
4
+
5
+ require "fiveman/cli"
6
+
7
+ Fiveman::CLI.start
@@ -0,0 +1,41 @@
1
+ #!/bin/sh
2
+ #
3
+ #/ Usage: fiveman-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 %>=<%= 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 %></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,31 @@
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
+ stdout_logfile=<%= log %>/<%= name %>-<%= num %>.log
21
+ stderr_logfile=<%= log %>/<%= name %>-<%= num %>.error.log
22
+ user=<%= user %>
23
+ directory=<%= engine.root %>
24
+ environment=<%= environment.join(',') %>
25
+
26
+ <%
27
+ end
28
+ end
29
+ -%>
30
+ [group:<%= app %>]
31
+ programs=<%= app_names.join(',') %>
@@ -0,0 +1,5 @@
1
+ [Unit]
2
+ Wants=<%= service_names.join(' ') %>
3
+
4
+ [Install]
5
+ WantedBy=multi-user.target
@@ -0,0 +1,21 @@
1
+ [Unit]
2
+ PartOf=<%= app %>.target
3
+ StopWhenUnneeded=yes
4
+
5
+ [Service]
6
+ User=<%= user %>
7
+ WorkingDirectory=<%= engine.root %>
8
+ Environment=PORT=<%= port %>
9
+ Environment=PS=<%= process_name %>
10
+ <% engine.env.each_pair do |var,env| -%>
11
+ Environment="<%= var %>=<%= env %>"
12
+ <% end -%>
13
+ ExecStart=/bin/bash -lc 'exec -a "<%= app %>-<%= process_name %>" <%= process.command %>'
14
+ Restart=always
15
+ RestartSec=14s
16
+ StandardInput=null
17
+ StandardOutput=syslog
18
+ StandardError=syslog
19
+ SyslogIdentifier=%n
20
+ KillMode=mixed
21
+ TimeoutStopSec=<%= engine.options[:timeout] %>
@@ -0,0 +1,2 @@
1
+ start on runlevel [2345]
2
+ stop on runlevel [!2345]
@@ -0,0 +1,15 @@
1
+ start on starting <%= app %>-<%= name %>
2
+ stop on stopping <%= app %>-<%= name %>
3
+ respawn
4
+
5
+ env PORT=<%= port %>
6
+ <% engine.env.each do |name,value| -%>
7
+ <% next if name.upcase == "PORT" -%>
8
+ env <%= name %>='<%= value.gsub(/'/, "'\"'\"'") %>'
9
+ <% end -%>
10
+
11
+ setuid <%= user %>
12
+
13
+ chdir <%= engine.root %>
14
+
15
+ exec <%= process.command %>
@@ -0,0 +1,2 @@
1
+ start on starting <%= app %>
2
+ stop on stopping <%= app %>
@@ -0,0 +1,162 @@
1
+ require "fiveman"
2
+ require "fiveman/helpers"
3
+ require "fiveman/engine"
4
+ require "fiveman/engine/cli"
5
+ require "fiveman/export"
6
+ require "fiveman/version"
7
+ require "shellwords"
8
+ require "yaml"
9
+ require "fiveman/vendor/thor/lib/thor"
10
+
11
+ class Fiveman::CLI < Fiveman::Thor
12
+
13
+ include Fiveman::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
+ method_option :timestamp, :type => :boolean, :default => true, :desc => "Include timestamp in output"
28
+
29
+ class << self
30
+ # Hackery. Take the run method away from Thor so that we can redefine it.
31
+ def is_thor_reserved_word?(word, type)
32
+ return false if word == "run"
33
+ super
34
+ end
35
+ end
36
+
37
+ def start(process=nil)
38
+ check_procfile!
39
+ load_environment!
40
+ engine.load_procfile(procfile)
41
+ engine.options[:formation] = "#{process}=1" if process
42
+ engine.start
43
+ end
44
+
45
+ desc "export FORMAT LOCATION", "Export the application to another process management format"
46
+
47
+ method_option :app, :type => :string, :aliases => "-a"
48
+ method_option :log, :type => :string, :aliases => "-l"
49
+ method_option :run, :type => :string, :aliases => "-r", :desc => "Specify the pid file directory, defaults to /var/run/<application>"
50
+ method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
51
+ method_option :port, :type => :numeric, :aliases => "-p"
52
+ method_option :user, :type => :string, :aliases => "-u"
53
+ method_option :template, :type => :string, :aliases => "-t"
54
+ method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"', :desc => 'Specify what processes will run and how many. Default: "all=1"'
55
+ 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."
56
+
57
+ def export(format, location=nil)
58
+ check_procfile!
59
+ load_environment!
60
+ engine.load_procfile(procfile)
61
+ formatter = Fiveman::Export.formatter(format)
62
+ formatter.new(location, engine, options).export
63
+ rescue Fiveman::Export::Exception => ex
64
+ error ex.message
65
+ end
66
+
67
+ desc "check", "Validate your application's Procfile"
68
+
69
+ def check
70
+ check_procfile!
71
+ engine.load_procfile(procfile)
72
+ error "no processes defined" unless engine.processes.length > 0
73
+ puts "valid procfile detected (#{engine.process_names.join(', ')})"
74
+ end
75
+
76
+ desc "run COMMAND [ARGS...]", "Run a command using your application's environment"
77
+
78
+ method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
79
+ stop_on_unknown_option! :run
80
+
81
+ def run(*args)
82
+ load_environment!
83
+
84
+ if File.file?(procfile)
85
+ engine.load_procfile(procfile)
86
+ end
87
+
88
+ pid = fork do
89
+ begin
90
+ engine.env.each { |k,v| ENV[k] = v }
91
+ if args.size == 1 && process = engine.process(args.first)
92
+ process.exec(:env => engine.env)
93
+ else
94
+ exec args.shelljoin
95
+ end
96
+ rescue Errno::EACCES
97
+ error "not executable: #{args.first}"
98
+ rescue Errno::ENOENT
99
+ error "command not found: #{args.first}"
100
+ end
101
+ end
102
+ trap("INT") do
103
+ Process.kill(:INT, pid)
104
+ end
105
+ Process.wait(pid)
106
+ exit $?.exitstatus || 0
107
+ rescue Interrupt
108
+ end
109
+
110
+ desc "version", "Display Fiveman gem version"
111
+
112
+ def version
113
+ puts Fiveman::VERSION
114
+ end
115
+
116
+ no_tasks do
117
+ def engine
118
+ @engine ||= begin
119
+ engine_class = Fiveman::Engine::CLI
120
+ engine = engine_class.new(options)
121
+ engine
122
+ end
123
+ end
124
+ end
125
+
126
+ private ######################################################################
127
+
128
+ def error(message)
129
+ puts "ERROR: #{message}"
130
+ exit 1
131
+ end
132
+
133
+ def check_procfile!
134
+ error("#{procfile} does not exist.") unless File.file?(procfile)
135
+ end
136
+
137
+ def load_environment!
138
+ if options[:env]
139
+ options[:env].split(",").each do |file|
140
+ engine.load_env file
141
+ end
142
+ else
143
+ default_env = File.join(engine.root, ".env")
144
+ engine.load_env default_env if File.file?(default_env)
145
+ end
146
+ end
147
+
148
+ def procfile
149
+ case
150
+ when options[:procfile] then options[:procfile]
151
+ when options[:root] then File.expand_path(File.join(options[:root], "Procfile"))
152
+ else "Procfile"
153
+ end
154
+ end
155
+
156
+ def options
157
+ original_options = super
158
+ return original_options unless File.file?(".fiveman")
159
+ defaults = ::YAML::load_file(".fiveman") || {}
160
+ Fiveman::Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
161
+ end
162
+ end
@@ -0,0 +1,9 @@
1
+ module Fiveman
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,101 @@
1
+ require "fiveman/engine"
2
+
3
+ class Fiveman::Engine::CLI < Fiveman::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 Fiveman.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
+ FIVEMAN_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 "fiveman: master" unless Fiveman.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 = $stdout.color(:reset)
59
+ output += message
60
+ $stdout.puts output
61
+ $stdout.flush
62
+ end
63
+ rescue Errno::EPIPE
64
+ terminate_gracefully
65
+ end
66
+
67
+ def shutdown
68
+ end
69
+
70
+ private
71
+
72
+ def name_padding
73
+ @name_padding ||= begin
74
+ index_padding = @names.values.map { |n| formation[n] }.max.to_s.length + 1
75
+ name_padding = @names.values.map { |n| n.length + index_padding }.sort.last
76
+ [ 6, name_padding ].max
77
+ end
78
+ end
79
+
80
+ def pad_process_name(name)
81
+ name.ljust(name_padding, " ")
82
+ end
83
+
84
+ def map_colors
85
+ colors = Hash.new("white")
86
+ @names.values.each_with_index do |name, index|
87
+ colors[name] = FIVEMAN_COLORS[index % FIVEMAN_COLORS.length]
88
+ end
89
+ colors["system"] = "bright_white"
90
+ colors
91
+ end
92
+
93
+ def proctitle(title)
94
+ $0 = title
95
+ end
96
+
97
+ def termtitle(title)
98
+ printf("\033]0;#{title}\007") unless Fiveman.windows?
99
+ end
100
+
101
+ end