foreman 0.27.0 → 0.28.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
data/bin/runner ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ exec $1 2>&1
@@ -3,7 +3,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
3
3
  app.uid = "<%= user %>"
4
4
  app.gid = "<%= user %>"
5
5
 
6
- <% engine.processes.each do |process| %>
6
+ <% engine.procfile.entries.each do |process| %>
7
7
  <% 1.upto(concurrency[process.name]) do |num| %>
8
8
  <% port = engine.port_for(process, num, options[:port]) %>
9
9
  app.process("<%= process.name %>-<%=num%>") do |process|
@@ -19,7 +19,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
19
19
  process.monitor_children do |children|
20
20
  children.stop_command "kill -QUIT {{PID}}"
21
21
  end
22
-
22
+
23
23
  process.group = "<%= app %>-<%= process.name %>"
24
24
  end
25
25
  <% end %>
data/lib/foreman.rb CHANGED
@@ -9,5 +9,10 @@ module Foreman
9
9
  require 'foreman/engine'
10
10
  Foreman::Engine.load_env!(env_file)
11
11
  end
12
+
13
+ def self.runner
14
+ File.expand_path("../../bin/runner", __FILE__)
15
+ end
16
+
12
17
  end
13
18
 
data/lib/foreman/cli.rb CHANGED
@@ -8,20 +8,15 @@ class Foreman::CLI < Thor
8
8
 
9
9
  class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
10
10
 
11
- desc "start [PROCESS]", "Start the application, or a specific process"
11
+ desc "start", "Start the application"
12
12
 
13
13
  method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
14
14
  method_option :port, :type => :numeric, :aliases => "-p"
15
15
  method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
16
16
 
17
- def start(process=nil)
17
+ def start
18
18
  check_procfile!
19
-
20
- if process
21
- engine.execute(process)
22
- else
23
- engine.start
24
- end
19
+ engine.start
25
20
  end
26
21
 
27
22
  desc "export FORMAT LOCATION", "Export the application to another process management format"
@@ -55,8 +50,8 @@ class Foreman::CLI < Thor
55
50
  desc "check", "Validate your application's Procfile"
56
51
 
57
52
  def check
58
- error "no processes defined" unless engine.processes.length > 0
59
- display "valid procfile detected (#{engine.processes.map(&:name).join(', ')})"
53
+ error "no processes defined" unless engine.procfile.entries.length > 0
54
+ display "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
60
55
  end
61
56
 
62
57
  private ######################################################################
@@ -23,6 +23,7 @@ class Foreman::Engine
23
23
  @directory = File.expand_path(File.dirname(procfile))
24
24
  @options = options
25
25
  @environment = read_environment_files(options[:env])
26
+ @output_mutex = Mutex.new
26
27
  end
27
28
 
28
29
  def self.load_env!(env_file)
@@ -32,34 +33,17 @@ class Foreman::Engine
32
33
 
33
34
  def start
34
35
  proctitle "ruby: foreman master"
35
- termtitle "#{File.basename(@directory)} - foreman (#{processes.size} processes)"
36
-
37
- processes.each do |process|
38
- process.color = next_color
39
- fork process
40
- end
41
-
42
- trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
43
- trap("INT") { puts "SIGINT received"; terminate_gracefully }
44
-
45
- watch_for_termination
46
- end
47
-
48
- def execute(name)
49
- error "no such process: #{name}" unless process = procfile[name]
50
- process.color = next_color
51
- fork process
36
+ termtitle "#{File.basename(@directory)} - foreman"
52
37
 
53
38
  trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
54
39
  trap("INT") { puts "SIGINT received"; terminate_gracefully }
55
40
 
41
+ assign_colors
42
+ spawn_processes
43
+ watch_for_output
56
44
  watch_for_termination
57
45
  end
58
46
 
59
- def processes
60
- procfile.processes
61
- end
62
-
63
47
  def port_for(process, num, base_port=nil)
64
48
  base_port ||= 5000
65
49
  offset = procfile.process_names.index(process.name) * 100
@@ -68,47 +52,20 @@ class Foreman::Engine
68
52
 
69
53
  private ######################################################################
70
54
 
71
- def fork(process)
55
+ def spawn_processes
72
56
  concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
73
57
 
74
- 1.upto(concurrency[process.name]) do |num|
75
- fork_individual(process, num, port_for(process, num, @options[:port]))
76
- end
77
- end
78
-
79
- def fork_individual(process, num, port)
80
- apply_environment!
81
-
82
- ENV["PORT"] = port.to_s
83
- ENV["PS"] = "#{process.name}.#{num}"
84
-
85
- pid = Process.fork do
86
- run(process)
58
+ procfile.entries.each do |entry|
59
+ reader, writer = IO.pipe
60
+ entry.spawn(concurrency[entry.name], writer, @directory, @environment, base_port).each do |process|
61
+ running_processes[process.pid] = process
62
+ readers[process] = reader
63
+ end
87
64
  end
88
-
89
- info "started with pid #{pid}", process
90
- running_processes[pid] = process
91
65
  end
92
66
 
93
- def run(process)
94
- proctitle "ruby: foreman #{process.name}"
95
- trap("SIGINT", "IGNORE")
96
-
97
- begin
98
- Dir.chdir directory do
99
- PTY.spawn(process.command) do |stdin, stdout, pid|
100
- trap("SIGTERM") { Process.kill("SIGTERM", pid) }
101
- until stdin.eof?
102
- info stdin.gets, process
103
- end
104
- end
105
- end
106
- rescue PTY::ChildExited, Interrupt, Errno::EIO, Errno::ENOENT
107
- begin
108
- info "process exiting", process
109
- rescue Interrupt
110
- end
111
- end
67
+ def base_port
68
+ options[:port] || 5000
112
69
  end
113
70
 
114
71
  def kill_all(signal="SIGTERM")
@@ -120,29 +77,59 @@ private ######################################################################
120
77
  def terminate_gracefully
121
78
  info "sending SIGTERM to all processes"
122
79
  kill_all "SIGTERM"
123
- Timeout.timeout(3) { Process.waitall }
80
+ Timeout.timeout(5) { Process.waitall }
124
81
  rescue Timeout::Error
125
82
  info "sending SIGKILL to all processes"
126
83
  kill_all "SIGKILL"
127
84
  end
128
85
 
86
+ def watch_for_output
87
+ Thread.new do
88
+ begin
89
+ loop do
90
+ rs, ws = IO.select(readers.values, [], [], 1)
91
+ (rs || []).each do |r|
92
+ ps, message = r.gets.split(",", 2)
93
+ color = colors[ps.split(".").first]
94
+ info message, ps, color
95
+ end
96
+ end
97
+ rescue Exception => ex
98
+ puts ex.message
99
+ puts ex.backtrace
100
+ end
101
+ end
102
+ end
103
+
129
104
  def watch_for_termination
130
105
  pid, status = Process.wait2
131
106
  process = running_processes.delete(pid)
132
- info "process terminated", process
107
+ info "process terminated", process.name
133
108
  terminate_gracefully
134
109
  kill_all
135
110
  rescue Errno::ECHILD
136
111
  end
137
112
 
138
- def info(message, process=nil)
139
- print process.color if process
140
- print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(process)} | "
113
+ def info(message, name="system", color=Term::ANSIColor.white)
114
+ print color
115
+ print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
141
116
  print Term::ANSIColor.reset
142
117
  print message.chomp
143
118
  puts
144
119
  end
145
120
 
121
+ def print(message=nil)
122
+ @output_mutex.synchronize do
123
+ $stdout.print message
124
+ end
125
+ end
126
+
127
+ def puts(message=nil)
128
+ @output_mutex.synchronize do
129
+ $stdout.puts message
130
+ end
131
+ end
132
+
146
133
  def error(message)
147
134
  puts "ERROR: #{message}"
148
135
  exit 1
@@ -156,9 +143,8 @@ private ######################################################################
156
143
  end
157
144
  end
158
145
 
159
- def pad_process_name(process)
160
- name = process ? "#{ENV["PS"]}" : "system"
161
- name.ljust(longest_process_name + 3) # add 3 for process number padding
146
+ def pad_process_name(name="system")
147
+ name.to_s.ljust(longest_process_name + 3) # add 3 for process number padding
162
148
  end
163
149
 
164
150
  def proctitle(title)
@@ -173,6 +159,24 @@ private ######################################################################
173
159
  @running_processes ||= {}
174
160
  end
175
161
 
162
+ def readers
163
+ @readers ||= {}
164
+ end
165
+
166
+ def colors
167
+ @colors ||= {}
168
+ end
169
+
170
+ def assign_colors
171
+ procfile.entries.each do |entry|
172
+ colors[entry.name] = next_color
173
+ end
174
+ end
175
+
176
+ def process_by_reader(reader)
177
+ readers.invert[reader]
178
+ end
179
+
176
180
  def next_color
177
181
  @current_color ||= -1
178
182
  @current_color += 1
@@ -12,7 +12,7 @@ class Foreman::Export::Inittab < Foreman::Export::Base
12
12
  inittab = []
13
13
  inittab << "# ----- foreman #{app} processes -----"
14
14
 
15
- engine.processes.inject(1) do |index, process|
15
+ engine.procfile.entries.inject(1) do |index, process|
16
16
  1.upto(concurrency[process.name]) do |num|
17
17
  id = app.slice(0, 2).upcase + sprintf("%02d", index)
18
18
  port = engine.port_for(process, num, options[:port])
@@ -3,58 +3,58 @@ require "foreman/export"
3
3
 
4
4
  class Foreman::Export::Runit < Foreman::Export::Base
5
5
  ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
6
-
6
+
7
7
  def export(location, options={})
8
8
  error("Must specify a location") unless location
9
-
9
+
10
10
  app = options[:app] || File.basename(engine.directory)
11
11
  user = options[:user] || app
12
12
  log_root = options[:log] || "/var/log/#{app}"
13
13
  template_root = options[:template]
14
-
14
+
15
15
  concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
16
-
16
+
17
17
  run_template = export_template('runit', 'run.erb', template_root)
18
18
  log_run_template = export_template('runit', 'log_run.erb', template_root)
19
19
 
20
- engine.processes.each do |process|
20
+ engine.procfile.entries.each do |process|
21
21
  1.upto(concurrency[process.name]) do |num|
22
22
  process_directory = "#{location}/#{app}-#{process.name}-#{num}"
23
23
  process_env_directory = "#{process_directory}/env"
24
24
  process_log_directory = "#{process_directory}/log"
25
-
25
+
26
26
  create_directory process_directory
27
27
  create_directory process_env_directory
28
28
  create_directory process_log_directory
29
-
29
+
30
30
  run = ERB.new(run_template).result(binding)
31
31
  write_file "#{process_directory}/run", run
32
-
32
+
33
33
  port = engine.port_for(process, num, options[:port])
34
34
  environment_variables = {'PORT' => port}.
35
35
  merge(engine.environment).
36
36
  merge(inline_variables(process.command))
37
-
37
+
38
38
  environment_variables.each_pair do |var, env|
39
39
  write_file "#{process_env_directory}/#{var.upcase}", env
40
40
  end
41
-
41
+
42
42
  log_run = ERB.new(log_run_template).result(binding)
43
43
  write_file "#{process_log_directory}/run", log_run
44
-
44
+
45
45
  end
46
46
  end
47
-
47
+
48
48
  end
49
-
49
+
50
50
  private
51
51
  def create_directory(location)
52
52
  say "creating: #{location}"
53
53
  FileUtils.mkdir(location)
54
54
  end
55
-
55
+
56
56
  def inline_variables(command)
57
- variable_name_regex =
57
+ variable_name_regex =
58
58
  Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
59
59
  end
60
- end
60
+ end
@@ -26,7 +26,7 @@ class Foreman::Export::Upstart < Foreman::Export::Base
26
26
 
27
27
  process_template = export_template("upstart", "process.conf.erb", template_root)
28
28
 
29
- engine.processes.each do |process|
29
+ engine.procfile.entries.each do |process|
30
30
  next if (conc = concurrency[process.name]) < 1
31
31
  process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
32
32
  process_master_config = ERB.new(process_master_template).result(binding)
@@ -2,13 +2,58 @@ require "foreman"
2
2
 
3
3
  class Foreman::Process
4
4
 
5
- attr_reader :name
6
- attr_reader :command
7
- attr_accessor :color
5
+ attr_reader :entry
6
+ attr_reader :num
7
+ attr_reader :pid
8
+ attr_reader :port
8
9
 
9
- def initialize(name, command)
10
- @name = name
11
- @command = command
10
+ def initialize(entry, num, port)
11
+ @entry = entry
12
+ @num = num
13
+ @port = port
14
+ end
15
+
16
+ def run(pipe, basedir, environment)
17
+ Dir.chdir(basedir) do
18
+ with_environment(environment.merge("PORT" => port.to_s)) do
19
+ run_process entry.command
20
+ end
21
+ end
22
+ end
23
+
24
+ def name
25
+ "%s.%s" % [ entry.name, num ]
26
+ end
27
+
28
+ private
29
+
30
+ def run_process(command)
31
+ io = IO.popen([Foreman.runner, replace_command_env(command)], "w+")
32
+ @pid = io.pid
33
+ trap("SIGTERM") { "got sigterm for %d" % @pid }
34
+ output pipe, "started with pid %d" % @pid
35
+ Thread.new do
36
+ until io.eof?
37
+ output pipe, io.gets
38
+ end
39
+ end
40
+ end
41
+
42
+ def output(pipe, message)
43
+ pipe.puts "%s,%s" % [ name, message ]
44
+ end
45
+
46
+ def replace_command_env(command)
47
+ command.gsub(/\$(\w+)/) { |e| ENV[e[1..-1]] }
48
+ end
49
+
50
+ def with_environment(environment)
51
+ old_env = ENV.each_pair.inject({}) { |h,(k,v)| h.update(k => v) }
52
+ environment.each { |k,v| ENV[k] = v }
53
+ ret = yield
54
+ ENV.clear
55
+ old_env.each { |k,v| ENV[k] = v}
56
+ ret
12
57
  end
13
58
 
14
59
  end
@@ -1,4 +1,5 @@
1
1
  require "foreman"
2
+ require "foreman/procfile_entry"
2
3
 
3
4
  # A valid Procfile entry is captured by this regex.
4
5
  # All other lines are ignored.
@@ -10,18 +11,18 @@ require "foreman"
10
11
  #
11
12
  class Foreman::Procfile
12
13
 
13
- attr_reader :processes
14
+ attr_reader :entries
14
15
 
15
16
  def initialize(filename)
16
- @processes = parse_procfile(filename)
17
+ @entries = parse_procfile(filename)
17
18
  end
18
19
 
19
- def process_names
20
- processes.map(&:name)
20
+ def [](name)
21
+ entries.detect { |entry| entry.name == name }
21
22
  end
22
23
 
23
- def [](name)
24
- processes.detect { |process| process.name == name }
24
+ def process_names
25
+ entries.map(&:name)
25
26
  end
26
27
 
27
28
  private
@@ -29,7 +30,7 @@ private
29
30
  def parse_procfile(filename)
30
31
  File.read(filename).split("\n").map do |line|
31
32
  if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
32
- Foreman::Process.new($1, $2)
33
+ Foreman::ProcfileEntry.new($1, $2)
33
34
  end
34
35
  end.compact
35
36
  end
@@ -0,0 +1,22 @@
1
+ require "foreman"
2
+
3
+ class Foreman::ProcfileEntry
4
+
5
+ attr_reader :name
6
+ attr_reader :command
7
+ attr_accessor :color
8
+
9
+ def initialize(name, command)
10
+ @name = name
11
+ @command = command
12
+ end
13
+
14
+ def spawn(num, pipe, basedir, environment, base_port)
15
+ (1..num).to_a.map do |n|
16
+ process = Foreman::Process.new(self, n, base_port + (n-1))
17
+ process.run(pipe, basedir, environment)
18
+ process
19
+ end
20
+ end
21
+
22
+ end
@@ -1,5 +1,5 @@
1
1
  module Foreman
2
2
 
3
- VERSION = "0.27.0"
3
+ VERSION = "0.28.0.pre1"
4
4
 
5
5
  end
@@ -24,8 +24,9 @@ describe "Foreman::Engine" do
24
24
  describe "start" do
25
25
  it "forks the processes" do
26
26
  write_procfile
27
- mock(subject).fork(subject.procfile["alpha"])
28
- mock(subject).fork(subject.procfile["bravo"])
27
+ mock.instance_of(Foreman::Process).run_process("./alpha")
28
+ mock.instance_of(Foreman::Process).run_process("./bravo")
29
+ mock(subject).watch_for_output
29
30
  mock(subject).watch_for_termination
30
31
  subject.start
31
32
  end
@@ -33,29 +34,14 @@ describe "Foreman::Engine" do
33
34
  it "handles concurrency" do
34
35
  write_procfile
35
36
  engine = Foreman::Engine.new("Procfile",:concurrency => "alpha=2")
36
- mock(engine).fork_individual(engine.procfile["alpha"], 1, 5000)
37
- mock(engine).fork_individual(engine.procfile["alpha"], 2, 5001)
38
- mock(engine).fork_individual(engine.procfile["bravo"], 1, 5100)
37
+ mock.instance_of(Foreman::Process).run_process("./alpha").twice
38
+ mock.instance_of(Foreman::Process).run_process("./bravo")
39
+ mock(engine).watch_for_output
39
40
  mock(engine).watch_for_termination
40
41
  engine.start
41
42
  end
42
43
  end
43
44
 
44
- describe "execute" do
45
- it "runs the processes" do
46
- write_procfile
47
- mock(subject).fork(subject.procfile["alpha"])
48
- mock(subject).watch_for_termination
49
- subject.execute("alpha")
50
- end
51
-
52
- it "shows an error running a process that doesnt exist" do
53
- write_procfile
54
- mock(subject).puts("ERROR: no such process: foo")
55
- lambda { subject.execute("foo") }.should raise_error(SystemExit)
56
- end
57
- end
58
-
59
45
  describe "environment" do
60
46
  before(:each) do
61
47
  write_procfile
@@ -66,9 +52,10 @@ describe "Foreman::Engine" do
66
52
  File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
67
53
  engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
68
54
  stub(engine).info
55
+ mock(engine).spawn_processes
69
56
  mock(engine).watch_for_termination
70
57
  engine.environment.should == {"FOO"=>"baz"}
71
- engine.execute("alpha")
58
+ engine.start
72
59
  end
73
60
 
74
61
  it "should read more than one if specified" do
@@ -76,9 +63,10 @@ describe "Foreman::Engine" do
76
63
  File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") }
77
64
  engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2")
78
65
  stub(engine).info
66
+ mock(engine).spawn_processes
79
67
  mock(engine).watch_for_termination
80
68
  engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" }
81
- engine.execute("alpha")
69
+ engine.start
82
70
  end
83
71
 
84
72
  it "should fail if specified and doesnt exist" do
@@ -89,11 +77,10 @@ describe "Foreman::Engine" do
89
77
  it "should read .env if none specified" do
90
78
  File.open(".env", "w") { |f| f.puts("FOO=qoo") }
91
79
  engine = Foreman::Engine.new("Procfile")
92
- stub(engine).info
80
+ mock(engine).spawn_processes
93
81
  mock(engine).watch_for_termination
94
- mock(engine).fork_individual(anything, anything, anything)
95
82
  engine.environment.should == {"FOO"=>"qoo"}
96
- engine.execute("bravo")
83
+ engine.start
97
84
  end
98
85
  end
99
86
  end
@@ -13,8 +13,7 @@ describe Foreman::Export::Bluepill do
13
13
 
14
14
  it "exports to the filesystem" do
15
15
  bluepill.export("/tmp/init", :concurrency => "alpha=2")
16
-
17
16
  File.read("/tmp/init/app.pill").should == example_export_file("bluepill/app.pill")
18
17
  end
19
18
 
20
- end
19
+ end
@@ -19,7 +19,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
19
19
  process.monitor_children do |children|
20
20
  children.stop_command "kill -QUIT {{PID}}"
21
21
  end
22
-
22
+
23
23
  process.group = "app-alpha"
24
24
  end
25
25
 
@@ -37,7 +37,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
37
37
  process.monitor_children do |children|
38
38
  children.stop_command "kill -QUIT {{PID}}"
39
39
  end
40
-
40
+
41
41
  process.group = "app-alpha"
42
42
  end
43
43
 
@@ -57,7 +57,7 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
57
57
  process.monitor_children do |children|
58
58
  children.stop_command "kill -QUIT {{PID}}"
59
59
  end
60
-
60
+
61
61
  process.group = "app-bravo"
62
62
  end
63
63
 
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: foreman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.27.0
5
- prerelease:
4
+ version: 0.28.0.pre1
5
+ prerelease: 7
6
6
  platform: ruby
7
7
  authors:
8
8
  - David Dollar
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-12-05 00:00:00.000000000Z
12
+ date: 2011-12-09 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: term-ansicolor
16
- requirement: &70283570808260 !ruby/object:Gem::Requirement
16
+ requirement: &70324999098260 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.0.5
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70283570808260
24
+ version_requirements: *70324999098260
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: thor
27
- requirement: &70283570807760 !ruby/object:Gem::Requirement
27
+ requirement: &70324999097620 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: 0.13.6
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70283570807760
35
+ version_requirements: *70324999097620
36
36
  description: Process manager for applications with multiple components
37
37
  email: ddollar@gmail.com
38
38
  executables:
@@ -41,6 +41,7 @@ extensions: []
41
41
  extra_rdoc_files: []
42
42
  files:
43
43
  - bin/foreman
44
+ - bin/runner
44
45
  - data/example/error
45
46
  - data/example/log/neverdie.log
46
47
  - data/example/Procfile
@@ -63,6 +64,7 @@ files:
63
64
  - lib/foreman/export.rb
64
65
  - lib/foreman/process.rb
65
66
  - lib/foreman/procfile.rb
67
+ - lib/foreman/procfile_entry.rb
66
68
  - lib/foreman/utils.rb
67
69
  - lib/foreman/version.rb
68
70
  - lib/foreman.rb
@@ -104,16 +106,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
104
106
  version: '0'
105
107
  segments:
106
108
  - 0
107
- hash: -3613490576676930257
109
+ hash: 710611056517005467
108
110
  required_rubygems_version: !ruby/object:Gem::Requirement
109
111
  none: false
110
112
  requirements:
111
- - - ! '>='
113
+ - - ! '>'
112
114
  - !ruby/object:Gem::Version
113
- version: '0'
114
- segments:
115
- - 0
116
- hash: -3613490576676930257
115
+ version: 1.3.1
117
116
  requirements: []
118
117
  rubyforge_project:
119
118
  rubygems_version: 1.8.10