jefe 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # [Gaffer](http://en.wikipedia.org/wiki/Gaffer_(boss)), the featherweight Procfile manager
2
+
3
+ Through the magic of [Thor](https://github.com/wycats/thor) and [EventMachine](http://rubyeventmachine.com/), we give you the equivalent to [Foreman](https://github.com/ddollar/foreman) in just 200 lines of sweet unadulterated Ruby.
data/bin/jefe ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'jefe/cli'
4
+ Jefe::CLI.start
data/lib/jefe/cli.rb ADDED
@@ -0,0 +1,66 @@
1
+ require 'jefe'
2
+ require 'jefe/version'
3
+
4
+ class Jefe::CLI < Thor
5
+
6
+ class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
7
+
8
+ desc "start [COMMAND...]", "Start the application"
9
+
10
+ method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
11
+ method_option :port, :type => :numeric, :aliases => "-p"
12
+ method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
13
+
14
+ def start(*args)
15
+ error("#{procfile} does not exist") unless File.exists? procfile
16
+
17
+ engine = Jefe.new
18
+ engine.load File.read(procfile)
19
+ engine.printer = Jefe::ColorPrinter.new
20
+ engine.backend = Jefe::EM.new(engine.printer)
21
+
22
+ names = args.empty? ? engine.process_types.keys : args
23
+ trap("INT") do
24
+ puts
25
+ engine.stop
26
+ end
27
+ engine.start concurrency(names), port
28
+ end
29
+
30
+ def help(*args)
31
+ puts "Jefe #{Jefe::VERSION}, the featherweight Procfile manager"
32
+ puts
33
+ super
34
+ end
35
+
36
+ private
37
+
38
+ def port
39
+ options[:port] || 5000
40
+ end
41
+
42
+ def concurrency names
43
+ if options[:concurrency]
44
+ options[:concurrency].split(",").reduce({}) do |tot, kv|
45
+ k, v = kv.split "="
46
+ tot[k] = v.to_i
47
+ tot
48
+ end
49
+ else
50
+ names.reduce({}) do |tot, name|
51
+ tot[name] = 1
52
+ tot
53
+ end
54
+ end
55
+ end
56
+
57
+ def procfile
58
+ options[:procfile] || "Procfile"
59
+ end
60
+
61
+ def error(message)
62
+ puts "ERROR: #{message}"
63
+ exit 1
64
+ end
65
+
66
+ end
@@ -0,0 +1 @@
1
+ Jefe::VERSION = "1.0.0"
data/lib/jefe.rb ADDED
@@ -0,0 +1,135 @@
1
+ require 'eventmachine'
2
+ require 'pty'
3
+ require 'term/ansicolor'
4
+ require 'thor'
5
+
6
+ class Jefe
7
+ def initialize
8
+ @process_types = {}
9
+ end
10
+ attr_accessor :backend, :printer, :process_types
11
+ def load file
12
+ file.split("\n").each do |line|
13
+ if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
14
+ add_process_type $1, $2
15
+ end
16
+ end
17
+ end
18
+ def add_process_type name, command
19
+ @process_types[name] = command
20
+ end
21
+
22
+ def start concurrency, port
23
+ processes = []
24
+ concurrency.each do |(name, num)|
25
+ num.times do |i|
26
+ env = { "PORT" => (port + i) }
27
+ command = self.process_types[name].gsub(/\$(\w+)/) { env[$1] || ENV[$1] }
28
+ processes.push ["#{name}.#{i}", command]
29
+ end
30
+ port += 100
31
+ end
32
+
33
+ self.printer.out "system", "starting"
34
+ self.backend.start do |b|
35
+ processes.each do |(name, command)|
36
+ self.printer.out name, "starting #{command}"
37
+ b.add name, command
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ def stop
44
+ self.printer.out "system", "stopping"
45
+ self.backend.stop
46
+ end
47
+ end
48
+
49
+ class Jefe::ColorPrinter
50
+ Color = Term::ANSIColor
51
+ COLORS = [:cyan, :yellow, :green, :magenta, :red]
52
+ def initialize
53
+ @colors ||= {"system" => :white}
54
+ end
55
+ def out name, command
56
+ type = name.match(/^([A-Za-z0-9_]+).\d+$/) ? $1 : name
57
+ @colors[type] = COLORS.shift.tap {|c| COLORS.push c} unless @colors[type]
58
+ color = @colors[type]
59
+ puts "#{Color.send color} #{Time.now.strftime '%H:%M:%S'} #{name}#{' ' * (9 - name.length)} | #{Color.reset}#{command.chomp}"
60
+ end
61
+ end
62
+
63
+ class Jefe::EM
64
+ def initialize(printer)
65
+ @printer = printer
66
+ @connections = []
67
+ end
68
+
69
+ def start
70
+ EM.run do
71
+ yield self
72
+ end
73
+ end
74
+
75
+ def add name, command
76
+ PTY.spawn(command) do |output, input, pid|
77
+ input.close
78
+
79
+ EM.attach output, ProcessHandler do |c|
80
+ c.init name, pid, @printer
81
+ bind(c)
82
+ end
83
+ end
84
+ end
85
+
86
+ def stop
87
+ @connections.each &:stop
88
+ EM.stop
89
+ end
90
+
91
+ def bind c
92
+ @connections.push c
93
+ c.engine = self
94
+ end
95
+
96
+ def unbind c
97
+ @connections.delete c
98
+ stop if @connections.empty?
99
+ end
100
+
101
+ class ProcessHandler < EM::Connection
102
+ include EM::Protocols::LineText2
103
+ attr_accessor :engine, :name, :pid, :printer
104
+
105
+ def init name, pid, printer
106
+ self.name = name
107
+ self.pid = pid
108
+ self.printer = printer
109
+
110
+ out "started with pid #{self.pid}"
111
+ end
112
+
113
+ def receive_line data
114
+ out data
115
+ end
116
+
117
+ def unbind
118
+ out "process terminated"
119
+ self.engine.unbind self
120
+ end
121
+
122
+ def out msg
123
+ self.printer.out self.name, msg
124
+ end
125
+
126
+ def to_s
127
+ "#{self.name} in pid #{self.pid}"
128
+ end
129
+
130
+ def stop
131
+ self.printer.out "system", "killing #{self}"
132
+ Process.kill("INT", self.pid)
133
+ end
134
+ end
135
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jefe
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease:
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Michael Maltese
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2012-01-14 00:00:00 -08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: eventmachine
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 3
30
+ segments:
31
+ - 0
32
+ version: "0"
33
+ type: :runtime
34
+ version_requirements: *id001
35
+ - !ruby/object:Gem::Dependency
36
+ name: term-ansicolor
37
+ prerelease: false
38
+ requirement: &id002 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ hash: 3
44
+ segments:
45
+ - 0
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: thor
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :runtime
62
+ version_requirements: *id003
63
+ description: Through the magic of Thor and EventMachine, we give you the equivalent to Foreman in just 200 lines of sweet unadulterated Ruby.
64
+ email: michael.maltese@pomona.edu
65
+ executables:
66
+ - jefe
67
+ extensions: []
68
+
69
+ extra_rdoc_files: []
70
+
71
+ files:
72
+ - lib/jefe/cli.rb
73
+ - lib/jefe/version.rb
74
+ - lib/jefe.rb
75
+ - bin/jefe
76
+ - README.md
77
+ has_rdoc: true
78
+ homepage: http://github.com/mikemaltese/jefe
79
+ licenses: []
80
+
81
+ post_install_message:
82
+ rdoc_options: []
83
+
84
+ require_paths:
85
+ - - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ hash: 3
92
+ segments:
93
+ - 0
94
+ version: "0"
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ none: false
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ hash: 3
101
+ segments:
102
+ - 0
103
+ version: "0"
104
+ requirements: []
105
+
106
+ rubyforge_project:
107
+ rubygems_version: 1.3.9.3
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: The featherweight Procfile manager
111
+ test_files: []
112
+