Compare commits
4 Commits
v0.63.0
...
v0.48.0.pr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b704ad852 | ||
|
|
43428175c3 | ||
|
|
87f65e489c | ||
|
|
e5521f2cbb |
@@ -16,7 +16,9 @@ notifications:
|
||||
- http://dx-helper.herokuapp.com/travis
|
||||
|
||||
rvm:
|
||||
- 1.8.7
|
||||
- 1.9.2
|
||||
- 1.9.3
|
||||
- 2.0.0
|
||||
- jruby
|
||||
- rbx
|
||||
- ree
|
||||
|
||||
125
Changelog.md
125
Changelog.md
@@ -1,125 +1,8 @@
|
||||
## 0.62.0 (2013-03-08)
|
||||
## 0.48.0.pre1 (2012-06-11)
|
||||
|
||||
* Merge pull request #334 from ged/reentrant_signal_handlers [David Dollar]
|
||||
* Merge pull request #335 from ged/20_encoding_fix [David Dollar]
|
||||
* Try to allow children to shut down gracefully [Michael Granger]
|
||||
* Add deferred signal-handling (fixes #332). [Michael Granger]
|
||||
* Fix spec encoding problem under Ruby 2.0.0. [Michael Granger]
|
||||
* add ruby 2.0 to travis [David Dollar]
|
||||
* Merge pull request #327 from patheticpat/master [David Dollar]
|
||||
* Fixed a typo in cli options description [Michael Kaiser]
|
||||
* handled by mingw now [David Dollar]
|
||||
|
||||
## 0.61.0 (2013-01-14)
|
||||
|
||||
* Fix bug in color definitons [nseo]
|
||||
* Fix for high CPU load when processes close output [Pavel Forkert]
|
||||
* Ensure foreman is the process group leader [Christos Trochalakis]
|
||||
* Don't ignore blank lines in the output [Matt Venables]
|
||||
* Add license to gemspec [petedmarsh]
|
||||
* Since JRuby 1.9 doesn't require posix/spawn, only follow that path if JRuby is loaded and running in 1.8 mode. [Adam Hutchison]
|
||||
* Remove explicit requirement on rubygems. [Cyril Rohr]
|
||||
* Dont use shared_path variable before multistage has a chance at it [Aditya Sanghi]
|
||||
* Strip Windows Line Endings [Paul Morton]
|
||||
* Fix man page: --directory is actually --root. [Evan Jones]
|
||||
* Add timeout switch to CLI [Paulo Luis Franchini Casaretto]
|
||||
* Remove expectation of double quotes around environment variables from test comparisons [Kevin McAllister]
|
||||
* Remove explicit wrapping of Shellwords.escape in double quotes [Kevin McAllister]
|
||||
|
||||
## 0.60.2 (2012-10-08)
|
||||
|
||||
* Fix for nil value on io select loop, fixes #260 [Silvio Relli]
|
||||
|
||||
## 0.60.1 (2012-10-08)
|
||||
|
||||
* sleep on select() to avoid spinning the cpu [Silvio Relli]
|
||||
|
||||
## 0.60.0 (2012-09-25)
|
||||
|
||||
* foreman run can run things from the Procfile like heroku run. [Dan Peterson]
|
||||
|
||||
## 0.59.0 (2012-09-15)
|
||||
|
||||
* Use /bin/sh instead of bash for foreman-runner [Jeremy Evans]
|
||||
|
||||
## 0.58.0 (2012-09-14)
|
||||
|
||||
* dont set HOME [David Dollar]
|
||||
* Add StandardOutPath to launchd export [Aaron Kalin]
|
||||
* Add command argument string splitting [Aaron Kalin]
|
||||
* Cleanup launchd exporter [Aaron Kalin]
|
||||
* Enable trim_mode via '-' in ERB templates [Aaron Kalin]
|
||||
* Add support for setting environment variables [Aaron Kalin]
|
||||
* foreman run should exit with the same code as its command [Omar Khan]
|
||||
* Handle multiline strings in .env file [Szymon Nowak]
|
||||
* Use path and env variables in the inittab export [Indrek Juhkam]
|
||||
* fixed the directory option [Arnaud Lachaume]
|
||||
* Add capistrano export support [Daniel Farrell]
|
||||
|
||||
## 0.57.0 (2012-08-21)
|
||||
|
||||
* fix startup checks for upstart exporter [Aditya Sanghi]
|
||||
|
||||
## 0.56.0 (2012-08-19)
|
||||
|
||||
* read .profile, not .profile.d [David Dollar]
|
||||
|
||||
## 0.55.0 (2012-08-14)
|
||||
|
||||
* use a forked process to exec a run with environment [David Dollar]
|
||||
|
||||
## 0.54.0 (2012-08-14)
|
||||
|
||||
* use Foreman::Process to extract command running [David Dollar]
|
||||
* changed to check env for bash [brntbeer]
|
||||
|
||||
## 0.53.0 (2012-07-24)
|
||||
|
||||
* put app root in $HOME [David Dollar]
|
||||
|
||||
## 0.52.0 (2012-07-24)
|
||||
|
||||
* wrap command in a runner that sources .profile.d scripts [David Dollar]
|
||||
* fix upstart export specs [David Dollar]
|
||||
* Make upstart export start/stop with network [Daniel Farrell]
|
||||
|
||||
## 0.51.0 (2012-07-11)
|
||||
|
||||
* dont try to colorize windows [David Dollar]
|
||||
|
||||
## 0.50.0 (2012-07-11)
|
||||
|
||||
* handle windows [David Dollar]
|
||||
|
||||
## 0.49.0 (2012-07-11)
|
||||
|
||||
* 1.8 compatibility [David Dollar]
|
||||
* use one pgroup for all of foreman and kill that since ruby 1.8 sucks at pgroups [David Dollar]
|
||||
* better debugging [David Dollar]
|
||||
|
||||
## 0.48.0 (2012-07-10)
|
||||
|
||||
* allow old exporter format to work, but with deprecation warning [David Dollar]
|
||||
* remove debugging code [David Dollar]
|
||||
* Merge pull request #219 from MarkDBlackwell/patch-1 [David Dollar]
|
||||
* Avoid crash by verifying the existence of SIGHUP before accessing it. [Mark D. Blackwell]
|
||||
* allow color to be forced on [David Dollar]
|
||||
* terminate gracefully if stdout goes away [David Dollar]
|
||||
* always flush output [David Dollar]
|
||||
* Merge pull request #212 from morgoth/added-version-command [David Dollar]
|
||||
* added command for displaying foreman version [Wojciech Wnętrzak]
|
||||
* Merge pull request #211 from morgoth/fixed-yaml-usage [David Dollar]
|
||||
* fixed using YAML [Wojciech Wnętrzak]
|
||||
* test on more things, but don't fail [David Dollar]
|
||||
* changelog [David Dollar]
|
||||
* 0.48.0.pre1 [David Dollar]
|
||||
* foreman doesn't work on ruby 1.8, may try to fix later [David Dollar]
|
||||
* use bash [David Dollar]
|
||||
* massive refactoring for programmatic control and stability [David Dollar]
|
||||
* Merge pull request #164 from hsume2/master [David Dollar]
|
||||
* Only run tmux specs if tmux is installed [Henry Hsu]
|
||||
* Do not assume BUNDLE_GEMFILE [Henry Hsu]
|
||||
* Add support for starting procfile in tmux session [Henry Hsu]
|
||||
* Massive refactoring for programmatic control and stability [David Dollar]
|
||||
* Procfile commands with shell interpolations now work again [David Dollar]
|
||||
* Stop trying to test on Ruby 1.8 [David Dollar]
|
||||
|
||||
## 0.47.0 (2012-06-07)
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
foreman (0.63.0)
|
||||
dotenv (>= 0.7)
|
||||
foreman (0.48.0.pre2)
|
||||
thor (>= 0.13.6)
|
||||
|
||||
GEM
|
||||
@@ -14,7 +13,6 @@ GEM
|
||||
xml-simple
|
||||
builder (3.0.0)
|
||||
diff-lcs (1.1.3)
|
||||
dotenv (0.7.0)
|
||||
fakefs (0.3.2)
|
||||
hpricot (0.8.6)
|
||||
hpricot (0.8.6-java)
|
||||
@@ -41,7 +39,7 @@ GEM
|
||||
multi_json (~> 1.0.3)
|
||||
simplecov-html (~> 0.5.3)
|
||||
simplecov-html (0.5.3)
|
||||
thor (0.16.0)
|
||||
thor (0.15.2)
|
||||
timecop (0.3.5)
|
||||
win32console (1.3.0-x86-mingw32)
|
||||
xml-simple (1.0.15)
|
||||
|
||||
19
LICENSE
19
LICENSE
@@ -1,19 +0,0 @@
|
||||
Copyright (c) 2012 David Dollar
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -43,6 +43,4 @@ David Dollar
|
||||
|
||||
## License
|
||||
|
||||
Foreman is licensed under the MIT license.
|
||||
|
||||
See LICENSE for the full license text.
|
||||
MIT
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
#/ Usage: foreman-runner [-d <dir>] [-p] <command> [<args>...]
|
||||
#/ Usage: foreman-runner [-d <dir>] <command> [<args>...]
|
||||
#/
|
||||
#/ Run a command with exec, optionally changing directory first
|
||||
|
||||
@@ -16,12 +16,9 @@ usage() {
|
||||
exit
|
||||
}
|
||||
|
||||
read_profile=""
|
||||
|
||||
while getopts ":hd:p" OPT; do
|
||||
while getopts ":hd:" OPT; do
|
||||
case $OPT in
|
||||
d) cd "$OPTARG" ;;
|
||||
p) read_profile="1" ;;
|
||||
h) usage ;;
|
||||
\?) error "invalid option: -$OPTARG" ;;
|
||||
:) error "option -$OPTARG requires an argument" ;;
|
||||
@@ -32,10 +29,4 @@ shift $((OPTIND-1))
|
||||
|
||||
[ -z "$1" ] && usage
|
||||
|
||||
if [ "$read_profile" = "1" ]; then
|
||||
if [ -f .profile ]; then
|
||||
. ./.profile
|
||||
fi
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/bin/sh
|
||||
export FOO=bar
|
||||
@@ -9,6 +9,6 @@ sigterm() {
|
||||
#trap sigterm SIGTERM
|
||||
|
||||
while true; do
|
||||
echo "$NAME: ping $$"
|
||||
echo "$NAME: ping"
|
||||
sleep 1
|
||||
done
|
||||
|
||||
@@ -4,25 +4,14 @@
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string><%= "#{app}-#{name}-#{num}" %></string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<%- engine.env.merge("PORT" => port).each_pair do |var,env| -%>
|
||||
<key><%= var.upcase %></key>
|
||||
<string><%= env %></string>
|
||||
<%- end -%>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<%- command_args.each do |command| -%>
|
||||
<string><%= command %></string>
|
||||
<%- end -%>
|
||||
<string><%= process.command %></string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string><%= log %>/<%= app %>-<%= name %>-<%=num%>.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string><%= log %>/<%= app %>-<%= name %>-<%=num%>.log</string>
|
||||
<key>UserName</key>
|
||||
|
||||
@@ -6,7 +6,3 @@ bash << "EOF"
|
||||
EOF
|
||||
|
||||
end script
|
||||
|
||||
start on runlevel [2345]
|
||||
|
||||
stop on runlevel [016]
|
||||
|
||||
@@ -3,7 +3,6 @@ require "foreman/version"
|
||||
|
||||
Gem::Specification.new do |gem|
|
||||
gem.name = "foreman"
|
||||
gem.license = "MIT"
|
||||
gem.version = Foreman::VERSION
|
||||
|
||||
gem.author = "David Dollar"
|
||||
@@ -18,9 +17,9 @@ Gem::Specification.new do |gem|
|
||||
gem.files << "man/foreman.1"
|
||||
|
||||
gem.add_dependency 'thor', '>= 0.13.6'
|
||||
gem.add_dependency 'dotenv', '>= 0.7'
|
||||
|
||||
if ENV["PLATFORM"] == "java"
|
||||
gem.add_dependency "posix-spawn", "~> 0.3.6"
|
||||
gem.platform = Gem::Platform.new("java")
|
||||
end
|
||||
|
||||
|
||||
@@ -8,12 +8,8 @@ module Foreman
|
||||
File.expand_path("../../bin/foreman-runner", __FILE__)
|
||||
end
|
||||
|
||||
def self.jruby_18?
|
||||
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java" and ruby_18?
|
||||
end
|
||||
|
||||
def self.ruby_18?
|
||||
defined?(RUBY_VERSION) and RUBY_VERSION =~ /^1\.8\.\d+/
|
||||
def self.jruby?
|
||||
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java"
|
||||
end
|
||||
|
||||
def self.windows?
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
if defined?(Capistrano)
|
||||
Capistrano::Configuration.instance(:must_exist).load do
|
||||
|
||||
namespace :foreman do
|
||||
desc <<-DESC
|
||||
Export the Procfile to upstart. Will use sudo if available.
|
||||
|
||||
You can override any of these defaults by setting the variables shown below.
|
||||
|
||||
set :foreman_format, "upstart"
|
||||
set :foreman_location, "/etc/init"
|
||||
set :foreman_procfile, "Procfile"
|
||||
set :foreman_app, application
|
||||
set :foreman_user, user
|
||||
set :foreman_log, 'shared_path/log'
|
||||
set :foreman_concurrency, false
|
||||
DESC
|
||||
task :export, :roles => :app do
|
||||
bundle_cmd = fetch(:bundle_cmd, "bundle")
|
||||
foreman_format = fetch(:foreman_format, "upstart")
|
||||
foreman_location = fetch(:foreman_location, "/etc/init")
|
||||
foreman_procfile = fetch(:foreman_procfile, "Procfile")
|
||||
foreman_app = fetch(:foreman_app, application)
|
||||
foreman_user = fetch(:foreman_user, user)
|
||||
foreman_log = fetch(:foreman_log, "#{shared_path}/log")
|
||||
foreman_concurrency = fetch(:foreman_concurrency, false)
|
||||
|
||||
args = ["#{foreman_format} #{foreman_location}"]
|
||||
args << "-f #{foreman_procfile}"
|
||||
args << "-a #{foreman_app}"
|
||||
args << "-u #{foreman_user}"
|
||||
args << "-l #{foreman_log}"
|
||||
args << "-c #{foreman_concurrency}" if foreman_concurrency
|
||||
run "cd #{release_path} && #{sudo} #{bundle_cmd} exec foreman export #{args.join(' ')}"
|
||||
end
|
||||
|
||||
desc "Start the application services"
|
||||
task :start, :roles => :app do
|
||||
run "#{sudo} start #{application}"
|
||||
end
|
||||
|
||||
desc "Stop the application services"
|
||||
task :stop, :roles => :app do
|
||||
run "#{sudo} stop #{application}"
|
||||
end
|
||||
|
||||
desc "Restart the application services"
|
||||
task :restart, :roles => :app do
|
||||
run "#{sudo} start #{application} || #{sudo} restart #{application}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,17 +3,13 @@ require "foreman/helpers"
|
||||
require "foreman/engine"
|
||||
require "foreman/engine/cli"
|
||||
require "foreman/export"
|
||||
require "foreman/version"
|
||||
require "shellwords"
|
||||
require "yaml"
|
||||
require "thor"
|
||||
|
||||
class Foreman::CLI < Thor
|
||||
|
||||
include Foreman::Helpers
|
||||
|
||||
map ["-v", "--version"] => :version
|
||||
|
||||
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
||||
class_option :root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"
|
||||
|
||||
@@ -23,7 +19,6 @@ class Foreman::CLI < Thor
|
||||
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
||||
method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"'
|
||||
method_option :port, :type => :numeric, :aliases => "-p"
|
||||
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."
|
||||
|
||||
class << self
|
||||
# Hackery. Take the run method away from Thor so that we can redefine it.
|
||||
@@ -76,33 +71,13 @@ class Foreman::CLI < Thor
|
||||
|
||||
def run(*args)
|
||||
load_environment!
|
||||
|
||||
if File.exist?(procfile)
|
||||
engine.load_procfile(procfile)
|
||||
begin
|
||||
exec engine.env, args.shelljoin
|
||||
rescue Errno::EACCES
|
||||
error "not executable: #{args.first}"
|
||||
rescue Errno::ENOENT
|
||||
error "command not found: #{args.first}"
|
||||
end
|
||||
|
||||
pid = fork do
|
||||
begin
|
||||
engine.env.each { |k,v| ENV[k] = v }
|
||||
if args.size == 1 && process = engine.process(args.first)
|
||||
process.exec(:env => engine.env)
|
||||
else
|
||||
exec args.shelljoin
|
||||
end
|
||||
rescue Errno::EACCES
|
||||
error "not executable: #{args.first}"
|
||||
rescue Errno::ENOENT
|
||||
error "command not found: #{args.first}"
|
||||
end
|
||||
end
|
||||
Process.wait(pid)
|
||||
exit $?.exitstatus
|
||||
end
|
||||
|
||||
desc "version", "Display Foreman gem version"
|
||||
|
||||
def version
|
||||
puts Foreman::VERSION
|
||||
end
|
||||
|
||||
no_tasks do
|
||||
@@ -140,7 +115,7 @@ private ######################################################################
|
||||
def procfile
|
||||
case
|
||||
when options[:procfile] then options[:procfile]
|
||||
when options[:root] then File.expand_path(File.join(options[:root], "Procfile"))
|
||||
when options[:root] then File.expand_path(File.join(options[:app_root], "Procfile"))
|
||||
else "Procfile"
|
||||
end
|
||||
end
|
||||
@@ -148,7 +123,7 @@ private ######################################################################
|
||||
def options
|
||||
original_options = super
|
||||
return original_options unless File.exists?(".foreman")
|
||||
defaults = ::YAML::load_file(".foreman") || {}
|
||||
defaults = YAML::load_file(".foreman") || {}
|
||||
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
|
||||
end
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require "foreman"
|
||||
require "foreman/env"
|
||||
require "foreman/process"
|
||||
require "foreman/procfile"
|
||||
require "dotenv"
|
||||
require "tempfile"
|
||||
require "timeout"
|
||||
require "fileutils"
|
||||
@@ -9,10 +9,6 @@ require "thread"
|
||||
|
||||
class Foreman::Engine
|
||||
|
||||
# The signals that the engine cares about.
|
||||
#
|
||||
HANDLED_SIGNALS = [ :TERM, :INT, :HUP ]
|
||||
|
||||
attr_reader :env
|
||||
attr_reader :options
|
||||
attr_reader :processes
|
||||
@@ -28,8 +24,7 @@ class Foreman::Engine
|
||||
def initialize(options={})
|
||||
@options = options.dup
|
||||
|
||||
@options[:formation] ||= (options[:concurrency] || "all=1")
|
||||
@options[:timeout] ||= 5
|
||||
@options[:formation] ||= "all=1"
|
||||
|
||||
@env = {}
|
||||
@mutex = Mutex.new
|
||||
@@ -37,22 +32,15 @@ class Foreman::Engine
|
||||
@processes = []
|
||||
@running = {}
|
||||
@readers = {}
|
||||
|
||||
# Self-pipe for deferred signal-handling (ala djb: http://cr.yp.to/docs/selfpipe.html)
|
||||
reader, writer = create_pipe
|
||||
reader.close_on_exec = true if reader.respond_to?(:close_on_exec)
|
||||
writer.close_on_exec = true if writer.respond_to?(:close_on_exec)
|
||||
@selfpipe = { :reader => reader, :writer => writer }
|
||||
|
||||
# Set up a global signal queue
|
||||
# http://blog.rubybestpractices.com/posts/ewong/016-Implementing-Signal-Handlers.html
|
||||
Thread.main[:signal_queue] = []
|
||||
end
|
||||
|
||||
# Start the processes registered to this +Engine+
|
||||
#
|
||||
def start
|
||||
register_signal_handlers
|
||||
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
||||
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
||||
trap("HUP") { puts "SIGHUP received"; terminate_gracefully }
|
||||
|
||||
startup
|
||||
spawn_processes
|
||||
watch_for_output
|
||||
@@ -61,74 +49,6 @@ class Foreman::Engine
|
||||
shutdown
|
||||
end
|
||||
|
||||
# Set up deferred signal handlers
|
||||
#
|
||||
def register_signal_handlers
|
||||
HANDLED_SIGNALS.each do |sig|
|
||||
if ::Signal.list.include? sig.to_s
|
||||
trap(sig) { Thread.main[:signal_queue] << sig ; notice_signal }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Unregister deferred signal handlers
|
||||
#
|
||||
def restore_default_signal_handlers
|
||||
HANDLED_SIGNALS.each do |sig|
|
||||
trap(sig, :DEFAULT) if ::Signal.list.include? sig.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Wake the main thread up via the selfpipe when there's a signal
|
||||
#
|
||||
def notice_signal
|
||||
@selfpipe[:writer].write_nonblock( '.' )
|
||||
rescue Errno::EAGAIN
|
||||
# Ignore writes that would block
|
||||
rescue Errno::EINT
|
||||
# Retry if another signal arrived while writing
|
||||
retry
|
||||
end
|
||||
|
||||
# Invoke the real handler for signal +sig+. This shouldn't be called directly
|
||||
# by signal handlers, as it might invoke code which isn't re-entrant.
|
||||
#
|
||||
# @param [Symbol] sig the name of the signal to be handled
|
||||
#
|
||||
def handle_signal(sig)
|
||||
case sig
|
||||
when :TERM
|
||||
handle_term_signal
|
||||
when :INT
|
||||
handle_interrupt
|
||||
when :HUP
|
||||
handle_hangup
|
||||
else
|
||||
system "unhandled signal #{sig}"
|
||||
end
|
||||
end
|
||||
|
||||
# Handle a TERM signal
|
||||
#
|
||||
def handle_term_signal
|
||||
puts "SIGTERM received"
|
||||
terminate_gracefully
|
||||
end
|
||||
|
||||
# Handle an INT signal
|
||||
#
|
||||
def handle_interrupt
|
||||
puts "SIGINT received"
|
||||
terminate_gracefully
|
||||
end
|
||||
|
||||
# Handle a HUP signal
|
||||
#
|
||||
def handle_hangup
|
||||
puts "SIGHUP received"
|
||||
terminate_gracefully
|
||||
end
|
||||
|
||||
# Register a process to be run by this +Engine+
|
||||
#
|
||||
# @param [String] name A name for this process
|
||||
@@ -169,40 +89,20 @@ class Foreman::Engine
|
||||
# @param [String] filename A .env file to load into the environment
|
||||
#
|
||||
def load_env(filename)
|
||||
@env.update Dotenv::Environment.new(filename)
|
||||
end
|
||||
|
||||
# Send a signal to all processes started by this +Engine+
|
||||
#
|
||||
# @param [String] signal The signal to send to each process
|
||||
#
|
||||
def kill_children(signal="SIGTERM")
|
||||
if Foreman.windows?
|
||||
@running.each do |pid, (process, index)|
|
||||
system "sending #{signal} to #{name_for(pid)} at pid #{pid}"
|
||||
begin
|
||||
Process.kill(signal, pid)
|
||||
rescue Errno::ESRCH, Errno::EPERM
|
||||
end
|
||||
end
|
||||
else
|
||||
begin
|
||||
Process.kill signal, *@running.keys unless @running.empty?
|
||||
rescue Errno::ESRCH, Errno::EPERM
|
||||
end
|
||||
Foreman::Env.new(filename).entries do |name, value|
|
||||
@env[name] = value
|
||||
end
|
||||
end
|
||||
|
||||
# Send a signal to the whole process group.
|
||||
# Send a signal to all processesstarted by this +Engine+
|
||||
#
|
||||
# @param [String] signal The signal to send
|
||||
# @param [String] signal The signal to send to each process
|
||||
#
|
||||
def killall(signal="SIGTERM")
|
||||
if Foreman.windows?
|
||||
kill_children(signal)
|
||||
else
|
||||
@running.each do |pid, (process, index)|
|
||||
system "sending #{signal} to #{name_for(pid)} at pid #{pid}"
|
||||
begin
|
||||
Process.kill "-#{signal}", Process.pid
|
||||
Process.kill(signal, -1 * pid)
|
||||
rescue Errno::ESRCH, Errno::EPERM
|
||||
end
|
||||
end
|
||||
@@ -254,28 +154,11 @@ class Foreman::Engine
|
||||
#
|
||||
# @param [Foreman::Process] process A +Process+ associated with this engine
|
||||
# @param [Fixnum] instance The instance of the process
|
||||
#
|
||||
#
|
||||
# @returns [Fixnum] port The port to use for this instance of this process
|
||||
#
|
||||
def port_for(process, instance, base=nil)
|
||||
if base
|
||||
base + (@processes.index(process.process) * 100) + (instance - 1)
|
||||
else
|
||||
base_port + (@processes.index(process) * 100) + (instance - 1)
|
||||
end
|
||||
end
|
||||
|
||||
# Get the base port for this foreman instance
|
||||
#
|
||||
# @returns [Fixnum] port The base port
|
||||
#
|
||||
def base_port
|
||||
(options[:port] || env["PORT"] || ENV["PORT"] || 5000).to_i
|
||||
end
|
||||
|
||||
# deprecated
|
||||
def environment
|
||||
env
|
||||
def port_for(process, instance)
|
||||
base_port + (@processes.index(process) * 100) + (instance - 1)
|
||||
end
|
||||
|
||||
private
|
||||
@@ -296,6 +179,10 @@ private
|
||||
|
||||
## Helpers ##########################################################
|
||||
|
||||
def base_port
|
||||
(options[:port] || env["PORT"] || ENV["PORT"] || 5000).to_i
|
||||
end
|
||||
|
||||
def create_pipe
|
||||
IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
|
||||
end
|
||||
@@ -306,7 +193,7 @@ private
|
||||
end
|
||||
|
||||
def parse_formation(formation)
|
||||
pairs = formation.to_s.gsub(/\s/, "").split(",")
|
||||
pairs = @options[:formation].to_s.gsub(/\s/, "").split(",")
|
||||
|
||||
pairs.inject(Hash.new(0)) do |ax, pair|
|
||||
process, amount = pair.split("=")
|
||||
@@ -349,9 +236,7 @@ private
|
||||
1.upto(formation[@names[process]]) do |n|
|
||||
reader, writer = create_pipe
|
||||
begin
|
||||
pid = process.run(:output => writer, :env => {
|
||||
"PORT" => port_for(process, n).to_s
|
||||
})
|
||||
pid = process.run(:output => writer, :env => { "PORT" => port_for(process, n).to_s })
|
||||
writer.puts "started with pid #{pid}"
|
||||
rescue Errno::ENOENT
|
||||
writer.puts "unknown command: #{process.command}"
|
||||
@@ -366,28 +251,9 @@ private
|
||||
Thread.new do
|
||||
begin
|
||||
loop do
|
||||
io = IO.select([@selfpipe[:reader]] + @readers.values, nil, nil, 30)
|
||||
|
||||
begin
|
||||
@selfpipe[:reader].read_nonblock(11)
|
||||
rescue Errno::EAGAIN, Errno::EINTR => err
|
||||
# ignore
|
||||
end
|
||||
|
||||
# Look for any signals that arrived and handle them
|
||||
while sig = Thread.main[:signal_queue].shift
|
||||
self.handle_signal(sig)
|
||||
end
|
||||
|
||||
(io.nil? ? [] : io.first).each do |reader|
|
||||
next if reader == @selfpipe[:reader]
|
||||
|
||||
if reader.eof?
|
||||
@readers.delete_if { |key, value| value == reader }
|
||||
else
|
||||
data = reader.gets
|
||||
output_with_mutex name_for(@readers.invert[reader]), data
|
||||
end
|
||||
(IO.select(@readers.values).first || []).each do |reader|
|
||||
data = reader.gets
|
||||
output_with_mutex name_for(@readers.key(reader)), data
|
||||
end
|
||||
end
|
||||
rescue Exception => ex
|
||||
@@ -408,16 +274,10 @@ private
|
||||
|
||||
def terminate_gracefully
|
||||
return if @terminating
|
||||
restore_default_signal_handlers
|
||||
@terminating = true
|
||||
if Foreman.windows?
|
||||
system "sending SIGKILL to all processes"
|
||||
kill_children "SIGKILL"
|
||||
else
|
||||
system "sending SIGTERM to all processes"
|
||||
kill_children "SIGTERM"
|
||||
end
|
||||
Timeout.timeout(options[:timeout]) do
|
||||
system "sending SIGTERM to all processes"
|
||||
killall "SIGTERM"
|
||||
Timeout.timeout(5) do
|
||||
watch_for_termination while @running.length > 0
|
||||
end
|
||||
rescue Timeout::Error
|
||||
|
||||
@@ -31,7 +31,6 @@ class Foreman::Engine::CLI < Foreman::Engine
|
||||
|
||||
def color?
|
||||
return true if @@color_force
|
||||
return false if Foreman.windows?
|
||||
return false unless self.respond_to?(:isatty)
|
||||
self.isatty && ENV["TERM"]
|
||||
end
|
||||
@@ -44,17 +43,17 @@ class Foreman::Engine::CLI < Foreman::Engine
|
||||
|
||||
end
|
||||
|
||||
FOREMAN_COLORS = %w( cyan yellow green magenta red blue bright_cyan bright_yellow
|
||||
bright_green bright_magenta bright_red bright_blue )
|
||||
FOREMAN_COLORS = %w( cyan yellow green magenta red blue intense_cyan intense_yellow
|
||||
intense_green intense_magenta intense_red, intense_blue )
|
||||
|
||||
def startup
|
||||
@colors = map_colors
|
||||
proctitle "foreman: master" unless Foreman.windows?
|
||||
Color.enable($stdout, options[:color])
|
||||
proctitle "foreman: master"
|
||||
end
|
||||
|
||||
def output(name, data)
|
||||
data.to_s.lines.map(&:chomp).each do |message|
|
||||
data.to_s.chomp.split("\n").each do |message|
|
||||
Color.enable($stdout, options[:color]) unless $stdout.respond_to?(:color?)
|
||||
output = ""
|
||||
output += $stdout.color(@colors[name.split(".").first].to_sym)
|
||||
output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
|
||||
@@ -89,7 +88,7 @@ private
|
||||
@names.values.each_with_index do |name, index|
|
||||
colors[name] = FOREMAN_COLORS[index % FOREMAN_COLORS.length]
|
||||
end
|
||||
colors["system"] = "bright_white"
|
||||
colors["system"] = "intense_white"
|
||||
colors
|
||||
end
|
||||
|
||||
|
||||
27
lib/foreman/env.rb
Normal file
27
lib/foreman/env.rb
Normal file
@@ -0,0 +1,27 @@
|
||||
require "foreman"
|
||||
|
||||
class Foreman::Env
|
||||
|
||||
attr_reader :entries
|
||||
|
||||
def initialize(filename)
|
||||
@entries = File.read(filename).split("\n").inject({}) do |ax, line|
|
||||
if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
|
||||
key = $1
|
||||
case val = $2
|
||||
when /\A'(.*)'\z/ then ax[key] = $1
|
||||
when /\A"(.*)"\z/ then ax[key] = $1.gsub(/\\(.)/, '\1')
|
||||
else ax[key] = val
|
||||
end
|
||||
end
|
||||
ax
|
||||
end
|
||||
end
|
||||
|
||||
def entries
|
||||
@entries.each do |key, value|
|
||||
yield key, value
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,6 +1,5 @@
|
||||
require "foreman"
|
||||
require "foreman/helpers"
|
||||
require "pathname"
|
||||
|
||||
module Foreman::Export
|
||||
extend Foreman::Helpers
|
||||
@@ -32,3 +31,4 @@ require "foreman/export/bluepill"
|
||||
require "foreman/export/runit"
|
||||
require "foreman/export/supervisord"
|
||||
require "foreman/export/launchd"
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
require "foreman/export"
|
||||
require "ostruct"
|
||||
require "pathname"
|
||||
require "shellwords"
|
||||
|
||||
class Foreman::Export::Base
|
||||
@@ -10,37 +8,11 @@ class Foreman::Export::Base
|
||||
attr_reader :options
|
||||
attr_reader :formation
|
||||
|
||||
# deprecated
|
||||
attr_reader :port
|
||||
|
||||
def initialize(location, engine, options={})
|
||||
@location = location
|
||||
@engine = engine
|
||||
@options = options.dup
|
||||
@formation = engine.formation
|
||||
|
||||
# deprecated
|
||||
def port
|
||||
Foreman::Export::Base.warn_deprecation!
|
||||
engine.base_port
|
||||
end
|
||||
|
||||
# deprecated
|
||||
def template
|
||||
Foreman::Export::Base.warn_deprecation!
|
||||
options[:template]
|
||||
end
|
||||
|
||||
# deprecated
|
||||
def @engine.procfile
|
||||
Foreman::Export::Base.warn_deprecation!
|
||||
@processes.map do |process|
|
||||
OpenStruct.new(
|
||||
:name => @names[process],
|
||||
:process => process
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def export
|
||||
@@ -64,18 +36,6 @@ class Foreman::Export::Base
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def self.warn_deprecation!
|
||||
@@deprecation_warned ||= false
|
||||
return if @@deprecation_warned
|
||||
puts "WARNING: Using deprecated exporter interface. Please update your exporter"
|
||||
puts "the interface shown in the upstart exporter:"
|
||||
puts
|
||||
puts "https://github.com/ddollar/foreman/blob/master/lib/foreman/export/upstart.rb"
|
||||
puts "https://github.com/ddollar/foreman/blob/master/data/export/upstart/process.conf.erb"
|
||||
puts
|
||||
@@deprecation_warned = true
|
||||
end
|
||||
|
||||
def error(message)
|
||||
raise Foreman::Export::Exception.new(message)
|
||||
end
|
||||
@@ -91,35 +51,20 @@ private ######################################################################
|
||||
end
|
||||
|
||||
def shell_quote(value)
|
||||
Shellwords.escape(value)
|
||||
'"' + Shellwords.escape(value) + '"'
|
||||
end
|
||||
|
||||
# deprecated
|
||||
def old_export_template(exporter, file, template_root)
|
||||
if template_root && File.exist?(file_path = File.join(template_root, file))
|
||||
File.read(file_path)
|
||||
elsif File.exist?(file_path = File.expand_path(File.join("~/.foreman/templates", file)))
|
||||
File.read(file_path)
|
||||
else
|
||||
File.read(File.expand_path("../../../../data/export/#{exporter}/#{file}", __FILE__))
|
||||
end
|
||||
end
|
||||
|
||||
def export_template(name, file=nil, template_root=nil)
|
||||
if file && template_root
|
||||
old_export_template name, file, template_root
|
||||
else
|
||||
name_without_first = name.split("/")[1..-1].join("/")
|
||||
matchers = []
|
||||
matchers << File.join(options[:template], name_without_first) if options[:template]
|
||||
matchers << File.expand_path("~/.foreman/templates/#{name}")
|
||||
matchers << File.expand_path("../../../../data/export/#{name}", __FILE__)
|
||||
File.read(matchers.detect { |m| File.exists?(m) })
|
||||
end
|
||||
def export_template(name)
|
||||
name_without_first = name.split("/")[1..-1].join("/")
|
||||
matchers = []
|
||||
matchers << File.join(options[:template], name_without_first) if options[:template]
|
||||
matchers << File.expand_path("~/.foreman/templates/#{name}")
|
||||
matchers << File.expand_path("../../../../data/export/#{name}", __FILE__)
|
||||
File.read(matchers.detect { |m| File.exists?(m) })
|
||||
end
|
||||
|
||||
def write_template(name, target, binding)
|
||||
compiled = ERB.new(export_template(name), nil, '-').result(binding)
|
||||
compiled = ERB.new(export_template(name)).result(binding)
|
||||
write_file target, compiled
|
||||
end
|
||||
|
||||
@@ -136,9 +81,7 @@ private ######################################################################
|
||||
def write_file(filename, contents)
|
||||
say "writing: #{filename}"
|
||||
|
||||
filename = File.join(location, filename) unless Pathname.new(filename).absolute?
|
||||
|
||||
File.open(filename, "w") do |file|
|
||||
File.open(File.join(location, filename), "w") do |file|
|
||||
file.puts contents
|
||||
end
|
||||
end
|
||||
|
||||
@@ -13,16 +13,7 @@ class Foreman::Export::Inittab < Foreman::Export::Base
|
||||
1.upto(engine.formation[name]) do |num|
|
||||
id = app.slice(0, 2).upcase + sprintf("%02d", index)
|
||||
port = engine.port_for(process, num)
|
||||
|
||||
commands = []
|
||||
commands << "cd #{engine.root}"
|
||||
commands << "export PORT=#{port}"
|
||||
engine.env.each_pair do |var, env|
|
||||
commands << "export #{var.upcase}=#{shell_quote(env)}"
|
||||
end
|
||||
commands << "#{process.command} >> #{log}/#{name}-#{num}.log 2>&1"
|
||||
|
||||
inittab << "#{id}:4:respawn:/bin/su - #{user} -c '#{commands.join(";")}'"
|
||||
inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log}/#{name}-#{num}.log 2>&1'"
|
||||
index += 1
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,8 +7,6 @@ class Foreman::Export::Launchd < Foreman::Export::Base
|
||||
super
|
||||
engine.each_process do |name, process|
|
||||
1.upto(engine.formation[name]) do |num|
|
||||
port = engine.port_for(process, num)
|
||||
command_args = process.command.split(" ")
|
||||
write_template "launchd/launchd.plist.erb", "#{app}-#{name}-#{num}.plist", binding
|
||||
end
|
||||
end
|
||||
|
||||
@@ -6,7 +6,7 @@ class Foreman::Export::Upstart < Foreman::Export::Base
|
||||
def export
|
||||
super
|
||||
|
||||
(Dir["#{location}/#{app}-*.conf"] << "#{location}/#{app}.conf").each do |file|
|
||||
Dir["#{location}/#{app}*.conf"].each do |file|
|
||||
clean file
|
||||
end
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require "foreman"
|
||||
require "rubygems"
|
||||
|
||||
class Foreman::Process
|
||||
|
||||
@@ -20,21 +21,6 @@ class Foreman::Process
|
||||
@options[:env] ||= {}
|
||||
end
|
||||
|
||||
# Get environment-expanded command for a +Process+
|
||||
#
|
||||
# @param [Hash] custom_env ({}) Environment variables to merge with defaults
|
||||
#
|
||||
# @return [String] The expanded command
|
||||
#
|
||||
def expanded_command(custom_env={})
|
||||
env = @options[:env].merge(custom_env)
|
||||
expanded_command = command.dup
|
||||
env.each do |key, val|
|
||||
expanded_command.gsub!("$#{key}", val)
|
||||
end
|
||||
expanded_command
|
||||
end
|
||||
|
||||
# Run a +Process+
|
||||
#
|
||||
# @param [Hash] options
|
||||
@@ -45,55 +31,31 @@ class Foreman::Process
|
||||
# @returns [Fixnum] pid The +pid+ of the process
|
||||
#
|
||||
def run(options={})
|
||||
env = @options[:env].merge(options[:env] || {})
|
||||
env = options[:env] ? @options[:env].merge(options[:env]) : @options[:env]
|
||||
output = options[:output] || $stdout
|
||||
|
||||
if Foreman.windows?
|
||||
Dir.chdir(cwd) do
|
||||
Process.spawn env, expanded_command(env), :out => output, :err => output
|
||||
Process.spawn env, command, :out => output, :err => output, :new_pgroup => true
|
||||
end
|
||||
elsif Foreman.jruby_18?
|
||||
require "posix/spawn"
|
||||
wrapped_command = "#{Foreman.runner} -d '#{cwd}' -p -- #{command}"
|
||||
POSIX::Spawn.spawn env, wrapped_command, :out => output, :err => output
|
||||
elsif Foreman.ruby_18?
|
||||
fork do
|
||||
$stdout.reopen output
|
||||
$stderr.reopen output
|
||||
env.each { |k,v| ENV[k] = v }
|
||||
wrapped_command = "#{Foreman.runner} -d '#{cwd}' -p -- #{command}"
|
||||
Kernel.exec wrapped_command
|
||||
elsif Foreman.jruby?
|
||||
Dir.chdir(cwd) do
|
||||
require "posix/spawn"
|
||||
POSIX::Spawn.spawn env, command, :out => output, :err => output, :pgroup => 0
|
||||
end
|
||||
else
|
||||
wrapped_command = "#{Foreman.runner} -d '#{cwd}' -p -- #{command}"
|
||||
Process.spawn env, wrapped_command, :out => output, :err => output
|
||||
Dir.chdir(cwd) do
|
||||
Process.spawn env, command, :out => output, :err => output, :pgroup => 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Exec a +Process+
|
||||
#
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options :env ({}) Environment variables to set for this execution
|
||||
#
|
||||
# @return Does not return
|
||||
def exec(options={})
|
||||
env = @options[:env].merge(options[:env] || {})
|
||||
env.each { |k, v| ENV[k] = v }
|
||||
Dir.chdir(cwd)
|
||||
Kernel.exec expanded_command(env)
|
||||
end
|
||||
|
||||
# Send a signal to this +Process+
|
||||
#
|
||||
# @param [String] signal The signal to send
|
||||
#
|
||||
def kill(signal)
|
||||
if Foreman.windows?
|
||||
pid && Process.kill(signal, pid)
|
||||
else
|
||||
pid && Process.kill("-#{signal}", pid)
|
||||
end
|
||||
pid && Process.kill(signal, -1 * pid)
|
||||
rescue Errno::ESRCH
|
||||
false
|
||||
end
|
||||
@@ -114,12 +76,10 @@ class Foreman::Process
|
||||
!alive?
|
||||
end
|
||||
|
||||
# Returns the working directory for this +Process+
|
||||
#
|
||||
# @returns [String]
|
||||
#
|
||||
private
|
||||
|
||||
def cwd
|
||||
File.expand_path(@options[:cwd] || ".")
|
||||
@options[:cwd] || "."
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -82,8 +82,8 @@ class Foreman::Procfile
|
||||
private
|
||||
|
||||
def parse(filename)
|
||||
File.read(filename).gsub("\r\n","\n").split("\n").map do |line|
|
||||
if line =~ /^([A-Za-z0-9_-]+):\s*(.+)$/
|
||||
File.read(filename).split("\n").map do |line|
|
||||
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
||||
[$1, $2]
|
||||
end
|
||||
end.compact
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Foreman
|
||||
|
||||
VERSION = "0.63.0"
|
||||
VERSION = "0.48.0.pre2"
|
||||
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "FOREMAN" "1" "January 2013" "Foreman 0.61.0" "Foreman Manual"
|
||||
.TH "FOREMAN" "1" "April 2012" "Foreman 0.46.0" "Foreman Manual"
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBforeman\fR \- manage Procfile\-based applications
|
||||
@@ -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\.
|
||||
.
|
||||
@@ -86,7 +90,7 @@ Specify the user the application should be run as\. Defaults to the app name
|
||||
These options control all modes of foreman\'s operation\.
|
||||
.
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-root\fR
|
||||
\fB\-d\fR, \fB\-\-directory\fR
|
||||
Specify an alternate application root\. This defaults to the directory containing the Procfile\.
|
||||
.
|
||||
.TP
|
||||
@@ -230,7 +234,7 @@ Run one process type from the application defined in a specific Procfile:
|
||||
.
|
||||
.nf
|
||||
|
||||
$ foreman start alpha \-f ~/myapp/Procfile
|
||||
$ foreman start alpha \-p ~/myapp/Procfile
|
||||
.
|
||||
.fi
|
||||
.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -80,7 +84,7 @@ The following options control how the application is run:
|
||||
|
||||
These options control all modes of foreman's operation.
|
||||
|
||||
* `-d`, `--root`:
|
||||
* `-d`, `--directory`:
|
||||
Specify an alternate application root. This defaults to the directory
|
||||
containing the Procfile.
|
||||
|
||||
|
||||
@@ -50,7 +50,7 @@ describe "Foreman::CLI", :fakefs do
|
||||
describe "check" do
|
||||
it "with a valid Procfile displays the jobs" do
|
||||
write_procfile
|
||||
foreman("check").should == "valid procfile detected (alpha, bravo, foo_bar, foo-bar)\n"
|
||||
foreman("check").should == "valid procfile detected (alpha, bravo)\n"
|
||||
end
|
||||
|
||||
it "with a blank Procfile displays an error" do
|
||||
@@ -72,25 +72,6 @@ describe "Foreman::CLI", :fakefs do
|
||||
it "includes the environment" do
|
||||
forked_foreman("run #{resource_path("bin/env FOO")} -e #{resource_path(".env")}").should == "bar\n"
|
||||
end
|
||||
|
||||
it "can run a command from the Procfile" do
|
||||
forked_foreman("run -f #{resource_path("Procfile")} test").should == "testing\n"
|
||||
end
|
||||
|
||||
it "exits with the same exit code as the command" do
|
||||
fork_and_get_exitstatus("run echo 1").should == 0
|
||||
fork_and_get_exitstatus("run date 'invalid_date'").should == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "version" do
|
||||
it "displays gem version" do
|
||||
foreman("version").chomp.should == Foreman::VERSION
|
||||
end
|
||||
|
||||
it "displays gem version on shortcut command" do
|
||||
foreman("-v").chomp.should == Foreman::VERSION
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -90,14 +90,6 @@ describe "Foreman::Engine", :fakefs do
|
||||
subject.env["OTHER"].should == 'escaped"quote'
|
||||
end
|
||||
|
||||
it "should handle multiline strings" do
|
||||
File.open("/tmp/env", "w") do |f|
|
||||
f.puts 'FOO="bar\nbaz"'
|
||||
end
|
||||
subject.load_env "/tmp/env"
|
||||
subject.env["FOO"].should == "bar\nbaz"
|
||||
end
|
||||
|
||||
it "should fail if specified and doesnt exist" do
|
||||
lambda { subject.load_env "/tmp/env" }.should raise_error(Errno::ENOENT)
|
||||
end
|
||||
|
||||
@@ -18,14 +18,4 @@ describe Foreman::Export::Launchd, :fakefs do
|
||||
File.read("/tmp/init/app-bravo-1.plist").should == example_export_file("launchd/launchd-b.default")
|
||||
end
|
||||
|
||||
context "with multiple command arguments" do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", "charlie") }
|
||||
|
||||
it "splits each command argument" do
|
||||
launchd.export
|
||||
File.read("/tmp/init/app-alpha-1.plist").should == example_export_file("launchd/launchd-c.default")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -29,32 +29,16 @@ describe Foreman::Export::Upstart, :fakefs do
|
||||
mock(FileUtils).rm("/tmp/init/app-alpha-1.conf")
|
||||
mock(FileUtils).rm("/tmp/init/app-bravo.conf")
|
||||
mock(FileUtils).rm("/tmp/init/app-bravo-1.conf")
|
||||
mock(FileUtils).rm("/tmp/init/app-foo-bar.conf")
|
||||
mock(FileUtils).rm("/tmp/init/app-foo-bar-1.conf")
|
||||
mock(FileUtils).rm("/tmp/init/app-foo_bar.conf")
|
||||
mock(FileUtils).rm("/tmp/init/app-foo_bar-1.conf")
|
||||
|
||||
upstart.export
|
||||
upstart.export
|
||||
end
|
||||
|
||||
it "does not delete exported files for similarly named applications" do
|
||||
FileUtils.mkdir_p "/tmp/init"
|
||||
|
||||
["app2", "app2-alpha", "app2-alpha-1"].each do |name|
|
||||
path = "/tmp/init/#{name}.conf"
|
||||
FileUtils.touch(path)
|
||||
dont_allow(FileUtils).rm(path)
|
||||
end
|
||||
|
||||
upstart.export
|
||||
end
|
||||
|
||||
it "quotes and escapes environment variables" do
|
||||
engine.env['KEY'] = 'd"\|d'
|
||||
upstart.export
|
||||
"foobarfoo".should include "bar"
|
||||
File.read("/tmp/init/app-alpha-1.conf").should =~ /KEY=d\\"\\\\\\\|d/
|
||||
File.read("/tmp/init/app-alpha-1.conf").should =~ /KEY="d\\"\\\\\\\|d/
|
||||
end
|
||||
|
||||
context "with a formation" do
|
||||
|
||||
@@ -41,7 +41,7 @@ describe Foreman::Process do
|
||||
|
||||
it "should output utf8 properly" do
|
||||
process = Foreman::Process.new(resource_path("bin/utf8"))
|
||||
run(process).should == "\xFF\x03\n".force_encoding('binary')
|
||||
run(process).should == "\xFF\x03\n"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -16,10 +16,8 @@ describe Foreman::Procfile, :fakefs do
|
||||
it "loads a passed-in Procfile" do
|
||||
write_procfile
|
||||
procfile = Foreman::Procfile.new("Procfile")
|
||||
procfile["alpha"].should == "./alpha"
|
||||
procfile["bravo"].should == "./bravo"
|
||||
procfile["foo-bar"].should == "./foo-bar"
|
||||
procfile["foo_bar"].should == "./foo_bar"
|
||||
procfile["alpha"].should == "./alpha"
|
||||
procfile["bravo"].should == "./bravo"
|
||||
end
|
||||
|
||||
it "can have a process appended to it" do
|
||||
|
||||
@@ -42,40 +42,5 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
|
||||
process.group = "app-bravo"
|
||||
end
|
||||
|
||||
app.process("foo_bar-1") do |process|
|
||||
process.start_command = "./foo_bar"
|
||||
|
||||
process.working_dir = "/tmp/app"
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT"=>"5200"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
process.stop_grace_time = 45.seconds
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-foo_bar-1.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
children.stop_command "kill {{PID}}"
|
||||
end
|
||||
|
||||
process.group = "app-foo_bar"
|
||||
end
|
||||
|
||||
app.process("foo-bar-1") do |process|
|
||||
process.start_command = "./foo-bar"
|
||||
|
||||
process.working_dir = "/tmp/app"
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT"=>"5300"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
process.stop_grace_time = 45.seconds
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-foo-bar-1.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
|
||||
children.stop_command "kill {{PID}}"
|
||||
end
|
||||
|
||||
process.group = "app-foo-bar"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# ----- foreman app processes -----
|
||||
AP01:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5000;./alpha >> /var/log/app/alpha-1.log 2>&1'
|
||||
AP02:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5001;./alpha >> /var/log/app/alpha-2.log 2>&1'
|
||||
AP01:4:respawn:/bin/su - app -c 'PORT=5000 ./alpha >> /var/log/app/alpha-1.log 2>&1'
|
||||
AP02:4:respawn:/bin/su - app -c 'PORT=5001 ./alpha >> /var/log/app/alpha-2.log 2>&1'
|
||||
# ----- end foreman app processes -----
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# ----- foreman app processes -----
|
||||
AP01:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5000;./alpha >> /var/log/app/alpha-1.log 2>&1'
|
||||
AP02:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5100;./bravo >> /var/log/app/bravo-1.log 2>&1'
|
||||
AP03:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5200;./foo_bar >> /var/log/app/foo_bar-1.log 2>&1'
|
||||
AP04:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5300;./foo-bar >> /var/log/app/foo-bar-1.log 2>&1'
|
||||
AP01:4:respawn:/bin/su - app -c 'PORT=5000 ./alpha >> /var/log/app/alpha-1.log 2>&1'
|
||||
AP02:4:respawn:/bin/su - app -c 'PORT=5100 ./bravo >> /var/log/app/bravo-1.log 2>&1'
|
||||
# ----- end foreman app processes -----
|
||||
|
||||
@@ -4,11 +4,6 @@
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>app-alpha-1</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PORT</key>
|
||||
<string>5000</string>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>./alpha</string>
|
||||
@@ -17,8 +12,6 @@
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/var/log/app/app-alpha-1.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/log/app/app-alpha-1.log</string>
|
||||
<key>UserName</key>
|
||||
|
||||
@@ -4,11 +4,6 @@
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>app-bravo-1</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PORT</key>
|
||||
<string>5100</string>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>./bravo</string>
|
||||
@@ -17,8 +12,6 @@
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/var/log/app/app-bravo-1.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/log/app/app-bravo-1.log</string>
|
||||
<key>UserName</key>
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>app-alpha-1</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PORT</key>
|
||||
<string>5000</string>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>./alpha</string>
|
||||
<string>charlie</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/var/log/app/app-alpha-1.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/log/app/app-alpha-1.log</string>
|
||||
<key>UserName</key>
|
||||
<string>app</string>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/tmp/app</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -8,7 +8,7 @@ stdout_logfile=/var/log/app/alpha-1.log
|
||||
stderr_logfile=/var/log/app/alpha-1.error.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5000
|
||||
environment=PORT="5000"
|
||||
[program:app-bravo-1]
|
||||
command=./bravo
|
||||
autostart=true
|
||||
@@ -18,27 +18,7 @@ stdout_logfile=/var/log/app/bravo-1.log
|
||||
stderr_logfile=/var/log/app/bravo-1.error.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5100
|
||||
[program:app-foo_bar-1]
|
||||
command=./foo_bar
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopsignal=QUIT
|
||||
stdout_logfile=/var/log/app/foo_bar-1.log
|
||||
stderr_logfile=/var/log/app/foo_bar-1.error.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5200
|
||||
[program:app-foo-bar-1]
|
||||
command=./foo-bar
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopsignal=QUIT
|
||||
stdout_logfile=/var/log/app/foo-bar-1.log
|
||||
stderr_logfile=/var/log/app/foo-bar-1.error.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5300
|
||||
environment=PORT="5100"
|
||||
|
||||
[group:app]
|
||||
programs=app-alpha-1,app-bravo-1,app-foo_bar-1,app-foo-bar-1
|
||||
programs=app-alpha-1,app-bravo-1
|
||||
|
||||
@@ -8,7 +8,7 @@ stdout_logfile=/var/log/app/alpha-1.log
|
||||
stderr_logfile=/var/log/app/alpha-1.error.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5000
|
||||
environment=PORT="5000"
|
||||
[program:app-alpha-2]
|
||||
command=./alpha
|
||||
autostart=true
|
||||
@@ -18,7 +18,7 @@ stdout_logfile=/var/log/app/alpha-2.log
|
||||
stderr_logfile=/var/log/app/alpha-2.error.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5001
|
||||
environment=PORT="5001"
|
||||
|
||||
[group:app]
|
||||
programs=app-alpha-1,app-alpha-2
|
||||
|
||||
@@ -6,7 +6,3 @@ bash << "EOF"
|
||||
EOF
|
||||
|
||||
end script
|
||||
|
||||
start on runlevel [2345]
|
||||
|
||||
stop on runlevel [016]
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require "rubygems"
|
||||
|
||||
require "simplecov"
|
||||
SimpleCov.start do
|
||||
add_filter "/spec/"
|
||||
@@ -52,16 +54,11 @@ def fork_and_capture(&blk)
|
||||
Process.wait pid
|
||||
buffer = ""
|
||||
until rd.eof?
|
||||
p [:foo]
|
||||
buffer += rd.gets
|
||||
end
|
||||
end
|
||||
|
||||
def fork_and_get_exitstatus(args)
|
||||
pid = Process.spawn("bundle exec bin/foreman #{args}", :out => "/dev/null", :err => "/dev/null")
|
||||
Process.wait(pid)
|
||||
$?.exitstatus
|
||||
end
|
||||
|
||||
def mock_exit(&block)
|
||||
block.should raise_error(SystemExit)
|
||||
end
|
||||
@@ -79,8 +76,6 @@ def write_procfile(procfile="Procfile", alpha_env="")
|
||||
file.puts "alpha: ./alpha" + " #{alpha_env}".rstrip
|
||||
file.puts "\n"
|
||||
file.puts "bravo:\t./bravo"
|
||||
file.puts "foo_bar:\t./foo_bar"
|
||||
file.puts "foo-bar:\t./foo-bar"
|
||||
end
|
||||
File.expand_path(procfile)
|
||||
end
|
||||
|
||||
@@ -32,7 +32,7 @@ def latest_release
|
||||
end
|
||||
|
||||
def newer_release
|
||||
tags = %x{ git tag --contains v#{latest_release} | grep -v pre }.split("\n").sort_by do |tag|
|
||||
tags = %x{ git tag --contains v#{latest_release} }.split("\n").sort_by do |tag|
|
||||
Gem::Version.new(tag[1..-1])
|
||||
end
|
||||
tags[1]
|
||||
@@ -60,6 +60,7 @@ end
|
||||
|
||||
desc "Cut a release"
|
||||
task :release do
|
||||
Rake::Task["authors"].invoke
|
||||
Rake::Task["changelog"].invoke
|
||||
Rake::Task["pages"].invoke
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user