Compare commits

..

42 Commits

Author SHA1 Message Date
David Dollar
de54f6a5a5 0.37.1 2012-01-29 14:03:45 -05:00
David Dollar
0dff116340 use a 1.8-compatbiel method for IO.pipe 2012-01-29 14:01:13 -05:00
David Dollar
e053dc8434 cleanup 2012-01-29 13:58:16 -05:00
David Dollar
20f6ba1563 use binary pipes 2012-01-29 13:56:57 -05:00
David Dollar
557a08ea77 add utf8 test to engine 2012-01-29 13:56:51 -05:00
David Dollar
8eccc819d6 remove nonfunctional utf8 test 2012-01-29 13:56:42 -05:00
David Dollar
347d4a0184 factor out poll_readers 2012-01-29 13:56:35 -05:00
David Dollar
0147f5d284 set up example procfile with utf8 item 2012-01-29 13:56:22 -05:00
David Dollar
1da034ce66 try to add failing test for utf8 2012-01-29 10:03:31 -05:00
David Dollar
5fc7552572 refactor resource_file 2012-01-29 10:03:14 -05:00
David Dollar
083efe3ae9 sleep longer after loading scripts 2012-01-29 10:03:03 -05:00
David Dollar
9d2382a2d2 remove autotest 2012-01-29 02:06:01 -05:00
David Dollar
f731daae1a update docs 2012-01-29 01:55:44 -05:00
David Dollar
b7f0e3f573 typo 2012-01-29 01:55:14 -05:00
David Dollar
e7c523dab7 cleanup 2012-01-29 01:52:30 -05:00
David Dollar
9f4f15a29c update docs 2012-01-29 01:51:52 -05:00
David Dollar
b2dc89c50e fix up authors 2012-01-29 01:51:00 -05:00
David Dollar
389bf05800 cleanup 2012-01-29 01:47:05 -05:00
David Dollar
a632a62efd update docs 2012-01-29 01:45:57 -05:00
David Dollar
2edf6e1c68 pedantry 2012-01-29 01:41:21 -05:00
David Dollar
56158e881b add changelog 2012-01-29 01:40:36 -05:00
David Dollar
6b5ed495d5 fix up packaging after moving tasks 2012-01-29 01:38:21 -05:00
David Dollar
9652c285f1 fix up changelog task 2012-01-29 01:32:20 -05:00
David Dollar
ba171cc10d curate changelog 2012-01-29 01:26:21 -05:00
David Dollar
ebb191adba better changelog task 2012-01-29 01:26:14 -05:00
David Dollar
8c3ef1a7af 0.37.0 2012-01-29 01:13:27 -05:00
David Dollar
cf69c31ae3 raw changelog 2012-01-29 01:13:02 -05:00
David Dollar
e16a35da4b fix changelog script 2012-01-29 01:12:56 -05:00
David Dollar
2490dd2a5b put an entire line of output inside a single mutex so we don't cross the streams 2012-01-29 00:09:44 -05:00
David Dollar
2705520496 fix race condition with process termination 2012-01-29 00:09:28 -05:00
David Dollar
522fee5e9e pedantry 2012-01-29 00:08:58 -05:00
David Dollar
c462473a25 fix simplecov 2012-01-28 18:53:43 -05:00
David Dollar
224fe94dc2 add more tests 2012-01-28 18:48:26 -05:00
David Dollar
7cb73f5c36 rearrange tasks, add coverage 2012-01-28 18:48:13 -05:00
David Dollar
1537ddbcc9 tweak simplecov entry 2012-01-28 16:27:02 -05:00
David Dollar
b04c81dd06 add simplecov 2012-01-28 16:26:12 -05:00
David Dollar
ae22d34967 Merge pull request #144 from technomancy/debian
Drop the S3 publishing rake tasks.
2012-01-26 17:02:42 -08:00
Phil Hagelberg
9a359efbf7 Drop the S3 publishing rake tasks. 2012-01-26 17:00:07 -08:00
David Dollar
9632227d29 0.37.0.pre5 2012-01-24 08:34:48 -08:00
David Dollar
288d118ca4 Merge pull request #142 from apollo13/master
Fix bash string comparision
2012-01-24 08:29:38 -08:00
David Dollar
576455b8d7 rename readme 2012-01-24 08:14:50 -08:00
Florian Apolloner
e1d3955d3c Fix the test for an empty string in bin/runner 2012-01-24 09:51:16 +01:00
37 changed files with 625 additions and 284 deletions

2
.gitignore vendored
View File

@@ -5,4 +5,4 @@
/man/*.markdown
/pkg
/tags
/vendor
/vendor

View File

@@ -1,11 +0,0 @@
0.26.1 12/05/2011 6160246da0fafe9cf8fde188d94bbc6babc667dc
==========================================================
Merge pull request #103 from csquared/load_env_from_irb [David Dollar]
refactor load_env to apply_environment [Chris Continanza]
rename load! to load_env! [Chris Continanza]
use ./.env as default [Chris Continanza]
load contents from env file [Chris Continanza]
refactor engine to expose env methods [Chris Continanza]
disable email notifications [David Dollar]
add travis config [David Dollar]

139
Changelog.md Normal file
View File

@@ -0,0 +1,139 @@
## 0.37.0 (2012-01-29)
* put an entire line of output inside a single mutex so we don't cross the streams [David Dollar]
* fix race condition with process termination [David Dollar]
* allow external custom exporters [Chris Lowder]
* fix the test for an empty string in bin/runner [Florian Apolloner]
* ensure we have non-nil data, fixes #111 [David Dollar]
* make sure error method exists, fixes #104 [David Dollar]
* clean up chdir usage [David Dollar]
* normalize platform names [David Dollar]
* add windows support [David Dollar]
* add jruby support [David Dollar]
* pass basedir along to the runner script [David Dollar]
* harden runner script [David Dollar]
* add many missing specs [brainopia]
* clean up fakefs usage in specs [brainopia]
* runit creates a full path to export directory. [Fletcher Nichol]
## 0.36.1 (2012-01-18)
* 0.36.1 [David Dollar]
* bump term-ansicolor in gemspec [David Dollar]
## 0.36.0 (2012-01-17)
* 0.36.0 [David Dollar]
* sync the writer stream [David Dollar]
* capture stderr as well [David Dollar]
## 0.35.0 (2012-01-16)
* update rake [David Dollar]
* 0.35.0 [David Dollar]
* Merge pull request #132 from Viximo/feature/concurrency [David Dollar]
* Fix export specs [Matt Griffin]
* Merge branch 'master' of https://github.com/michaeldwan/foreman into feature/concurrency [Matt Griffin]
* default process concurrency is 0 when concurrency options specified, otherwise default concurrency is 1 [Michael Dwan]
## 0.34.1 (2012-01-16)
* 0.34.1 [David Dollar]
* fix missing start desc [David Dollar]
## 0.34.0 (2012-01-16)
* 0.34.0 [David Dollar]
* update man page [David Dollar]
* update docs for -d [David Dollar]
* Merge pull request #101 from ndbroadbent/foreman [David Dollar]
* Wrap around to the first colour when all the colours are used [Craig R Webster]
* run specs in random order [David Dollar]
* update rspec [David Dollar]
* pedantry [David Dollar]
* Set executable bit on runit run scripts. [Matthijs Langenberg]
* Merge pull request #114 from gburt/master [David Dollar]
* add more colors [Gabriel Burt]
* Added option to specify app_root, if executing a Procfile from a shared location [Nathan Broadbent]
## 0.33.1 (2012-01-16)
* 0.33.1 [David Dollar]
* Merge pull request #129 from fnichol/resolve-home-template [David Dollar]
* Expand template path under user's home directory. [Fletcher Nichol]
## 0.33.0 (2012-01-15)
* 0.33.0 [David Dollar]
* Revert "Merge pull request #125 from brainopia/master" [David Dollar]
## 0.32.0 (2012-01-12)
* 0.32.0 [David Dollar]
* Merge pull request #125 from brainopia/master [David Dollar]
* Merge pull request #121 from Viximo/feature/run [David Dollar]
* Return some whitespace that was accidentally removed [Matt Griffin]
* Steal the run method back from Thor so that it can be used in place for exec for running commands in the foreman environment. [Matt Griffin]
* Remove old cruft [brainopia]
* In case someone wants to use bin/runner directly [brainopia]
* Fix for double fork [brainopia]
* Use ruby exec which works with escaped cmd and replaces shell [brainopia]
* Fix foreman to work with cmds containing pipes and redirects [brainopia]
* Add "exec" action to allow execution of arbitrary commands with the app's environment. [Matt Griffin]
* tweak readme [David Dollar]
## 0.31.0 (2012-01-04)
* 0.31.0 [David Dollar]
* make fork more robust [David Dollar]
* remove unnecessary debug [David Dollar]
* add more information when shutting down [David Dollar]
* Merge pull request #110 from lstoll/master [David Dollar]
* Use different port ranges for each process type [Lincoln Stoll]
## 0.30.1 (2011-12-23)
* 0.30.1 [David Dollar]
* require thread for mutex [David Dollar]
## 0.30.0 (2011-12-22)
* 0.30.0 [David Dollar]
* compatibility with ruby 1.8 [David Dollar]
## 0.29.0 (2011-12-22)
* 0.29.0 [David Dollar]
* 0.28.0.pre2 [David Dollar]
* fix pipe error [David Dollar]
* 0.28.0.pre1 [David Dollar]
* Merge branch 'fork' [David Dollar]
* wip [David Dollar]
* wip [David Dollar]
* wip [David Dollar]
* wip [David Dollar]
* wip [David Dollar]
## 0.27.0 (2011-12-05)
* 0.27.0 [David Dollar]
* add changelog [David Dollar]
* Merge pull request #103 from csquared/load_env_from_irb [David Dollar]
* refactor load_env to apply_environment [Chris Continanza]
* rename load! to load_env! [Chris Continanza]
* use ./.env as default [Chris Continanza]
* load contents from env file [Chris Continanza]
* refactor engine to expose env methods [Chris Continanza]
* disable email notifications [David Dollar]
* add travis config [David Dollar]
## 0.26.1 2011-12-05
* Merge pull request #103 from csquared/load_env_from_irb [David Dollar]
* refactor load_env to apply_environment [Chris Continanza]
* rename load! to load_env! [Chris Continanza]
* use ./.env as default [Chris Continanza]
* load contents from env file [Chris Continanza]
* refactor engine to expose env methods [Chris Continanza]
* disable email notifications [David Dollar]
* add travis config [David Dollar]

View File

@@ -15,10 +15,10 @@ group :development do
gem 'rake'
gem 'ronn'
gem 'fakefs', '~> 0.3.2'
gem 'rcov', '~> 0.9.8'
gem 'rr', '~> 1.0.2'
gem 'rspec', '~> 2.0'
gem 'ZenTest'
gem 'aws-s3'
gem "rubyzip"
gem "simplecov", :require => false
end

View File

@@ -1,7 +1,7 @@
PATH
remote: .
specs:
foreman (0.37.0.pre4)
foreman (0.37.1)
term-ansicolor (~> 1.0.7)
thor (>= 0.13.6)
@@ -20,6 +20,7 @@ GEM
hpricot (0.8.2)
hpricot (0.8.2-java)
mime-types (1.16)
multi_json (1.0.4)
mustache (0.11.2)
parka (0.6.2)
crack
@@ -27,8 +28,6 @@ GEM
thor
posix-spawn (0.3.6)
rake (0.9.2.2)
rcov (0.9.8)
rcov (0.9.8-java)
rdiscount (1.6.5)
rest-client (1.6.1)
mime-types (>= 1.16)
@@ -46,6 +45,10 @@ GEM
diff-lcs (~> 1.1.2)
rspec-mocks (2.8.0)
rubyzip (0.9.4)
simplecov (0.5.4)
multi_json (~> 1.0.3)
simplecov-html (~> 0.5.3)
simplecov-html (0.5.3)
term-ansicolor (1.0.7)
thor (0.14.6)
win32console (1.3.0-x86-mingw32)
@@ -64,9 +67,9 @@ DEPENDENCIES
parka
posix-spawn (~> 0.3.6)
rake
rcov (~> 0.9.8)
ronn
rr (~> 1.0.2)
rspec (~> 2.0)
rubyzip
simplecov
win32console (~> 1.3.0)

View File

@@ -25,6 +25,7 @@ Manage Procfile-based applications
* [man page](http://ddollar.github.com/foreman)
* [wiki](http://github.com/ddollar/foreman/wiki)
* [changelog](https://github.com/ddollar/foreman/blob/master/Changelog.md)
## Authors
@@ -32,7 +33,7 @@ Manage Procfile-based applications
David Dollar
#### Patches contributed by
Adam Wiggins, Chris Continanza, Chris Lowder, Craig R Webster, Dan Farina, Dan Peterson, David Dollar, Fletcher Nichol, Gabriel Burt, Gamaliel Toro, Greg Reinacker, Hugues Le Gendre, Hunter Nield, Iain Hecker, Jay Zeschin, Keith Rarick, Khaja Minhajuddin, Lincoln Stoll, Marcos Muino Garcia, Mark McGranaghan, Matt Griffin, Matt Haynes, Matthijs Langenberg, Michael Dwan, Michael van Rooijen, Mike Javorski, Nathan Broadbent, Nathan L Smith, Nick Zadrozny, Phil Hagelberg, Ricardo Chimal, Jr, Thom May, Tom Ward, brainopia, clifff, jc00ke
Adam Wiggins, Chris Continanza, Chris Lowder, Craig R Webster, Dan Farina, Dan Peterson, David Dollar, Fletcher Nichol, Florian Apolloner, Gabriel Burt, Gamaliel Toro, Greg Reinacker, Hugues Le Gendre, Hunter Nield, Iain Hecker, Jay Zeschin, Keith Rarick, Khaja Minhajuddin, Lincoln Stoll, Marcos Muino Garcia, Mark McGranaghan, Matt Griffin, Matt Haynes, Matthijs Langenberg, Michael Dwan, Michael van Rooijen, Mike Javorski, Nathan Broadbent, Nathan L Smith, Nick Zadrozny, Phil Hagelberg, Ricardo Chimal, Jr, Thom May, Tom Ward, brainopia, clifff, jc00ke
## License

190
Rakefile
View File

@@ -1,192 +1,6 @@
require "rake"
require "rspec"
require "rspec/core/rake_task"
$:.unshift File.expand_path("../lib", __FILE__)
require "foreman"
task :default => :spec
task :release => :man
desc "Run all specs"
RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = 'spec/**/*_spec.rb'
end
desc "Generate RCov code coverage report"
task :rcov => "rcov:build" do
%x{ open coverage/index.html }
end
RSpec::Core::RakeTask.new("rcov:build") do |t|
t.pattern = 'spec/**/*_spec.rb'
t.rcov = true
t.rcov_opts = [ "--exclude", ".bundle", "--exclude", "spec" ]
end
desc 'Build the manual'
task :man do
ENV['RONN_MANUAL'] = "Foreman Manual"
ENV['RONN_ORGANIZATION'] = "Foreman #{Foreman::VERSION}"
sh "ronn -w -s toc -r5 --markdown man/*.ronn"
end
desc "Commit the manual to git"
task "man:commit" => :man do
sh "git add README.markdown"
sh "git commit -m 'update readme' || echo 'nothing to commit'"
end
desc "Generate the Github docs"
task :pages => "man:commit" do
sh %{
cp man/foreman.1.html /tmp/foreman.1.html
git checkout gh-pages
rm ./index.html
cp /tmp/foreman.1.html ./index.html
git add -u index.html
git commit -m "saving man page to github docs"
git push origin -f gh-pages
git checkout master
}
end
task :authors do
authors = %x{ git log --pretty=format:"%an" | sort -u }.split("\n")
puts authors.join(", ")
end
## dist
require "erb"
require "fileutils"
require "tmpdir"
def assemble(source, target, perms=0644)
FileUtils.mkdir_p(File.dirname(target))
File.open(target, "w") do |f|
f.puts ERB.new(File.read(source)).result(binding)
end
File.chmod(perms, target)
end
def assemble_distribution(target_dir=Dir.pwd)
distribution_files.each do |source|
target = source.gsub(/^#{project_root}/, target_dir)
FileUtils.mkdir_p(File.dirname(target))
FileUtils.cp(source, target)
end
end
GEM_BLACKLIST = %w( bundler foreman )
def assemble_gems(target_dir=Dir.pwd)
lines = %x{ bundle show }.strip.split("\n")
raise "error running bundler" unless $?.success?
%x{ env BUNDLE_WITHOUT="development:test" bundle show }.split("\n").each do |line|
if line =~ /^ \* (.*?) \((.*?)\)/
next if GEM_BLACKLIST.include?($1)
puts "vendoring: #{$1}-#{$2}"
gem_dir = %x{ bundle show #{$1} }.strip
FileUtils.mkdir_p "#{target_dir}/vendor/gems"
%x{ cp -R "#{gem_dir}" "#{target_dir}/vendor/gems" }
end
end.compact
end
def beta?
Foreman::VERSION.to_s =~ /pre/
end
def clean(file)
rm file if File.exists?(file)
end
def distribution_files(type=nil)
require "foreman/distribution"
base_files = Foreman::Distribution.files
type_files = type ?
Dir[File.expand_path("../dist/resources/#{type}/**/*", __FILE__)] :
[]
base_files.concat(type_files)
end
def mkchdir(dir)
FileUtils.mkdir_p(dir)
Dir.chdir(dir) do |dir|
yield(File.expand_path(dir))
end
end
def pkg(filename)
File.expand_path("../pkg/#{filename}", __FILE__)
end
def project_root
File.dirname(__FILE__)
end
def resource(name)
File.expand_path("../dist/resources/#{name}", __FILE__)
end
def s3_connect
return if @s3_connected
require "aws/s3"
unless ENV["DAVID_RELEASE_ACCESS"] && ENV["DAVID_RELEASE_SECRET"]
puts "please set DAVID_RELEASE_ACCESS and DAVID_RELEASE_SECRET in your environment"
exit 1
end
AWS::S3::Base.establish_connection!(
:access_key_id => ENV["DAVID_RELEASE_ACCESS"],
:secret_access_key => ENV["DAVID_RELEASE_SECRET"]
)
@s3_connected = true
end
def store(package_file, filename, bucket="assets.foreman.io")
s3_connect
puts "storing: #{filename}"
AWS::S3::S3Object.store(filename, File.open(package_file), bucket, :access => :public_read)
end
def tempdir
Dir.mktmpdir do |dir|
Dir.chdir(dir) do
yield(dir)
end
end
end
def version
require "foreman/version"
Foreman::VERSION
end
Dir[File.expand_path("../dist/**/*.rake", __FILE__)].each do |rake|
import rake
end
task :changelog do
timestamp = Time.now.utc.strftime('%m/%d/%Y')
sha = `git log | head -1`.split(' ').last
changelog = ["#{version} #{timestamp} #{sha}"]
changelog << ('=' * changelog[0].length)
changelog << ''
last_sha = `cat Changelog | head -1`.split(' ').last
shortlog = `git log #{last_sha}..HEAD --pretty=format:'%s [%an]'`
changelog << shortlog.split("\n")
changelog.concat ['', '', '']
old_changelog = File.read('Changelog')
File.open('Changelog', 'w') do |file|
file.write(changelog.join("\n"))
file.write(old_changelog)
end
Dir[File.expand_path("../tasks/*.rake", __FILE__)].each do |task|
load task
end

View File

@@ -1 +0,0 @@
Autotest.add_discovery { "rspec2" }

View File

@@ -29,7 +29,7 @@ shift $((OPTIND-1))
command=$1
if [ "$1" == "" ]; then
if [ -z "$1" ]; then
usage
fi

View File

@@ -1,2 +1,3 @@
ticker: ruby ./ticker $PORT
error: ruby ./error
utf8: ruby ./utf8

11
data/example/utf8 Executable file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env ruby
# encoding: BINARY
$stdout.sync = true
while true
puts "\u65e5\u672c\u8a9e\u6587\u5b57\u5217"
puts "\u0915\u0932\u094d\u0907\u0928\u0643\u0637\u0628\u041a\u0430\u043b\u0438\u043d\u0430"
puts "\xff\x03"
sleep 1
end

15
dist/deb.rake vendored
View File

@@ -20,12 +20,6 @@ file pkg("/apt-#{version}/foreman-#{version}.deb") => distribution_files("deb")
deb = File.basename(t.name)
sh "ar -r #{t.name} debian-binary control.tar.gz data.tar.gz"
touch "Sources"
sh "apt-ftparchive packages . > Packages"
sh "gzip -c Packages > Packages.gz"
sh "apt-ftparchive release . > Release"
sh "gpg -abs -u 0F1B0520 -o Release.gpg Release"
end
end
@@ -37,12 +31,3 @@ task "deb:clean" do
clean pkg("foreman-#{version}.deb")
FileUtils.rm_rf("pkg/apt-#{version}") if Dir.exists?("pkg/apt-#{version}")
end
desc "Publish .deb to S3."
task "deb:release" => "deb:build" do |t|
Dir["pkg/apt-#{version}/*"].each do |file|
unless File.directory?(file)
store file, "apt/#{File.basename(file)}"
end
end
end

View File

@@ -40,22 +40,11 @@ class Foreman::CLI < Thor
method_option :port, :type => :numeric, :aliases => "-p"
method_option :user, :type => :string, :aliases => "-u"
method_option :template, :type => :string, :aliases => "-t"
method_option :concurrency, :type => :string, :aliases => "-c",
:banner => '"alpha=5,bar=3"'
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
def export(format, location=nil)
check_procfile!
begin
require "foreman/export/#{ format.tr('-', '_') }"
classy_format = classify(format)
formatter = constantize("Foreman::Export::#{ classy_format }")
rescue NameError => ex
error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
rescue LoadError => ex
error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
end
formatter = Foreman::Export.formatter(format)
formatter.new(location, engine, options).export
rescue Foreman::Export::Exception => ex
error ex.message
@@ -65,7 +54,7 @@ class Foreman::CLI < Thor
def check
error "no processes defined" unless engine.procfile.entries.length > 0
display "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
puts "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
end
desc "run COMMAND", "Run a command using your application's environment"
@@ -95,19 +84,11 @@ private ######################################################################
options[:procfile] || "Procfile"
end
def display(message)
puts message
end
def error(message)
puts "ERROR: #{message}"
exit 1
end
def procfile_exists?(procfile)
File.exist?(procfile)
end
def options
original_options = super
return original_options unless File.exists?(".foreman")

View File

@@ -58,7 +58,7 @@ private ######################################################################
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
procfile.entries.each do |entry|
reader, writer = IO.pipe
reader, writer = (IO.method(:pipe).arity == 0 ? IO.pipe : IO.pipe("BINARY"))
entry.spawn(concurrency[entry.name], writer, @directory, @environment, port_for(entry, 1, base_port)).each do |process|
running_processes[process.pid] = process
readers[process] = reader
@@ -78,27 +78,39 @@ private ######################################################################
end
def terminate_gracefully
return if @terminating
@terminating = true
info "sending SIGTERM to all processes"
kill_all "SIGTERM"
Timeout.timeout(5) { Process.waitall }
Timeout.timeout(5) do
while running_processes.length > 0
pid, status = Process.wait2
process = running_processes.delete(pid)
info "process terminated", process.name
end
end
rescue Timeout::Error
info "sending SIGKILL to all processes"
kill_all "SIGKILL"
end
def poll_readers
rs, ws = IO.select(readers.values, [], [], 1)
(rs || []).each do |r|
data = r.gets
next unless data
ps, message = data.split(",", 2)
color = colors[ps.split(".").first]
info message, ps, color
end
end
def watch_for_output
Thread.new do
require "win32console" if Foreman.windows?
begin
loop do
rs, ws = IO.select(readers.values, [], [], 1)
(rs || []).each do |r|
data = r.gets
next unless data
ps, message = data.split(",", 2)
color = colors[ps.split(".").first]
info message, ps, color
end
poll_readers
end
rescue Exception => ex
puts ex.message
@@ -112,16 +124,16 @@ private ######################################################################
process = running_processes.delete(pid)
info "process terminated", process.name
terminate_gracefully
kill_all
rescue Errno::ECHILD
end
def info(message, name="system", color=Term::ANSIColor.white)
print color
print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
print Term::ANSIColor.reset
print message.chomp
puts ""
output = ""
output += color
output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
output += Term::ANSIColor.reset
output += message.chomp
puts output
end
def print(message=nil)

View File

@@ -1,9 +1,30 @@
require "foreman"
require "foreman/helpers"
module Foreman::Export
extend Foreman::Helpers
class Exception < ::Exception; end
def self.formatter(format)
begin
require "foreman/export/#{ format.tr('-', '_') }"
classy_format = classify(format)
formatter = constantize("Foreman::Export::#{ classy_format }")
rescue NameError => ex
error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
rescue LoadError => ex
error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
end
end
def self.error(message)
raise Foreman::Export::Exception.new(message)
end
end
require "foreman/export/base"
require "foreman/export/inittab"
require "foreman/export/upstart"

View File

@@ -24,12 +24,12 @@ class Foreman::Export::Inittab < Foreman::Export::Base
inittab = inittab.join("\n") + "\n"
if fname
if location == "-"
puts inittab
else
FileUtils.mkdir_p(log_root) rescue error "could not create #{log_root}"
FileUtils.chown(user, nil, log_root) rescue error "could not chown #{log_root} to #{user}"
write_file(fname, inittab)
else
puts inittab
write_file(location, inittab)
end
end

View File

@@ -6,9 +6,8 @@ module Foreman::Helpers
# classify('job-name') # => 'JobName'
def classify(dashed_word)
dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
end
end # Tries to find a constant with the name specified in the argument string:
# Tries to find a constant with the name specified in the argument string:
#
# constantize("Module") # => Module
# constantize("Test::Unit") # => Test::Unit
@@ -28,10 +27,6 @@ module Foreman::Helpers
def constantize(camel_cased_word)
camel_cased_word = camel_cased_word.to_s
if camel_cased_word.include?('-')
camel_cased_word = classify(camel_cased_word)
end
names = camel_cased_word.split('::')
names.shift if names.empty? || names.first.empty?
@@ -47,4 +42,4 @@ module Foreman::Helpers
end
constant
end
end
end

View File

@@ -63,7 +63,7 @@ private
$stderr.reopen writer
reader.close
exec Foreman.runner, "-d", basedir, command
end
end
end
[ reader, pid ]
end

View File

@@ -1,5 +1,5 @@
module Foreman
VERSION = "0.37.0.pre4"
VERSION = "0.37.1"
end

View File

@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "FOREMAN" "1" "January 2012" "Foreman 0.33.1" "Foreman Manual"
.TH "FOREMAN" "1" "January 2012" "Foreman 0.37.0" "Foreman Manual"
.
.SH "NAME"
\fBforeman\fR \- manage Procfile\-based applications

View File

@@ -27,6 +27,15 @@ describe "Foreman::CLI", :fakefs do
describe "export" do
describe "options" do
it "uses .foreman" do
write_procfile
File.open(".foreman", "w") { |f| f.puts "concurrency: alpha=2" }
mock_export = mock(Foreman::Export::Upstart)
mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "concurrency" => "alpha=2" }) { mock_export }
mock_export.export
foreman %{ export upstart /upstart }
end
it "respects --env" do
write_procfile
write_env("envfile")
@@ -49,10 +58,18 @@ describe "Foreman::CLI", :fakefs do
describe "with a Procfile" do
before(:each) { write_procfile }
describe "with an invalid formatter" do
describe "with a formatter with a generic error" do
before do
mock(Foreman::Export).formatter("errorful") { Class.new(Foreman::Export::Base) do
def export
raise Foreman::Export::Exception.new("foo")
end
end }
end
it "prints an error" do
mock_error(subject, "Unknown export format: invalidformatter (unable to load file 'foreman/export/invalidformatter').") do
subject.export("invalidformatter")
mock_error(subject, "foo") do
subject.export("errorful")
end
end
end
@@ -76,7 +93,7 @@ describe "Foreman::CLI", :fakefs do
before { write_procfile }
it "displays the jobs" do
mock(subject).display("valid procfile detected (alpha, bravo)")
mock(subject).puts("valid procfile detected (alpha, bravo)")
subject.check
end
end

View File

@@ -83,4 +83,23 @@ describe "Foreman::Engine", :fakefs do
engine.start
end
end
describe "utf8" do
before(:each) do
File.open("Procfile", "w") do |file|
file.puts "utf8: #{resource_path("bin/utf8")}"
end
end
it "should spawn" do
stub(subject).watch_for_output
stub(subject).watch_for_termination
subject.start
sleep 1
mock(subject).info(/started with pid \d+/, "utf8.1", anything)
mock(subject).info("\xff\x03\n", "utf8.1", anything)
subject.send(:poll_readers)
subject.send(:poll_readers)
end
end
end

View File

@@ -0,0 +1,22 @@
require "spec_helper"
require "foreman/export/base"
describe "Foreman::Export::Base" do
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
let(:location) { "/tmp/init" }
let(:engine) { Foreman::Engine.new(procfile) }
let(:subject) { Foreman::Export::Base.new(location, engine) }
it "has a say method for displaying info" do
mock(subject).puts("[foreman export] foo")
subject.send(:say, "foo")
end
it "export needs to be overridden" do
lambda { subject.export }.should raise_error("export method must be overridden")
end
it "raises errors as a Foreman::Export::Exception" do
lambda { subject.send(:error, "foo") }.should raise_error(Foreman::Export::Exception, "foo")
end
end

View File

@@ -17,7 +17,14 @@ describe Foreman::Export::Bluepill, :fakefs do
normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app.pill"))
end
context "with concurrency" do
it "cleans up if exporting into an existing dir" do
mock(FileUtils).rm("/tmp/init/app.pill")
bluepill.export
bluepill.export
end
context "with concurrency" do
let(:options) { Hash[:concurrency => "alpha=2"] }
it "exports to the filesystem with concurrency" do

View File

@@ -0,0 +1,40 @@
require "spec_helper"
require "foreman/engine"
require "foreman/export/inittab"
require "tmpdir"
describe Foreman::Export::Inittab, :fakefs do
let(:location) { "/tmp/inittab" }
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
let(:location) { "/tmp/inittab" }
let(:engine) { Foreman::Engine.new(procfile) }
let(:options) { Hash.new }
let(:inittab) { Foreman::Export::Inittab.new(location, engine, options) }
before(:each) { load_export_templates_into_fakefs("inittab") }
before(:each) { stub(inittab).say }
it "exports to the filesystem" do
inittab.export
File.read("/tmp/inittab").should == example_export_file("inittab/inittab.default")
end
context "to stdout" do
let(:location) { "-" }
it "exports to stdout" do
mock(inittab).puts example_export_file("inittab/inittab.default")
inittab.export
end
end
context "with concurrency" do
let(:options) { Hash[:concurrency => "alpha=2"] }
it "exports to the filesystem with concurrency" do
inittab.export
File.read("/tmp/inittab").should == example_export_file("inittab/inittab.concurrency")
end
end
end

View File

@@ -22,6 +22,17 @@ describe Foreman::Export::Upstart, :fakefs do
File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("upstart/app-bravo-1.conf")
end
it "cleans up if exporting into an existing dir" do
mock(FileUtils).rm("/tmp/init/app.conf")
mock(FileUtils).rm("/tmp/init/app-alpha.conf")
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")
upstart.export
upstart.export
end
context "with concurrency" do
let(:options) { Hash[:concurrency => "alpha=2"] }

View File

@@ -1,2 +1,24 @@
require "spec_helper"
require "foreman/export"
describe "Foreman::Export" do
subject { Foreman::Export }
describe "with a formatter that doesn't declare the appropriate class" do
it "prints an error" do
mock(subject).require("foreman/export/invalidformatter")
mock_export_error("Unknown export format: invalidformatter (no class Foreman::Export::Invalidformatter).") do
subject.formatter("invalidformatter")
end
end
end
describe "with an invalid formatter" do
it "prints an error" do
mock_export_error("Unknown export format: invalidformatter (unable to load file 'foreman/export/invalidformatter').") do
subject.formatter("invalidformatter")
end
end
end
end

View File

@@ -0,0 +1,26 @@
require "spec_helper"
require "foreman/helpers"
describe "Foreman::Helpers" do
before do
module Foo
class Bar; end
end
end
after do
Object.send(:remove_const, :Foo)
end
subject { o = Object.new; o.extend(Foreman::Helpers); o }
it "should classify words" do
subject.classify("foo").should == "Foo"
subject.classify("foo-bar").should == "FooBar"
end
it "should constantize words" do
subject.constantize("Object").should == Object
subject.constantize("Foo::Bar").should == Foo::Bar
end
end

View File

@@ -36,6 +36,7 @@ describe Foreman::Process do
def run_file(executable, code)
file = File.open("#{basedir}/script", 'w') {|it| it << code }
run "#{executable} #{file.path}"
sleep 1
end
context 'options' do
@@ -98,7 +99,6 @@ describe Foreman::Process do
trap "TERM", "IGNORE"
loop { sleep 1 }
CODE
sleep 1 # wait for ruby to start
subject.should be_alive
subject.kill 'TERM'
subject.should be_alive

View File

@@ -25,4 +25,10 @@ describe Foreman do
ENV['FOO'].should == 'bar'
end
end
describe "runner" do
it "should exist" do
File.exists?(Foreman.runner).should == true
end
end
end

2
spec/resources/bin/utf8 Executable file
View File

@@ -0,0 +1,2 @@
#!/usr/bin/env ruby
puts "\xff\x03"

View File

@@ -0,0 +1,4 @@
# ----- foreman app processes -----
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 -----

View File

@@ -0,0 +1,4 @@
# ----- foreman app processes -----
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 -----

View File

@@ -1,10 +1,20 @@
require "rubygems"
require "simplecov"
SimpleCov.start do
add_filter "/spec/"
end
require "rspec"
require "fakefs/safe"
require "fakefs/spec_helpers"
$:.unshift File.expand_path("../../lib", __FILE__)
def mock_export_error(message)
lambda { yield }.should raise_error(Foreman::Export::Exception, message)
end
def mock_error(subject, message)
mock_exit do
mock(subject).puts("ERROR: #{message}")
@@ -37,9 +47,11 @@ def write_procfile(procfile="Procfile", alpha_env="")
File.expand_path(procfile)
end
def write_env(env=".env")
def write_env(env=".env", options={"FOO"=>"bar"})
File.open(env, "w") do |file|
file.puts "FOO=bar"
options.each do |key, val|
file.puts "#{key}=#{val}"
end
end
end
@@ -56,9 +68,13 @@ def load_export_templates_into_fakefs(type)
end
end
def resource_path(filename)
File.expand_path("../resources/#{filename}", __FILE__)
end
def example_export_file(filename)
FakeFS.deactivate!
data = File.read(File.expand_path("../resources/export/#{filename}", __FILE__))
data = File.read(File.expand_path(resource_path("export/#{filename}"), __FILE__))
FakeFS.activate!
data
end

112
tasks/dist.rake Normal file
View File

@@ -0,0 +1,112 @@
require "erb"
require "fileutils"
require "tmpdir"
def assemble(source, target, perms=0644)
FileUtils.mkdir_p(File.dirname(target))
File.open(target, "w") do |f|
f.puts ERB.new(File.read(source)).result(binding)
end
File.chmod(perms, target)
end
def assemble_distribution(target_dir=Dir.pwd)
distribution_files.each do |source|
target = source.gsub(/^#{project_root}/, target_dir)
FileUtils.mkdir_p(File.dirname(target))
FileUtils.cp(source, target)
end
end
GEM_BLACKLIST = %w( bundler foreman )
def assemble_gems(target_dir=Dir.pwd)
lines = %x{ cd #{project_root} && bundle show }.strip.split("\n")
raise "error running bundler" unless $?.success?
%x{ env BUNDLE_WITHOUT="development:test" bundle show }.split("\n").each do |line|
if line =~ /^ \* (.*?) \((.*?)\)/
next if GEM_BLACKLIST.include?($1)
puts "vendoring: #{$1}-#{$2}"
gem_dir = %x{ bundle show #{$1} }.strip
FileUtils.mkdir_p "#{target_dir}/vendor/gems"
%x{ cp -R "#{gem_dir}" "#{target_dir}/vendor/gems" }
end
end.compact
end
def beta?
Foreman::VERSION.to_s =~ /pre/
end
def clean(file)
rm file if File.exists?(file)
end
def distribution_files(type=nil)
require "foreman/distribution"
base_files = Foreman::Distribution.files
type_files = type ?
Dir[File.expand_path("../../dist/resources/#{type}/**/*", __FILE__)] : []
base_files.concat(type_files)
end
def mkchdir(dir)
FileUtils.mkdir_p(dir)
Dir.chdir(dir) do |dir|
yield(File.expand_path(dir))
end
end
def pkg(filename)
File.expand_path("../../pkg/#{filename}", __FILE__)
end
def project_root
File.expand_path("../..", __FILE__)
end
def resource(name)
File.expand_path("../../dist/resources/#{name}", __FILE__)
end
def s3_connect
return if @s3_connected
require "aws/s3"
unless ENV["DAVID_RELEASE_ACCESS"] && ENV["DAVID_RELEASE_SECRET"]
puts "please set DAVID_RELEASE_ACCESS and DAVID_RELEASE_SECRET in your environment"
exit 1
end
AWS::S3::Base.establish_connection!(
:access_key_id => ENV["DAVID_RELEASE_ACCESS"],
:secret_access_key => ENV["DAVID_RELEASE_SECRET"]
)
@s3_connected = true
end
def store(package_file, filename, bucket="assets.foreman.io")
s3_connect
puts "storing: #{filename}"
AWS::S3::S3Object.store(filename, File.open(package_file), bucket, :access => :public_read)
end
def tempdir
Dir.mktmpdir do |dir|
Dir.chdir(dir) do
yield(dir)
end
end
end
def version
require "foreman/version"
Foreman::VERSION
end
Dir[File.expand_path("../../dist/**/*.rake", __FILE__)].each do |rake|
import rake
end

74
tasks/release.rake Normal file
View File

@@ -0,0 +1,74 @@
require "time"
desc "Build the manual"
task :man do
ENV['RONN_MANUAL'] = "Foreman Manual"
ENV['RONN_ORGANIZATION'] = "Foreman #{Foreman::VERSION}"
sh "ronn -w -s toc -r5 --markdown man/*.ronn"
end
desc "Commit the manual to git"
task "man:commit" => :man do
sh "git add README.md"
sh "git commit -am 'update docs' || echo 'nothing to commit'"
end
desc "Generate the Github docs"
task :pages => "man:commit" do
sh %{
cp man/foreman.1.html /tmp/foreman.1.html
git checkout gh-pages
rm ./index.html
cp /tmp/foreman.1.html ./index.html
git add -u index.html
git commit -m "saving man page to github docs"
git push origin -f gh-pages
git checkout master
}
end
desc "Generate an authors list"
task :authors do
authors = %x{ git log --pretty=format:"%an" | sort -u }.split("\n")
readme = File.read("README.md")
readme.gsub!(/#### Patches contributed by\n([^\n]*)\n/m, "#### Patches contributed by\n#{authors.join(", ")}\n")
File.open("README.md", "w") { |f| f.print readme }
end
def latest_release
latest = File.read("Changelog.md").split("\n").first.split(" ")[1]
end
def newer_release
tags = %x{ git tag --contains v#{latest_release} }.split("\n").sort_by do |tag|
Gem::Version.new(tag[1..-1])
end
tags.reject { |tag| Gem::Version.new(tag[1..-1]).prerelease? }[1]
end
desc "Generate a Changelog"
task :changelog do
while release = newer_release
entry = %x{ git show --format="%cd" #{release} | head -n 1 }
date = Time.parse(entry.chomp).strftime("%Y-%m-%d")
message = "## #{release[1..-1]} (#{date})\n\n"
message += %x{ git log --format="* %s [%an]" v#{latest_release}..#{release} }
changelog = File.read("Changelog.md")
changelog = message + "\n" + changelog
puts release
File.open("Changelog.md", "w") do |file|
file.print changelog
end
end
end
desc "Cut a release"
task :release do
Rake::Task["authors"].invoke
Rake::Task["changelog"].invoke
Rake::Task["pages"].invoke
end

8
tasks/rspec.rake Normal file
View File

@@ -0,0 +1,8 @@
require "rspec/core/rake_task"
task :default => :spec
desc "Run all specs"
RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = 'spec/**/*_spec.rb'
end