Merge pull request #164 from hsume2/master
Add support for running procfile in tmux session
This commit is contained in:
1
Gemfile
1
Gemfile
@@ -18,4 +18,5 @@ group :development do
|
||||
gem 'rr', '~> 1.0.2'
|
||||
gem 'rspec', '~> 2.0'
|
||||
gem "simplecov", :require => false
|
||||
gem 'timecop'
|
||||
end
|
||||
|
||||
@@ -40,6 +40,7 @@ GEM
|
||||
simplecov-html (~> 0.5.3)
|
||||
simplecov-html (0.5.3)
|
||||
thor (0.14.6)
|
||||
timecop (0.3.5)
|
||||
win32console (1.3.0-x86-mingw32)
|
||||
xml-simple (1.0.15)
|
||||
|
||||
@@ -58,4 +59,5 @@ DEPENDENCIES
|
||||
rr (~> 1.0.2)
|
||||
rspec (~> 2.0)
|
||||
simplecov
|
||||
timecop
|
||||
win32console (~> 1.3.0)
|
||||
|
||||
8
bin/taskman
Executable file
8
bin/taskman
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
$:.unshift File.expand_path("../../lib", __FILE__)
|
||||
|
||||
require "foreman/cli"
|
||||
|
||||
Foreman::CLI.engine_class = Foreman::TmuxEngine
|
||||
Foreman::CLI.start
|
||||
@@ -1,6 +1,7 @@
|
||||
require "foreman"
|
||||
require "foreman/helpers"
|
||||
require "foreman/engine"
|
||||
require "foreman/tmux_engine"
|
||||
require "foreman/export"
|
||||
require "shellwords"
|
||||
require "thor"
|
||||
@@ -10,6 +11,7 @@ class Foreman::CLI < Thor
|
||||
include Foreman::Helpers
|
||||
|
||||
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
||||
class_option :tmux, :type => :boolean, :aliases => "-t", :desc => "Run in tmux session"
|
||||
|
||||
desc "start [PROCESS]", "Start the application (or a specific PROCESS)"
|
||||
|
||||
@@ -73,6 +75,17 @@ class Foreman::CLI < Thor
|
||||
end
|
||||
end
|
||||
|
||||
class << self
|
||||
def new_engine(procfile, options)
|
||||
@engine_class ||= options[:tmux] ? Foreman::TmuxEngine : Foreman::Engine
|
||||
@engine_class.new(procfile, options)
|
||||
end
|
||||
|
||||
def engine_class=(klass)
|
||||
@engine_class = klass
|
||||
end
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def check_procfile!
|
||||
@@ -80,7 +93,7 @@ private ######################################################################
|
||||
end
|
||||
|
||||
def engine
|
||||
@engine ||= Foreman::Engine.new(procfile, options)
|
||||
@engine ||= self.class.new_engine(procfile, options)
|
||||
end
|
||||
|
||||
def procfile
|
||||
|
||||
37
lib/foreman/tmux_engine.rb
Normal file
37
lib/foreman/tmux_engine.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
require "foreman"
|
||||
require "foreman/engine"
|
||||
|
||||
class Foreman::TmuxEngine < Foreman::Engine
|
||||
|
||||
attr_reader :procfile
|
||||
attr_reader :session
|
||||
|
||||
def initialize(procfile, options={})
|
||||
@procfile = Foreman::Procfile.new(procfile)
|
||||
@options = options.dup
|
||||
@session = Time.now.to_i
|
||||
end
|
||||
|
||||
def start
|
||||
assign_colors
|
||||
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
|
||||
|
||||
ENV['BUNDLE_GEMFILE'] = nil
|
||||
|
||||
%x{tmux new-session -d -s #{session}}
|
||||
procfile.entries.each_with_index do |entry, index|
|
||||
name = "#{entry.name}.#{concurrency[entry.name]}"
|
||||
if index == 0
|
||||
%x{tmux rename-window -t #{session}:#{index} #{name}}
|
||||
else
|
||||
%x{tmux new-window -t #{session}:#{index} -n #{name}}
|
||||
end
|
||||
%x{tmux pipe-pane -o -t #{session}:#{index} "gawk '{ printf \\"%%s\\", \\"#{$stdout.color(colors[entry.name])}\\"; print strftime(\\"%%H:%%M:%%S\\"), \\"#{pad_process_name(name)} | #{$stdout.color(:reset)}\\", \\$0; fflush(); }' >> /tmp/foreman.#{session}.log"}
|
||||
%x{tmux send-keys -t #{session}:#{index} "#{entry.command}" C-m}
|
||||
end
|
||||
last_index = procfile.entries.length
|
||||
%x{tmux new-window -t #{session}:#{last_index} -n all}
|
||||
%x{tmux send-keys -t #{session}:#{last_index} "tail -f /tmp/foreman.#{session}.log" C-m}
|
||||
Kernel.exec("tmux attach-session -t #{session}")
|
||||
end
|
||||
end
|
||||
@@ -46,6 +46,10 @@ Specify an alternate Procfile to load, implies \fB\-d\fR at the Procfile root\.
|
||||
\fB\-p\fR, \fB\-\-port\fR
|
||||
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
|
||||
.
|
||||
.TP
|
||||
\fB\-t\fR, \fB\-\-tmux\fR
|
||||
Runs the processes in a tmux session\. Creates one window for each process and an extra window containing the output of each window (requires gawk)\.
|
||||
.
|
||||
.P
|
||||
\fBforeman run\fR is used to run one\-off commands using the same environment as your defined processes\.
|
||||
.
|
||||
|
||||
@@ -40,6 +40,10 @@ The following options control how the application is run:
|
||||
Specify which port to use as the base for this application. Should be
|
||||
a multiple of 1000.
|
||||
|
||||
* `-t`, `--tmux`:
|
||||
Runs the processes in a tmux session. Creates one window for each process
|
||||
and an extra window containing the output of each window (requires gawk).
|
||||
|
||||
`foreman run` is used to run one-off commands using the same environment
|
||||
as your defined processes.
|
||||
|
||||
|
||||
75
spec/foreman/tmux_engine_spec.rb
Normal file
75
spec/foreman/tmux_engine_spec.rb
Normal file
@@ -0,0 +1,75 @@
|
||||
require "spec_helper"
|
||||
require "foreman/tmux_engine"
|
||||
|
||||
describe "Foreman::TmuxEngine", :fakefs do
|
||||
subject { Foreman::TmuxEngine.new("Procfile", {}) }
|
||||
let(:session) { Time.now.to_i }
|
||||
|
||||
before do
|
||||
any_instance_of(Foreman::TmuxEngine) do |engine|
|
||||
stub(engine).proctitle
|
||||
stub(engine).termtitle
|
||||
end
|
||||
Timecop.freeze(Time.now)
|
||||
end
|
||||
|
||||
after do
|
||||
FileUtils.rm_f("foreman.#{session}.log")
|
||||
Timecop.return
|
||||
end
|
||||
|
||||
describe "initialize" do
|
||||
describe "without an existing Procfile" do
|
||||
it "raises an error" do
|
||||
lambda { subject }.should raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Procfile" do
|
||||
before { write_procfile }
|
||||
|
||||
it "reads the processes" do
|
||||
subject.procfile["alpha"].command.should == "./alpha"
|
||||
subject.procfile["bravo"].command.should == "./bravo"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if system("which tmux")
|
||||
describe "start" do
|
||||
before do
|
||||
write_procfile
|
||||
@pid = fork do
|
||||
exec("tmux start-server")
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
Process.waitpid(@pid)
|
||||
%x{tmux kill-session -t #{session} &> /dev/null}
|
||||
end
|
||||
|
||||
it "creates a tmux session and attaches" do
|
||||
%x{tmux has-session -t #{session} &> /dev/null}
|
||||
$?.exitstatus.should == 1
|
||||
|
||||
mock(Kernel).exec("tmux attach-session -t #{session}")
|
||||
subject.start
|
||||
|
||||
%x{tmux has-session -t #{session}}
|
||||
$?.exitstatus.should == 0
|
||||
|
||||
%x{tmux list-windows -t #{session}}.split("\n").map { |window|
|
||||
if window =~ /[0-9]+:\s(.+?)\s/
|
||||
$1
|
||||
end
|
||||
}.should == ["alpha.1", "bravo.1", "all"]
|
||||
sleep 1
|
||||
%x{tmux kill-session -t #{session}}
|
||||
output = %x[cat /tmp/foreman.#{session}.log]
|
||||
output.should =~ /alpha\.1.+\.\/alpha: No such file or directory/
|
||||
output.should =~ /bravo\.1.+\.\/bravo: No such file or directory/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -6,6 +6,7 @@ SimpleCov.start do
|
||||
end
|
||||
|
||||
require "rspec"
|
||||
require "timecop"
|
||||
require "fakefs/safe"
|
||||
require "fakefs/spec_helpers"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user