Merge pull request #164 from hsume2/master
Add support for running procfile in tmux session
This commit is contained in:
@@ -18,4 +18,5 @@ group :development do
|
|||||||
gem 'rr', '~> 1.0.2'
|
gem 'rr', '~> 1.0.2'
|
||||||
gem 'rspec', '~> 2.0'
|
gem 'rspec', '~> 2.0'
|
||||||
gem "simplecov", :require => false
|
gem "simplecov", :require => false
|
||||||
|
gem 'timecop'
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ GEM
|
|||||||
simplecov-html (~> 0.5.3)
|
simplecov-html (~> 0.5.3)
|
||||||
simplecov-html (0.5.3)
|
simplecov-html (0.5.3)
|
||||||
thor (0.14.6)
|
thor (0.14.6)
|
||||||
|
timecop (0.3.5)
|
||||||
win32console (1.3.0-x86-mingw32)
|
win32console (1.3.0-x86-mingw32)
|
||||||
xml-simple (1.0.15)
|
xml-simple (1.0.15)
|
||||||
|
|
||||||
@@ -58,4 +59,5 @@ DEPENDENCIES
|
|||||||
rr (~> 1.0.2)
|
rr (~> 1.0.2)
|
||||||
rspec (~> 2.0)
|
rspec (~> 2.0)
|
||||||
simplecov
|
simplecov
|
||||||
|
timecop
|
||||||
win32console (~> 1.3.0)
|
win32console (~> 1.3.0)
|
||||||
|
|||||||
Executable
+8
@@ -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
|
||||||
+14
-1
@@ -1,6 +1,7 @@
|
|||||||
require "foreman"
|
require "foreman"
|
||||||
require "foreman/helpers"
|
require "foreman/helpers"
|
||||||
require "foreman/engine"
|
require "foreman/engine"
|
||||||
|
require "foreman/tmux_engine"
|
||||||
require "foreman/export"
|
require "foreman/export"
|
||||||
require "shellwords"
|
require "shellwords"
|
||||||
require "thor"
|
require "thor"
|
||||||
@@ -10,6 +11,7 @@ class Foreman::CLI < Thor
|
|||||||
include Foreman::Helpers
|
include Foreman::Helpers
|
||||||
|
|
||||||
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
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)"
|
desc "start [PROCESS]", "Start the application (or a specific PROCESS)"
|
||||||
|
|
||||||
@@ -73,6 +75,17 @@ class Foreman::CLI < Thor
|
|||||||
end
|
end
|
||||||
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 ######################################################################
|
private ######################################################################
|
||||||
|
|
||||||
def check_procfile!
|
def check_procfile!
|
||||||
@@ -80,7 +93,7 @@ private ######################################################################
|
|||||||
end
|
end
|
||||||
|
|
||||||
def engine
|
def engine
|
||||||
@engine ||= Foreman::Engine.new(procfile, options)
|
@engine ||= self.class.new_engine(procfile, options)
|
||||||
end
|
end
|
||||||
|
|
||||||
def procfile
|
def procfile
|
||||||
|
|||||||
@@ -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
|
\fB\-p\fR, \fB\-\-port\fR
|
||||||
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
|
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
|
.P
|
||||||
\fBforeman run\fR is used to run one\-off commands using the same environment as your defined processes\.
|
\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
|
Specify which port to use as the base for this application. Should be
|
||||||
a multiple of 1000.
|
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
|
`foreman run` is used to run one-off commands using the same environment
|
||||||
as your defined processes.
|
as your defined processes.
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
end
|
||||||
|
|
||||||
require "rspec"
|
require "rspec"
|
||||||
|
require "timecop"
|
||||||
require "fakefs/safe"
|
require "fakefs/safe"
|
||||||
require "fakefs/spec_helpers"
|
require "fakefs/spec_helpers"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user