Compare commits
211 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c3ef1a7af | ||
|
|
cf69c31ae3 | ||
|
|
e16a35da4b | ||
|
|
2490dd2a5b | ||
|
|
2705520496 | ||
|
|
522fee5e9e | ||
|
|
c462473a25 | ||
|
|
224fe94dc2 | ||
|
|
7cb73f5c36 | ||
|
|
1537ddbcc9 | ||
|
|
b04c81dd06 | ||
|
|
ae22d34967 | ||
|
|
9a359efbf7 | ||
|
|
9632227d29 | ||
|
|
288d118ca4 | ||
|
|
576455b8d7 | ||
|
|
a04032454a | ||
|
|
29c94c785d | ||
|
|
a0a2dd9454 | ||
|
|
1b701cddf3 | ||
|
|
4080b3f1f2 | ||
|
|
84c49ae2b8 | ||
|
|
d54b46806c | ||
|
|
bc1c5e4c74 | ||
|
|
ed4a32518f | ||
|
|
e1d3955d3c | ||
|
|
c7167e1c83 | ||
|
|
db93432118 | ||
|
|
5eada6b7cc | ||
|
|
5e46b1f43a | ||
|
|
ef723f5831 | ||
|
|
52e24ef64b | ||
|
|
0b1daf1927 | ||
|
|
e0d84a56d7 | ||
|
|
7ee2edcc60 | ||
|
|
d428ac5356 | ||
|
|
5351b49fee | ||
|
|
6ebe76d8c1 | ||
|
|
7c43c672c9 | ||
|
|
41d9050ae3 | ||
|
|
09e9cefa3a | ||
|
|
85efe5c1ba | ||
|
|
c21634c04e | ||
|
|
6d4f3476f1 | ||
|
|
724812d6e3 | ||
|
|
ce6ec4848d | ||
|
|
a37a097f73 | ||
|
|
f28725bdac | ||
|
|
51eee011ce | ||
|
|
6c8c848f54 | ||
|
|
f60c4cb767 | ||
|
|
4ad49cb058 | ||
|
|
e993af2b20 | ||
|
|
d7000bccfa | ||
|
|
2995a605b4 | ||
|
|
ef280e802d | ||
|
|
8a9001842c | ||
|
|
8a8d31eb43 | ||
|
|
3e98170878 | ||
|
|
3d84de3062 | ||
|
|
9cc0afca49 | ||
|
|
7a25d3ac5a | ||
|
|
db1a5df354 | ||
|
|
e137596ce0 | ||
|
|
c9042c5aae | ||
|
|
550adc8070 | ||
|
|
fbdde3e62a | ||
|
|
e161ecb630 | ||
|
|
853a88dfbf | ||
|
|
b4cab08327 | ||
|
|
ade0005a92 | ||
|
|
241b91a0d5 | ||
|
|
047f106d48 | ||
|
|
df043e60d8 | ||
|
|
158c184f8c | ||
|
|
2ed1fe8d44 | ||
|
|
a008886bd0 | ||
|
|
c62f892ff6 | ||
|
|
d885e019b3 | ||
|
|
cfd337b44d | ||
|
|
e76f3533dc | ||
|
|
1485eeb859 | ||
|
|
e0b5928e88 | ||
|
|
a73dce5405 | ||
|
|
2abddb42b3 | ||
|
|
d961a32cfe | ||
|
|
2bfc065c1d | ||
|
|
fbe3d4ec69 | ||
|
|
631187e0d8 | ||
|
|
92d1a4d367 | ||
|
|
f4123f4ae1 | ||
|
|
d4c2332c59 | ||
|
|
e257fc89c1 | ||
|
|
a278755ae4 | ||
|
|
3367a060a7 | ||
|
|
ac7e0743ac | ||
|
|
e574880814 | ||
|
|
7132cacbf6 | ||
|
|
c1f279aa6f | ||
|
|
34cfe9ef9d | ||
|
|
79fc3b8029 | ||
|
|
91140638e1 | ||
|
|
48cc60c30f | ||
|
|
533139ea9f | ||
|
|
86e2056a24 | ||
|
|
ab29963ee4 | ||
|
|
cf269c39da | ||
|
|
76cd2e794b | ||
|
|
83748cb538 | ||
|
|
d2c9ce0f34 | ||
|
|
98337c92e1 | ||
|
|
33d738b3f8 | ||
|
|
9432989fbe | ||
|
|
66b1483a75 | ||
|
|
64bd4db128 | ||
|
|
b561555f3a | ||
|
|
baa7b7685c | ||
|
|
cfa6e6f259 | ||
|
|
a34bc59721 | ||
|
|
07e8ca4a4b | ||
|
|
342d30bbb8 | ||
|
|
268dd6240e | ||
|
|
9e60b3e1a4 | ||
|
|
1c6285f8af | ||
|
|
5de1bd18ac | ||
|
|
fff15bc627 | ||
|
|
a66157d611 | ||
|
|
fcfa913fb0 | ||
|
|
fc438472f9 | ||
|
|
fc95936327 | ||
|
|
0c27f78d46 | ||
|
|
356c61f471 | ||
|
|
dcff4da220 | ||
|
|
888520ee99 | ||
|
|
c7b6b334fd | ||
|
|
f476920a05 | ||
|
|
5436b68cf1 | ||
|
|
c9411cd2b1 | ||
|
|
6e95d1ce94 | ||
|
|
c5548a345e | ||
|
|
f668b87660 | ||
|
|
914a1ee958 | ||
|
|
e1c2946718 | ||
|
|
6160246da0 | ||
|
|
2e8030dbd4 | ||
|
|
58e4cdafd7 | ||
|
|
44a5dff724 | ||
|
|
e33921f083 | ||
|
|
79cf541ebe | ||
|
|
8bc8cb4b2e | ||
|
|
39ace26ae1 | ||
|
|
c383359136 | ||
|
|
a5e094353c | ||
|
|
12720b4c12 | ||
|
|
1c732a4658 | ||
|
|
8908a66e90 | ||
|
|
f63c0b0ca0 | ||
|
|
676d3ff8d1 | ||
|
|
615aada17e | ||
|
|
2e1d1c7c15 | ||
|
|
bf832ffd9f | ||
|
|
b9bfede48a | ||
|
|
bed8323029 | ||
|
|
f6ef5a5b4f | ||
|
|
f3c1e76860 | ||
|
|
33aa1efc90 | ||
|
|
caa688cdc6 | ||
|
|
c6a410b664 | ||
|
|
02c8d2cb10 | ||
|
|
ada41ce311 | ||
|
|
8f1c752a77 | ||
|
|
ddf25fe0ea | ||
|
|
9dac91a624 | ||
|
|
cdaeb646ac | ||
|
|
86e4251840 | ||
|
|
ba18f7e589 | ||
|
|
be73e8500f | ||
|
|
d26ca669a1 | ||
|
|
a3e758ab6c | ||
|
|
5dc232a7b1 | ||
|
|
4191cb7b9c | ||
|
|
90d4dffb82 | ||
|
|
823f307abc | ||
|
|
ed44a11e21 | ||
|
|
5719f4fc72 | ||
|
|
47008fb921 | ||
|
|
91edac3197 | ||
|
|
20e598abcc | ||
|
|
ec7f4a480d | ||
|
|
51376058d4 | ||
|
|
f7542083dd | ||
|
|
23124afc7e | ||
|
|
c948858f45 | ||
|
|
f358d897fa | ||
|
|
ec1b06abb5 | ||
|
|
75d4fc562d | ||
|
|
1cbb295b0d | ||
|
|
60fb125b51 | ||
|
|
69d596bb8b | ||
|
|
cbd5a6344c | ||
|
|
4230455859 | ||
|
|
b25dfee62d | ||
|
|
b73da71c9c | ||
|
|
43558758fc | ||
|
|
6da8aca609 | ||
|
|
9df93a64cc | ||
|
|
8fd9b753f4 | ||
|
|
7fc6d02e7b | ||
|
|
ddcaac8749 | ||
|
|
9db97abb10 | ||
|
|
21a041527c |
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,8 +1,8 @@
|
||||
.bundle
|
||||
coverage
|
||||
example/log/*
|
||||
man/*.html
|
||||
man/*.markdown
|
||||
pkg
|
||||
tags
|
||||
|
||||
/.bundle
|
||||
/coverage
|
||||
/example/log/*
|
||||
/man/*.html
|
||||
/man/*.markdown
|
||||
/pkg
|
||||
/tags
|
||||
/vendor
|
||||
|
||||
14
.travis.yml
Normal file
14
.travis.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
script: bundle exec rake spec
|
||||
|
||||
rvm:
|
||||
- 1.8.7
|
||||
- 1.9.2
|
||||
- 1.9.3
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
webhooks:
|
||||
on_success: always
|
||||
on_failure: always
|
||||
urls:
|
||||
- http://dx-helper.herokuapp.com/travis
|
||||
190
Changelog.md
Normal file
190
Changelog.md
Normal file
@@ -0,0 +1,190 @@
|
||||
## 0.37.0.pre5 2012-01-24 9632227
|
||||
0.37.0.pre5 [David Dollar]
|
||||
Merge pull request #142 from apollo13/master [David Dollar]
|
||||
rename readme [David Dollar]
|
||||
Fix the test for an empty string in bin/runner [Florian Apolloner]
|
||||
|
||||
## 0.37.0.pre4 2012-01-24 a040324
|
||||
0.37.0.pre4 [David Dollar]
|
||||
Merge pull request #130 from clowder/foreman [David Dollar]
|
||||
Don't expose the options hash. [Chris Lowder]
|
||||
Simplify subclassing by adding all arguments to the initializer. Also, clean up the method signatures for existing exporters. [Chris Lowder]
|
||||
Add ZenTest as a development dependency, there is an autotest folder but the gem is missing. [Chris Lowder]
|
||||
Attempt to require the custom export class. [Chris Lowder]
|
||||
Catching more than we need to. [Chris Lowder]
|
||||
Extract commonality into the base class, make life easy for our plugin writers. [Chris Lowder]
|
||||
Removing the hard coding of export formats allowing the user to 'plug-in' their own export format. [Chris Lowder]
|
||||
Update README.markdown [David Dollar]
|
||||
Revert "try on more rubies" [David Dollar]
|
||||
try on more rubies [David Dollar]
|
||||
fix webhook url [David Dollar]
|
||||
Update README.markdown [David Dollar]
|
||||
Update README.markdown [David Dollar]
|
||||
ensure we have non-nil data, fixes #111 [David Dollar]
|
||||
make sure error method exists. fixes #104 [David Dollar]
|
||||
Merge pull request #136 from fnichol/foreman [David Dollar]
|
||||
remove other from install instructions [David Dollar]
|
||||
cleanup [David Dollar]
|
||||
Revert "tweak authors" [David Dollar]
|
||||
tweak authors [David Dollar]
|
||||
fix authors [David Dollar]
|
||||
readme tweaks [David Dollar]
|
||||
readme tweaks [David Dollar]
|
||||
fix up authors [David Dollar]
|
||||
Update README.markdown [David Dollar]
|
||||
fix up specs [David Dollar]
|
||||
we're not chdiring any more [David Dollar]
|
||||
use strings rather than symbols to better emulate the real thing [David Dollar]
|
||||
Merge pull request #139 from brainopia/foreman [David Dollar]
|
||||
Add specs for Foreman::Process#run [brainopia]
|
||||
Implement Foreman::Process#kill,detach,alive?,dead? [brainopia]
|
||||
- Use explicit fakefs tag in specs - Clean up trailing whitespace [brainopia]
|
||||
Simplify Foreman::Process#with_environment [brainopia]
|
||||
Add specs for options of Foreman::Process#run [brainopia]
|
||||
Add specs for initialization of Foreman::Process [brainopia]
|
||||
runit creates a full path to export directory. [Fletcher Nichol]
|
||||
|
||||
## 0.37.0.pre3 2012-01-22 51eee01
|
||||
0.37.0.pre3 [David Dollar]
|
||||
normalize platform names [David Dollar]
|
||||
windows support [David Dollar]
|
||||
add windows support [David Dollar]
|
||||
dont need pty [David Dollar]
|
||||
fix specs [David Dollar]
|
||||
|
||||
## 0.37.0.pre2 2012-01-22 2995a60
|
||||
0.37.0.pre2 [David Dollar]
|
||||
0.37.0.pre1 [David Dollar]
|
||||
remove unnecessary stdout/stderr flattening [David Dollar]
|
||||
use PLATFORM=jruby instead of JRUBY=true [David Dollar]
|
||||
|
||||
## 0.37.0.pre1 2012-01-22 3e98170
|
||||
fix java build bug [David Dollar]
|
||||
dont do rubygems/bundler in the Rakefile [David Dollar]
|
||||
switch to posix-spawn for jruby [David Dollar]
|
||||
add jruby build [David Dollar]
|
||||
spork is in the gemspec now [David Dollar]
|
||||
remove debugging [David Dollar]
|
||||
move the spoon require into the jruby branch [David Dollar]
|
||||
pass basedir along to the runner script [David Dollar]
|
||||
beef up the runner script to allow a working directory to be set [David Dollar]
|
||||
Merge pull request #140 from jc00ke/foreman [David Dollar]
|
||||
Move spoon dep to Gemfile [jc00ke]
|
||||
Using spoon for JRuby support [jc00ke]
|
||||
use default bucket for storage [David Dollar]
|
||||
Merge pull request #138 from technomancy/debian [David Dollar]
|
||||
Add Debian packaging. [Phil Hagelberg]
|
||||
Ignore vendor dir. [Phil Hagelberg]
|
||||
|
||||
## 0.36.1 2012-01-18 1485eeb
|
||||
0.36.1 [David Dollar]
|
||||
bump term-ansicolor in gemspec [David Dollar]
|
||||
|
||||
## 0.36.0 2012-01-17 a73dce5
|
||||
0.36.0 [David Dollar]
|
||||
sync the writer stream [David Dollar]
|
||||
capture stderr as well [David Dollar]
|
||||
|
||||
## 0.35.0 2012-01-16 2bfc065
|
||||
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 d4c2332
|
||||
0.34.1 [David Dollar]
|
||||
fix missing start desc [David Dollar]
|
||||
|
||||
## 0.34.0 2012-01-16 a278755
|
||||
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 533139e
|
||||
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 cf269c3
|
||||
0.33.0 [David Dollar]
|
||||
Revert "Merge pull request #125 from brainopia/master" [David Dollar]
|
||||
|
||||
## 0.32.0 2012-01-12 83748cb
|
||||
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 342d30b
|
||||
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 fcfa913
|
||||
0.30.1 [David Dollar]
|
||||
require thread for mutex [David Dollar]
|
||||
|
||||
## 0.30.0 2011-12-22 fc95936
|
||||
0.30.0 [David Dollar]
|
||||
compatibility with ruby 1.8 [David Dollar]
|
||||
|
||||
## 0.29.0 2011-12-22 356c61f
|
||||
0.29.0 [David Dollar]
|
||||
|
||||
## 0.28.0.pre2 2011-12-08 dcff4da
|
||||
0.28.0.pre2 [David Dollar]
|
||||
fix pipe error [David Dollar]
|
||||
|
||||
## 0.28.0.pre1 2011-12-08 c7b6b33
|
||||
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 914a1ee
|
||||
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 a5e0943
|
||||
|
||||
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]
|
||||
21
Gemfile
21
Gemfile
@@ -1,3 +1,24 @@
|
||||
source "http://rubygems.org"
|
||||
|
||||
gemspec
|
||||
|
||||
platform :mingw do
|
||||
gem "win32console", "~> 1.3.0"
|
||||
end
|
||||
|
||||
platform :jruby do
|
||||
gem "posix-spawn", "~> 0.3.6"
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem 'parka'
|
||||
gem 'rake'
|
||||
gem 'ronn'
|
||||
gem 'fakefs', '~> 0.3.2'
|
||||
gem 'rr', '~> 1.0.2'
|
||||
gem 'rspec', '~> 2.0'
|
||||
gem 'ZenTest'
|
||||
gem 'aws-s3'
|
||||
gem "rubyzip"
|
||||
gem "simplecov", :require => false
|
||||
end
|
||||
|
||||
56
Gemfile.lock
56
Gemfile.lock
@@ -1,25 +1,33 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
foreman (0.20.0)
|
||||
term-ansicolor (~> 1.0.5)
|
||||
foreman (0.37.0)
|
||||
term-ansicolor (~> 1.0.7)
|
||||
thor (>= 0.13.6)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
ZenTest (4.6.2)
|
||||
aws-s3 (0.6.2)
|
||||
builder
|
||||
mime-types
|
||||
xml-simple
|
||||
builder (3.0.0)
|
||||
crack (0.1.8)
|
||||
diff-lcs (1.1.2)
|
||||
fakefs (0.2.1)
|
||||
diff-lcs (1.1.3)
|
||||
fakefs (0.3.2)
|
||||
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
|
||||
rest-client
|
||||
thor
|
||||
rake (0.8.7)
|
||||
rcov (0.9.8)
|
||||
posix-spawn (0.3.6)
|
||||
rake (0.9.2.2)
|
||||
rdiscount (1.6.5)
|
||||
rest-client (1.6.1)
|
||||
mime-types (>= 1.16)
|
||||
@@ -28,26 +36,40 @@ GEM
|
||||
mustache (>= 0.7.0)
|
||||
rdiscount (>= 1.5.8)
|
||||
rr (1.0.2)
|
||||
rspec (2.6.0)
|
||||
rspec-core (~> 2.6.0)
|
||||
rspec-expectations (~> 2.6.0)
|
||||
rspec-mocks (~> 2.6.0)
|
||||
rspec-core (2.6.4)
|
||||
rspec-expectations (2.6.0)
|
||||
rspec (2.8.0)
|
||||
rspec-core (~> 2.8.0)
|
||||
rspec-expectations (~> 2.8.0)
|
||||
rspec-mocks (~> 2.8.0)
|
||||
rspec-core (2.8.0)
|
||||
rspec-expectations (2.8.0)
|
||||
diff-lcs (~> 1.1.2)
|
||||
rspec-mocks (2.6.0)
|
||||
term-ansicolor (1.0.6)
|
||||
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)
|
||||
xml-simple (1.0.15)
|
||||
|
||||
PLATFORMS
|
||||
java
|
||||
ruby
|
||||
x86-mingw32
|
||||
|
||||
DEPENDENCIES
|
||||
fakefs (~> 0.2.1)
|
||||
ZenTest
|
||||
aws-s3
|
||||
fakefs (~> 0.3.2)
|
||||
foreman!
|
||||
parka
|
||||
posix-spawn (~> 0.3.6)
|
||||
rake
|
||||
rcov (~> 0.9.8)
|
||||
ronn
|
||||
rr (~> 1.0.2)
|
||||
rspec (~> 2.6.0)
|
||||
rspec (~> 2.0)
|
||||
rubyzip
|
||||
simplecov
|
||||
win32console (~> 1.3.0)
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
# Foreman
|
||||
|
||||
## Installation
|
||||
|
||||
gem install foreman
|
||||
|
||||
## Description
|
||||
|
||||
http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
|
||||
|
||||
## Manual
|
||||
|
||||
See the [man page](http://ddollar.github.com/foreman) for usage.
|
||||
|
||||
## Authorship
|
||||
|
||||
Created by David Dollar
|
||||
|
||||
Patches contributed by:
|
||||
|
||||
* Adam Wiggins
|
||||
* clifff
|
||||
* Dan Peterson
|
||||
* Jay Zeschin
|
||||
* Keith Rarick
|
||||
* Ricardo Chimal, Jr
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
39
README.md
Normal file
39
README.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Foreman
|
||||
|
||||
Manage Procfile-based applications
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th>If you have...</th>
|
||||
<th>Install with...</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ruby (MRI, JRuby, Windows)</td>
|
||||
<td><pre>$ gem install foreman</pre></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mac OS X</td>
|
||||
<td><a href="http://assets.foreman.io/foreman/foreman.pkg">foreman.pkg</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Getting Started
|
||||
|
||||
* http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
|
||||
|
||||
## Documentation
|
||||
|
||||
* [man page](http://ddollar.github.com/foreman)
|
||||
* [wiki](http://github.com/ddollar/foreman/wiki)
|
||||
|
||||
## Authors
|
||||
|
||||
#### Created and maintained by
|
||||
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
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
54
Rakefile
54
Rakefile
@@ -1,56 +1,6 @@
|
||||
require "rubygems"
|
||||
require "bundler"
|
||||
Bundler.setup
|
||||
|
||||
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
|
||||
}
|
||||
Dir[File.expand_path("../tasks/*.rake", __FILE__)].each do |task|
|
||||
load task
|
||||
end
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
#!/usr/bin/env ruby
|
||||
exec ARGV.join(" ")
|
||||
36
bin/runner
Executable file
36
bin/runner
Executable file
@@ -0,0 +1,36 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
#/ Usage: runner [-d <dir>] <command>
|
||||
#/
|
||||
#/ Run a command with exec, optionally changing directory first
|
||||
|
||||
set -e
|
||||
|
||||
error() {
|
||||
echo $@ >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat $0 | grep '^#/' | cut -c4-
|
||||
exit
|
||||
}
|
||||
|
||||
while getopts ":hd:" OPT; do
|
||||
case $OPT in
|
||||
d) cd $OPTARG ;;
|
||||
h) usage ;;
|
||||
\?) error "invalid option: -$OPTARG" ;;
|
||||
:) error "option -$OPTARG requires an argument" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND-1))
|
||||
|
||||
command=$1
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
exec $1
|
||||
@@ -1,2 +1,2 @@
|
||||
ticker: ./ticker $PORT
|
||||
error : ./error
|
||||
ticker: ruby ./ticker $PORT
|
||||
error: ruby ./error
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
$stdout.sync = true
|
||||
|
||||
puts "will error in 10s"
|
||||
sleep 5
|
||||
raise "Dying"
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
%w( SIGINT SIGTERM SIGHUP ).each do |signal|
|
||||
$stdout.sync = true
|
||||
|
||||
%w( SIGINT SIGTERM ).each do |signal|
|
||||
trap(signal) do
|
||||
puts "received #{signal} but i'm ignoring it!"
|
||||
end
|
||||
|
||||
27
data/export/bluepill/master.pill.erb
Normal file
27
data/export/bluepill/master.pill.erb
Normal file
@@ -0,0 +1,27 @@
|
||||
Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/bluepill.log") do |app|
|
||||
|
||||
app.uid = "<%= user %>"
|
||||
app.gid = "<%= user %>"
|
||||
|
||||
<% engine.procfile.entries.each do |process| %>
|
||||
<% 1.upto(concurrency[process.name]) do |num| %>
|
||||
<% port = engine.port_for(process, num, self.port) %>
|
||||
app.process("<%= process.name %>-<%=num%>") do |process|
|
||||
process.start_command = "<%= process.command.gsub("$PORT", port.to_s) %>"
|
||||
|
||||
process.working_dir = "<%= engine.directory %>"
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "<%= port %>"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
|
||||
process.stdout = process.stderr = "<%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
children.stop_command "kill -QUIT {{PID}}"
|
||||
end
|
||||
|
||||
process.group = "<%= app %>-<%= process.name %>"
|
||||
end
|
||||
<% end %>
|
||||
<% end %>
|
||||
end
|
||||
7
data/export/runit/log_run.erb
Normal file
7
data/export/runit/log_run.erb
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
LOG=<%= log_root %>/<%= process.name %>-<%= num %>
|
||||
|
||||
test -d "$LOG" || mkdir -p m2750 "$LOG" && chown <%= user %> "$LOG"
|
||||
exec chpst -u <%= user %> svlogd "$LOG"
|
||||
3
data/export/runit/run.erb
Normal file
3
data/export/runit/run.erb
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
cd <%= engine.directory %>
|
||||
exec chpst -u <%= user %> -e <%= process_env_directory %> <%= process.command %>
|
||||
@@ -2,4 +2,4 @@ start on starting <%= app %>-<%= process.name %>
|
||||
stop on stopping <%= app %>-<%= process.name %>
|
||||
respawn
|
||||
|
||||
exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>; <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
|
||||
exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>;<% engine.environment.each_pair do |var,env| %> export <%= var.upcase %>=<%= env %>; <% end %> <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
|
||||
|
||||
33
dist/deb.rake
vendored
Normal file
33
dist/deb.rake
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
file pkg("/apt-#{version}/foreman-#{version}.deb") => distribution_files("deb") do |t|
|
||||
mkchdir(File.dirname(t.name)) do
|
||||
mkchdir("usr/local/foreman") do
|
||||
assemble_distribution
|
||||
assemble_gems
|
||||
assemble resource("deb/foreman"), "bin/foreman", 0755
|
||||
File.chmod 0755, "bin/runner"
|
||||
end
|
||||
|
||||
assemble resource("deb/control"), "control"
|
||||
assemble resource("deb/postinst"), "postinst"
|
||||
|
||||
sh "tar czvf data.tar.gz usr/local/foreman --owner=root --group=root"
|
||||
sh "tar czvf control.tar.gz control postinst"
|
||||
|
||||
File.open("debian-binary", "w") do |f|
|
||||
f.puts "2.0"
|
||||
end
|
||||
|
||||
deb = File.basename(t.name)
|
||||
|
||||
sh "ar -r #{t.name} debian-binary control.tar.gz data.tar.gz"
|
||||
end
|
||||
end
|
||||
|
||||
desc "Build a .deb package"
|
||||
task "deb:build" => pkg("/apt-#{version}/foreman-#{version}.deb")
|
||||
|
||||
desc "Remove build artifacts for .deb"
|
||||
task "deb:clean" do
|
||||
clean pkg("foreman-#{version}.deb")
|
||||
FileUtils.rm_rf("pkg/apt-#{version}") if Dir.exists?("pkg/apt-#{version}")
|
||||
end
|
||||
14
dist/gem.rake
vendored
Normal file
14
dist/gem.rake
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
file pkg("foreman-#{version}.gem") => distribution_files do |t|
|
||||
sh "gem build foreman.gemspec"
|
||||
sh "mv foreman-#{version}.gem #{t.name}"
|
||||
end
|
||||
|
||||
task "gem:build" => pkg("foreman-#{version}.gem")
|
||||
|
||||
task "gem:clean" do
|
||||
clean pkg("foreman-#{version}.gem")
|
||||
end
|
||||
|
||||
task "gem:release" => "gem:build" do |t|
|
||||
sh "parka push -f #{pkg("foreman-#{version}.gem")}"
|
||||
end
|
||||
14
dist/jruby.rake
vendored
Normal file
14
dist/jruby.rake
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
file pkg("foreman-#{version}-jruby.gem") => distribution_files do |t|
|
||||
sh "env PLATFORM=java gem build foreman.gemspec"
|
||||
sh "mv foreman-#{version}-java.gem #{t.name}"
|
||||
end
|
||||
|
||||
task "jruby:build" => pkg("foreman-#{version}-jruby.gem")
|
||||
|
||||
task "jruby:clean" do
|
||||
clean pkg("foreman-#{version}-jruby.gem")
|
||||
end
|
||||
|
||||
task "jruby:release" => "jruby:build" do |t|
|
||||
sh "parka push -f #{pkg("foreman-#{version}-jruby.gem")}"
|
||||
end
|
||||
14
dist/mingw32.rake
vendored
Normal file
14
dist/mingw32.rake
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
file pkg("foreman-#{version}-mingw32.gem") => distribution_files do |t|
|
||||
sh "env PLATFORM=mingw32 gem build foreman.gemspec"
|
||||
sh "mv foreman-#{version}-mingw32.gem #{t.name}"
|
||||
end
|
||||
|
||||
task "mingw32:build" => pkg("foreman-#{version}-mingw32.gem")
|
||||
|
||||
task "mingw32:clean" do
|
||||
clean pkg("foreman-#{version}-mingw32.gem")
|
||||
end
|
||||
|
||||
task "mingw32:release" => "mingw32:build" do |t|
|
||||
sh "parka push -f #{pkg("foreman-#{version}-mingw32.gem")}"
|
||||
end
|
||||
52
dist/pkg.rake
vendored
Normal file
52
dist/pkg.rake
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
require "erb"
|
||||
|
||||
file pkg("foreman-#{version}.pkg") => distribution_files do |t|
|
||||
tempdir do |dir|
|
||||
mkchdir("foreman") do
|
||||
assemble_distribution
|
||||
assemble_gems
|
||||
assemble resource("pkg/foreman"), "bin/foreman", 0755
|
||||
end
|
||||
|
||||
kbytes = %x{ du -ks foreman | cut -f 1 }
|
||||
num_files = %x{ find foreman | wc -l }
|
||||
|
||||
mkdir_p "pkg"
|
||||
mkdir_p "pkg/Resources"
|
||||
mkdir_p "pkg/foreman-#{version}.pkg"
|
||||
|
||||
dist = File.read(resource("pkg/Distribution.erb"))
|
||||
dist = ERB.new(dist).result(binding)
|
||||
File.open("pkg/Distribution", "w") { |f| f.puts dist }
|
||||
|
||||
dist = File.read(resource("pkg/PackageInfo.erb"))
|
||||
dist = ERB.new(dist).result(binding)
|
||||
File.open("pkg/foreman-#{version}.pkg/PackageInfo", "w") { |f| f.puts dist }
|
||||
|
||||
mkdir_p "pkg/foreman-#{version}.pkg/Scripts"
|
||||
cp resource("pkg/postinstall"), "pkg/foreman-#{version}.pkg/Scripts/postinstall"
|
||||
chmod 0755, "pkg/foreman-#{version}.pkg/Scripts/postinstall"
|
||||
|
||||
sh %{ mkbom -s foreman pkg/foreman-#{version}.pkg/Bom }
|
||||
|
||||
Dir.chdir("foreman") do
|
||||
sh %{ pax -wz -x cpio . > ../pkg/foreman-#{version}.pkg/Payload }
|
||||
end
|
||||
|
||||
sh %{ pkgutil --flatten pkg foreman-#{version}.pkg }
|
||||
|
||||
cp_r "foreman-#{version}.pkg", t.name
|
||||
end
|
||||
end
|
||||
|
||||
task "pkg:build" => pkg("foreman-#{version}.pkg")
|
||||
|
||||
task "pkg:clean" do
|
||||
clean pkg("foreman-#{version}.pkg")
|
||||
end
|
||||
|
||||
task "pkg:release" => "pkg:build" do |t|
|
||||
store pkg("foreman-#{version}.pkg"), "foreman/foreman-#{version}.pkg"
|
||||
store pkg("foreman-#{version}.pkg"), "foreman/foreman-beta.pkg" if beta?
|
||||
store pkg("foreman-#{version}.pkg"), "foreman/foreman.pkg" unless beta?
|
||||
end
|
||||
12
dist/resources/deb/control
vendored
Normal file
12
dist/resources/deb/control
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
Package: foreman
|
||||
Version: <%= version %>
|
||||
Section: main
|
||||
Priority: standard
|
||||
Architecture: all
|
||||
Depends: ruby1.9.1
|
||||
Maintainer: Heroku
|
||||
Description: Manage Procfile-based applications.
|
||||
Foreman is a manager for Procfile-based applications. Its aim is to
|
||||
abstract away the details of the Procfile format, and allow you to
|
||||
either run your application directly or export it to some other
|
||||
process management format.
|
||||
18
dist/resources/deb/foreman
vendored
Normal file
18
dist/resources/deb/foreman
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env ruby1.9.1
|
||||
|
||||
# resolve bin path, ignoring symlinks
|
||||
require "pathname"
|
||||
bin_file = Pathname.new(__FILE__).realpath
|
||||
|
||||
# add locally vendored gems to libpath
|
||||
gem_dir = File.expand_path("../../vendor/gems", bin_file)
|
||||
Dir["#{gem_dir}/**/lib"].each do |libdir|
|
||||
$:.unshift libdir
|
||||
end
|
||||
|
||||
# add self to libpath
|
||||
$:.unshift File.expand_path("../../lib", bin_file)
|
||||
|
||||
require "foreman/cli"
|
||||
|
||||
Foreman::CLI.start
|
||||
30
dist/resources/deb/heroku-release-key.txt
vendored
Normal file
30
dist/resources/deb/heroku-release-key.txt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1.4.11 (Darwin)
|
||||
|
||||
mQENBE5SfAEBCADLp056ZgfdtAMXLWpEuL9zY+dIHIY5qLQcDmUivjHLVE4l3Bi3
|
||||
Mn570K0W9rfk7fHBPEO2XJEDdjk8Bg6mWTAeGjdfZgZaL+qO9NjqQ5QmVR+vgp7s
|
||||
yxJYlfY+JYTZvl/JiDWGhuPHSPggXILCMf3SpqWMHGPqe/3RAK+CHCNv/94uaoS4
|
||||
vi4HQT+k4sRceiM8WqkSRYSoc7rzdDejZn+InCYFfR56VeSFF4G4I6neZs/q5T9d
|
||||
Ty2i5d0gZLaX/Iqc+3Dy0vDKClc0HUQJ6ajDPuUqKLHFUpqyuwfJij60+C3GMi8K
|
||||
ckRPti31EPFVzq3GPHU+GqA+e9j84WHr4uJ5ABEBAAG0L0hlcm9rdSBSZWxlYXNl
|
||||
IEVuZ2luZWVyaW5nIDxyZWxlYXNlQGhlcm9rdS5jb20+iQE4BBMBAgAiBQJOUnwB
|
||||
AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDJJ+vgDxsFIChECAC9h4Ay
|
||||
Nx4AQFu85cjR9rijyBflPeVqi7Xhzd7IvLg2+kZSexlb2oidj7iVSMy+vy5tG9g9
|
||||
8Az/JqMCVjcZ7ltn60OGU8gIYpJqt6VmH3vfJBxXu/Sm9tym3UCYGVvMAN5Oq6yB
|
||||
HlQkQ8F3p0cW69PmF+fibkgo9RE0EYlBIt2rUHNilTGFS6vXGr5reFFp3/rRHq3k
|
||||
bixnUwFSqNujJgnBKDPwtSYKc4pMpnhuv88xEpLH7vU8NLXQZMitKQguV8XEmcsu
|
||||
43LXlsx5uVr239/XNW+h412gIHFDSzB/YuLWlVUXMfquC96z/wxMqWWZyskDNgr0
|
||||
WDdMgzK6CUfXSqQhuQENBE5SfAEBCADbnGKcXpdVauQpINQLtRnrT0BJIrIo1Yxv
|
||||
LQRb3G7RU+Eq6aHXwk9fSKa6nEv9RsmqiW874yODnr0d/DTUWMHT+jRvPHm1wlbE
|
||||
pGR1aPSo7GgkSUdaT6CVBN3JWZ2kVJGqohNoJMYbfVaWd/kpa/LiMFWzS8LfWT2K
|
||||
xiO2vIh4qBfeRCGR7s8rADCHuHJ0eibADrgqcRfdPrChB1JiYLeTdV4yRmSzJ7TM
|
||||
zWX7OVpGfIFLbCw9NeN65pI9ePs2mSPM7DYkhhKSXWMwJNXFzn1blOGiwAwKb48P
|
||||
a/QpE6TG3PQzbYyTTP0Td1XgKAHcprvbc89a/nAk3a+PJQ/MqvDzABEBAAGJAR8E
|
||||
GAECAAkFAk5SfAECGwwACgkQySfr4A8bBSD4mAgAnCT5WRiDl0259Px9Z9J9Wk8Z
|
||||
SxugDct2Yhzca4aw1Ou4cfaIFCDXzFlBzSJfqk0HoVhp9r2gzEPUCKnSjRDyxaMo
|
||||
wZCUtqigBua+z4NB4AWgeOl/2S06I2ki1K7pfl4piYcHtEThHamnhVPJ2Hi6HsHq
|
||||
mUU+8SxleHE4GCXmKkuvxelUq9jrhHikIkm1RoqFOPb9zV3WRy4YzVHQSYfHmfk0
|
||||
9kXlM/CS0sfNv2UKCX+5e6eFIZv0rdtpp6VEh0tsFmsIClY6Z9MX7bgp8MnUJpyk
|
||||
OeIzOzQgkb4aeT0Whl+EPcTeDZfqIhVBoNXupUanmWNppFcMngxfqG2NGi1vvQ==
|
||||
=aUAq
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
3
dist/resources/deb/postinst
vendored
Executable file
3
dist/resources/deb/postinst
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
ln -sf /usr/local/foreman/bin/foreman /usr/bin/foreman
|
||||
23
dist/resources/pkg/Distribution.erb
vendored
Normal file
23
dist/resources/pkg/Distribution.erb
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<installer-script minSpecVersion="1.000000" authoringTool="org.ruby-lang.rake" authoringToolVersion="0.8.7">
|
||||
<title>Foreman</title>
|
||||
<options customize="never" allow-external-scripts="no"/>
|
||||
<domains enable_localSystem="true"/>
|
||||
<script><![CDATA[
|
||||
function needs_git() {
|
||||
return (system.compareVersion(system.version.ProductVersion, "10.7.0") < 0);
|
||||
}
|
||||
]]></script>
|
||||
<choices-outline>
|
||||
<line choice="git"/>
|
||||
<line choice="foreman-<%= version %>"/>
|
||||
</choices-outline>
|
||||
<choice id="git" title="git" start_selected="false" start_enabled="false" selected="needs_git()" enabled="needs_git()">
|
||||
<pkg-ref id="git.pkg" />
|
||||
</choice>
|
||||
<choice id="foreman-<%= version %>" title="foreman">
|
||||
<pkg-ref id="io.foreman.installer"/>
|
||||
</choice>
|
||||
<pkg-ref id="io.foreman.installer" installKBytes="<%= kbytes %>" version="<%= version %>" auth="Root">#foreman-<%= version %>.pkg</pkg-ref>
|
||||
</installer-script>
|
||||
|
||||
7
dist/resources/pkg/PackageInfo.erb
vendored
Normal file
7
dist/resources/pkg/PackageInfo.erb
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<pkg-info format-version="2" identifier="io.foreman.installer" version="<%= version %>" install-location="/usr/local/foreman" auth="root">
|
||||
<payload installKBytes="<%= kbytes %>" numberOfFiles="<%= num_files %>"/>
|
||||
<scripts>
|
||||
<postinstall file="./postinstall"/>
|
||||
</scripts>
|
||||
</pkg-info>
|
||||
|
||||
15
dist/resources/pkg/foreman
vendored
Normal file
15
dist/resources/pkg/foreman
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
require "pathname"
|
||||
bin_file = Pathname.new(__FILE__).realpath
|
||||
|
||||
gem_dir = File.expand_path("../../vendor/gems", bin_file)
|
||||
Dir["#{gem_dir}/**/lib"].each do |libdir|
|
||||
$:.unshift libdir
|
||||
end
|
||||
|
||||
$:.unshift File.expand_path("../../lib", bin_file)
|
||||
|
||||
require "foreman/cli"
|
||||
|
||||
Foreman::CLI.start
|
||||
2
dist/resources/pkg/postinstall
vendored
Normal file
2
dist/resources/pkg/postinstall
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
ln -sf /usr/local/foreman/bin/foreman /usr/bin/foreman
|
||||
15
dist/resources/tgz/foreman
vendored
Normal file
15
dist/resources/tgz/foreman
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "pathname"
|
||||
bin_file = Pathname.new(__FILE__).realpath
|
||||
|
||||
gem_dir = File.expand_path("../vendor/gems", bin_file)
|
||||
Dir["#{gem_dir}/**/lib"].each do |libdir|
|
||||
$:.unshift libdir
|
||||
end
|
||||
|
||||
$:.unshift File.expand_path("../lib", bin_file)
|
||||
|
||||
require "foreman/cli"
|
||||
|
||||
Foreman::CLI.start
|
||||
24
dist/tgz.rake
vendored
Normal file
24
dist/tgz.rake
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
file pkg("foreman-#{version}.tgz") => distribution_files do |t|
|
||||
tempdir do |dir|
|
||||
mkchdir("foreman") do
|
||||
assemble_distribution
|
||||
assemble_gems
|
||||
rm_rf "bin"
|
||||
assemble resource("tgz/foreman"), "foreman", 0755
|
||||
end
|
||||
|
||||
sh "tar czvf #{t.name} foreman"
|
||||
end
|
||||
end
|
||||
|
||||
task "tgz:build" => pkg("foreman-#{version}.tgz")
|
||||
|
||||
task "tgz:clean" do
|
||||
clean pkg("foreman-#{version}.tgz")
|
||||
end
|
||||
|
||||
task "tgz:release" => "tgz:build" do |t|
|
||||
store pkg("foreman-#{version}.tgz"), "foreman/foreman-#{version}.tgz"
|
||||
store pkg("foreman-#{version}.tgz"), "foreman/foreman-beta.tgz" if beta?
|
||||
store pkg("foreman-#{version}.tgz"), "foreman/foreman.tgz" unless beta?
|
||||
end
|
||||
@@ -16,14 +16,16 @@ Gem::Specification.new do |gem|
|
||||
gem.files = Dir["**/*"].select { |d| d =~ %r{^(README|bin/|data/|ext/|lib/|spec/|test/)} }
|
||||
gem.files << "man/foreman.1"
|
||||
|
||||
gem.add_dependency 'term-ansicolor', '~> 1.0.5'
|
||||
gem.add_dependency 'term-ansicolor', '~> 1.0.7'
|
||||
gem.add_dependency 'thor', '>= 0.13.6'
|
||||
|
||||
gem.add_development_dependency 'parka'
|
||||
gem.add_development_dependency 'rake'
|
||||
gem.add_development_dependency 'ronn'
|
||||
gem.add_development_dependency 'fakefs', '~> 0.2.1'
|
||||
gem.add_development_dependency 'rcov', '~> 0.9.8'
|
||||
gem.add_development_dependency 'rr', '~> 1.0.2'
|
||||
gem.add_development_dependency 'rspec', '~> 2.6.0'
|
||||
if ENV["PLATFORM"] == "java"
|
||||
gem.add_dependency "posix-spawn", "~> 0.3.6"
|
||||
gem.platform = Gem::Platform.new("java")
|
||||
end
|
||||
|
||||
if ENV["PLATFORM"] == "mingw32"
|
||||
gem.add_dependency "win32console", "~> 1.3.0"
|
||||
gem.platform = Gem::Platform.new("mingw32")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,5 +4,22 @@ module Foreman
|
||||
|
||||
class AppDoesNotExist < Exception; end
|
||||
|
||||
end
|
||||
# load contents of env_file into ENV
|
||||
def self.load_env!(env_file = './.env')
|
||||
require 'foreman/engine'
|
||||
Foreman::Engine.load_env!(env_file)
|
||||
end
|
||||
|
||||
def self.runner
|
||||
File.expand_path("../../bin/runner", __FILE__)
|
||||
end
|
||||
|
||||
def self.jruby?
|
||||
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java"
|
||||
end
|
||||
|
||||
def self.windows?
|
||||
defined?(RUBY_PLATFORM) and RUBY_PLATFORM =~ /(win|w)32$/
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,50 +1,51 @@
|
||||
require "foreman"
|
||||
require "foreman/helpers"
|
||||
require "foreman/engine"
|
||||
require "foreman/export"
|
||||
require "thor"
|
||||
require "yaml"
|
||||
|
||||
class Foreman::CLI < Thor
|
||||
include Foreman::Helpers
|
||||
|
||||
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
||||
|
||||
desc "start [PROCESS]", "Start the application, or a specific process"
|
||||
desc "start", "Start the application"
|
||||
|
||||
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
||||
class_option :app_root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"
|
||||
|
||||
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
||||
method_option :port, :type => :numeric, :aliases => "-p"
|
||||
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
|
||||
|
||||
def start(process=nil)
|
||||
check_procfile!
|
||||
|
||||
if process
|
||||
engine.execute(process, options)
|
||||
else
|
||||
engine.start(options)
|
||||
class << self
|
||||
# Hackery. Take the run method away from Thor so that we can redefine it.
|
||||
def is_thor_reserved_word?(word, type)
|
||||
return false if word == 'run'
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
def start
|
||||
check_procfile!
|
||||
engine.start
|
||||
end
|
||||
|
||||
desc "export FORMAT LOCATION", "Export the application to another process management format"
|
||||
|
||||
method_option :app, :type => :string, :aliases => "-a"
|
||||
method_option :log, :type => :string, :aliases => "-l"
|
||||
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
||||
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!
|
||||
|
||||
formatter = case format
|
||||
when "inittab" then Foreman::Export::Inittab
|
||||
when "upstart" then Foreman::Export::Upstart
|
||||
else error "Unknown export format: #{format}."
|
||||
end
|
||||
|
||||
formatter.new(engine).export(location, options)
|
||||
|
||||
formatter = Foreman::Export.formatter(format)
|
||||
formatter.new(location, engine, options).export
|
||||
rescue Foreman::Export::Exception => ex
|
||||
error ex.message
|
||||
end
|
||||
@@ -52,9 +53,21 @@ class Foreman::CLI < Thor
|
||||
desc "check", "Validate your application's Procfile"
|
||||
|
||||
def check
|
||||
processes = engine.processes_in_order.map { |p| p.first }
|
||||
error "no processes defined" unless processes.length > 0
|
||||
display "valid procfile detected (#{processes.join(', ')})"
|
||||
error "no processes defined" unless engine.procfile.entries.length > 0
|
||||
puts "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
|
||||
end
|
||||
|
||||
desc "run COMMAND", "Run a command using your application's environment"
|
||||
|
||||
def run(*args)
|
||||
engine.apply_environment!
|
||||
begin
|
||||
exec args.join(" ")
|
||||
rescue Errno::EACCES
|
||||
error "not executable: #{args.first}"
|
||||
rescue Errno::ENOENT
|
||||
error "command not found: #{args.first}"
|
||||
end
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
@@ -64,31 +77,22 @@ private ######################################################################
|
||||
end
|
||||
|
||||
def engine
|
||||
@engine ||= Foreman::Engine.new(procfile)
|
||||
@engine ||= Foreman::Engine.new(procfile, options)
|
||||
end
|
||||
|
||||
def procfile
|
||||
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")
|
||||
defaults = YAML::load_file(".foreman") || {}
|
||||
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
9
lib/foreman/distribution.rb
Normal file
9
lib/foreman/distribution.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
module Foreman
|
||||
module Distribution
|
||||
def self.files
|
||||
Dir[File.expand_path("../../../{bin,data,lib}/**/*", __FILE__)].select do |file|
|
||||
File.file?(file)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,233 +1,234 @@
|
||||
require "foreman"
|
||||
require "foreman/process"
|
||||
require "foreman/procfile"
|
||||
require "foreman/utils"
|
||||
require "pty"
|
||||
require "tempfile"
|
||||
require "timeout"
|
||||
require "term/ansicolor"
|
||||
require "fileutils"
|
||||
require "thread"
|
||||
|
||||
class Foreman::Engine
|
||||
|
||||
attr_reader :procfile
|
||||
attr_reader :directory
|
||||
attr_reader :options
|
||||
|
||||
extend Term::ANSIColor
|
||||
|
||||
COLORS = [ cyan, yellow, green, magenta, red ]
|
||||
COLORS = [ cyan, yellow, green, magenta, red, blue,
|
||||
intense_cyan, intense_yellow, intense_green, intense_magenta,
|
||||
intense_red, intense_blue ]
|
||||
|
||||
def initialize(procfile)
|
||||
@procfile = read_procfile(procfile)
|
||||
@directory = File.expand_path(File.dirname(procfile))
|
||||
def initialize(procfile, options={})
|
||||
@procfile = Foreman::Procfile.new(procfile)
|
||||
@directory = options[:app_root] || File.expand_path(File.dirname(procfile))
|
||||
@options = options
|
||||
@environment = read_environment_files(options[:env])
|
||||
@output_mutex = Mutex.new
|
||||
end
|
||||
|
||||
def processes
|
||||
@processes ||= begin
|
||||
@order = []
|
||||
procfile.split("\n").inject({}) do |hash, line|
|
||||
next hash if line.strip == ""
|
||||
name, command = line.split(/ *: +/, 2)
|
||||
unless command
|
||||
warn_deprecated_procfile!
|
||||
name, command = line.split(/ +/, 2)
|
||||
end
|
||||
process = Foreman::Process.new(name, command)
|
||||
process.color = next_color
|
||||
@order << process.name
|
||||
hash.update(process.name => process)
|
||||
end
|
||||
end
|
||||
def self.load_env!(env_file)
|
||||
@environment = read_environment_files(env_file)
|
||||
apply_environment!
|
||||
end
|
||||
|
||||
def process_order
|
||||
processes
|
||||
@order.uniq
|
||||
end
|
||||
|
||||
def processes_in_order
|
||||
process_order.map do |name|
|
||||
[name, processes[name]]
|
||||
end
|
||||
end
|
||||
|
||||
def start(options={})
|
||||
environment = read_environment(options[:env])
|
||||
|
||||
def start
|
||||
proctitle "ruby: foreman master"
|
||||
|
||||
processes_in_order.each do |name, process|
|
||||
fork process, options, environment
|
||||
end
|
||||
|
||||
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
||||
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
||||
|
||||
watch_for_termination
|
||||
end
|
||||
|
||||
def execute(name, options={})
|
||||
environment = read_environment(options[:env])
|
||||
|
||||
fork processes[name], options, environment
|
||||
termtitle "#{File.basename(@directory)} - foreman"
|
||||
|
||||
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
||||
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
||||
|
||||
assign_colors
|
||||
spawn_processes
|
||||
watch_for_output
|
||||
watch_for_termination
|
||||
end
|
||||
|
||||
def port_for(process, num, base_port=nil)
|
||||
base_port ||= 5000
|
||||
offset = processes_in_order.map { |p| p.first }.index(process.name) * 100
|
||||
offset = procfile.process_names.index(process.name) * 100
|
||||
base_port.to_i + offset + num - 1
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def fork(process, options={}, environment={})
|
||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||
def spawn_processes
|
||||
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
|
||||
|
||||
1.upto(concurrency[process.name]) do |num|
|
||||
fork_individual(process, num, port_for(process, num, options[:port]), environment)
|
||||
procfile.entries.each do |entry|
|
||||
reader, writer = IO.pipe
|
||||
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
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def fork_individual(process, num, port, environment)
|
||||
environment.each { |k,v| ENV[k] = v }
|
||||
|
||||
ENV["PORT"] = port.to_s
|
||||
ENV["PS"] = "#{process.name}.#{num}"
|
||||
|
||||
pid = Process.fork do
|
||||
run(process)
|
||||
end
|
||||
|
||||
info "started with pid #{pid}", process
|
||||
running_processes[pid] = process
|
||||
end
|
||||
|
||||
def run(process)
|
||||
proctitle "ruby: foreman #{process.name}"
|
||||
trap("SIGINT", "IGNORE")
|
||||
|
||||
begin
|
||||
Dir.chdir directory do
|
||||
PTY.spawn(runner, process.command) do |stdin, stdout, pid|
|
||||
trap("SIGTERM") { Process.kill("SIGTERM", pid) }
|
||||
until stdin.eof?
|
||||
info stdin.gets, process
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue PTY::ChildExited, Interrupt, Errno::EIO
|
||||
begin
|
||||
info "process exiting", process
|
||||
rescue Interrupt
|
||||
end
|
||||
end
|
||||
def base_port
|
||||
options[:port] || 5000
|
||||
end
|
||||
|
||||
def kill_all(signal="SIGTERM")
|
||||
running_processes.each do |pid, process|
|
||||
Process.kill(signal, pid) rescue Errno::ESRCH
|
||||
info "sending #{signal} to pid #{pid}"
|
||||
process.kill signal
|
||||
end
|
||||
end
|
||||
|
||||
def info(message, process=nil)
|
||||
print process.color if process
|
||||
print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(process)} | "
|
||||
print Term::ANSIColor.reset
|
||||
print message.chomp
|
||||
puts
|
||||
def terminate_gracefully
|
||||
return if @terminating
|
||||
@terminating = true
|
||||
info "sending SIGTERM to all processes"
|
||||
kill_all "SIGTERM"
|
||||
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 error(message)
|
||||
puts "ERROR: #{message}"
|
||||
exit 1
|
||||
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
|
||||
end
|
||||
rescue Exception => ex
|
||||
puts ex.message
|
||||
puts ex.backtrace
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def watch_for_termination
|
||||
pid, status = Process.wait2
|
||||
process = running_processes.delete(pid)
|
||||
info "process terminated", process.name
|
||||
terminate_gracefully
|
||||
rescue Errno::ECHILD
|
||||
end
|
||||
|
||||
def info(message, name="system", color=Term::ANSIColor.white)
|
||||
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)
|
||||
@output_mutex.synchronize do
|
||||
$stdout.print message
|
||||
end
|
||||
end
|
||||
|
||||
def puts(message=nil)
|
||||
@output_mutex.synchronize do
|
||||
$stdout.puts message
|
||||
end
|
||||
end
|
||||
|
||||
def longest_process_name
|
||||
@longest_process_name ||= begin
|
||||
longest = processes.keys.map { |name| name.length }.sort.last
|
||||
longest = procfile.process_names.map { |name| name.length }.sort.last
|
||||
longest = 6 if longest < 6 # system
|
||||
longest
|
||||
end
|
||||
end
|
||||
|
||||
def pad_process_name(process)
|
||||
name = process ? "#{ENV["PS"]}" : "system"
|
||||
name.ljust(longest_process_name + 3) # add 3 for process number padding
|
||||
end
|
||||
|
||||
def print_info
|
||||
info "currently running processes:"
|
||||
running_processes.each do |pid, process|
|
||||
info "pid #{pid}", process
|
||||
end
|
||||
def pad_process_name(name="system")
|
||||
name.to_s.ljust(longest_process_name + 3) # add 3 for process number padding
|
||||
end
|
||||
|
||||
def proctitle(title)
|
||||
$0 = title
|
||||
end
|
||||
|
||||
def read_procfile(procfile)
|
||||
File.read(procfile)
|
||||
end
|
||||
|
||||
def watch_for_termination
|
||||
pid, status = Process.wait2
|
||||
process = running_processes.delete(pid)
|
||||
info "process terminated", process
|
||||
terminate_gracefully
|
||||
kill_all
|
||||
rescue Errno::ECHILD
|
||||
def termtitle(title)
|
||||
printf("\033]0;#{title}\007") unless Foreman.windows?
|
||||
end
|
||||
|
||||
def running_processes
|
||||
@running_processes ||= {}
|
||||
end
|
||||
|
||||
def readers
|
||||
@readers ||= {}
|
||||
end
|
||||
|
||||
def colors
|
||||
@colors ||= {}
|
||||
end
|
||||
|
||||
def assign_colors
|
||||
procfile.entries.each do |entry|
|
||||
colors[entry.name] = next_color
|
||||
end
|
||||
end
|
||||
|
||||
def process_by_reader(reader)
|
||||
readers.invert[reader]
|
||||
end
|
||||
|
||||
def next_color
|
||||
@current_color ||= -1
|
||||
@current_color += 1
|
||||
@current_color >= COLORS.length ? "" : COLORS[@current_color]
|
||||
@current_color = 0 if COLORS.length < @current_color
|
||||
COLORS[@current_color]
|
||||
end
|
||||
|
||||
def warn_deprecated_procfile!
|
||||
return if @already_warned_deprecated
|
||||
@already_warned_deprecated = true
|
||||
puts "!!! This format of Procfile is deprecated, and will not work starting in v0.12"
|
||||
puts "!!! Use a colon to separate the process name from the command"
|
||||
puts "!!! e.g. web: thin start"
|
||||
end
|
||||
module Env
|
||||
attr_reader :environment
|
||||
|
||||
def read_environment(filename)
|
||||
error "No such file: #{filename}" if filename && !File.exists?(filename)
|
||||
filename ||= ".env"
|
||||
environment = {}
|
||||
def read_environment_files(filenames)
|
||||
environment = {}
|
||||
|
||||
if File.exists?(filename)
|
||||
File.read(filename).split("\n").each do |line|
|
||||
if line =~ /\A([A-Za-z_]+)=(.*)\z/
|
||||
environment[$1] = $2
|
||||
(filenames || "").split(",").map(&:strip).each do |filename|
|
||||
error "No such file: #{filename}" unless File.exists?(filename)
|
||||
environment.merge!(read_environment(filename))
|
||||
end
|
||||
|
||||
environment.merge!(read_environment(".env")) unless filenames
|
||||
environment
|
||||
end
|
||||
|
||||
def read_environment(filename)
|
||||
return {} unless File.exists?(filename)
|
||||
|
||||
File.read(filename).split("\n").inject({}) do |hash, line|
|
||||
if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
|
||||
hash[$1] = $2
|
||||
end
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
||||
environment
|
||||
end
|
||||
|
||||
def runner
|
||||
File.expand_path("../../../bin/foreman-runner", __FILE__)
|
||||
end
|
||||
|
||||
def terminate_gracefully
|
||||
info "sending SIGTERM to all processes"
|
||||
kill_all "SIGTERM"
|
||||
Timeout.timeout(3) { Process.waitall }
|
||||
rescue Timeout::Error
|
||||
info "sending SIGKILL to all processes"
|
||||
kill_all "SIGKILL"
|
||||
def apply_environment!
|
||||
@environment.each { |k,v| ENV[k] = v }
|
||||
end
|
||||
|
||||
def error(message)
|
||||
puts "ERROR: #{message}"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
include Env
|
||||
extend Env
|
||||
end
|
||||
|
||||
@@ -1,9 +1,32 @@
|
||||
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"
|
||||
require "foreman/export/bluepill"
|
||||
require "foreman/export/runit"
|
||||
|
||||
@@ -3,10 +3,17 @@ require "foreman/utils"
|
||||
|
||||
class Foreman::Export::Base
|
||||
|
||||
attr_reader :engine
|
||||
attr_reader :location, :engine, :app, :log, :port, :user, :template, :concurrency
|
||||
|
||||
def initialize(engine)
|
||||
@engine = engine
|
||||
def initialize(location, engine, options={})
|
||||
@location = location
|
||||
@engine = engine
|
||||
@app = options[:app]
|
||||
@log = options[:log]
|
||||
@port = options[:port]
|
||||
@user = options[:user]
|
||||
@template = options[:template]
|
||||
@concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||
end
|
||||
|
||||
def export
|
||||
@@ -26,7 +33,7 @@ private ######################################################################
|
||||
def 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.join("~/.foreman/templates", file))
|
||||
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__))
|
||||
|
||||
26
lib/foreman/export/bluepill.rb
Normal file
26
lib/foreman/export/bluepill.rb
Normal file
@@ -0,0 +1,26 @@
|
||||
require "erb"
|
||||
require "foreman/export"
|
||||
|
||||
class Foreman::Export::Bluepill < Foreman::Export::Base
|
||||
|
||||
def export
|
||||
error("Must specify a location") unless location
|
||||
|
||||
FileUtils.mkdir_p location
|
||||
|
||||
app = self.app || File.basename(engine.directory)
|
||||
user = self.user || app
|
||||
log_root = self.log || "/var/log/#{app}"
|
||||
template_root = self.template
|
||||
|
||||
Dir["#{location}/#{app}.pill"].each do |file|
|
||||
say "cleaning up: #{file}"
|
||||
FileUtils.rm(file)
|
||||
end
|
||||
|
||||
master_template = export_template("bluepill", "master.pill.erb", template_root)
|
||||
master_config = ERB.new(master_template).result(binding)
|
||||
write_file "#{location}/#{app}.pill", master_config
|
||||
end
|
||||
|
||||
end
|
||||
@@ -2,20 +2,18 @@ require "foreman/export"
|
||||
|
||||
class Foreman::Export::Inittab < Foreman::Export::Base
|
||||
|
||||
def export(fname=nil, options={})
|
||||
app = options[:app] || File.basename(engine.directory)
|
||||
user = options[:user] || app
|
||||
log_root = options[:log] || "/var/log/#{app}"
|
||||
|
||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||
def export
|
||||
app = self.app || File.basename(engine.directory)
|
||||
user = self.user || app
|
||||
log_root = self.log || "/var/log/#{app}"
|
||||
|
||||
inittab = []
|
||||
inittab << "# ----- foreman #{app} processes -----"
|
||||
|
||||
engine.processes.values.inject(1) do |index, process|
|
||||
1.upto(concurrency[process.name]) do |num|
|
||||
engine.procfile.entries.inject(1) do |index, process|
|
||||
1.upto(self.concurrency[process.name]) do |num|
|
||||
id = app.slice(0, 2).upcase + sprintf("%02d", index)
|
||||
port = engine.port_for(process, num, options[:port])
|
||||
port = engine.port_for(process, num, self.port)
|
||||
inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log_root}/#{process.name}-#{num}.log 2>&1'"
|
||||
index += 1
|
||||
end
|
||||
@@ -26,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
|
||||
|
||||
|
||||
59
lib/foreman/export/runit.rb
Normal file
59
lib/foreman/export/runit.rb
Normal file
@@ -0,0 +1,59 @@
|
||||
require "erb"
|
||||
require "foreman/export"
|
||||
|
||||
class Foreman::Export::Runit < Foreman::Export::Base
|
||||
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
||||
|
||||
def export
|
||||
error("Must specify a location") unless location
|
||||
|
||||
app = self.app || File.basename(engine.directory)
|
||||
user = self.user || app
|
||||
log_root = self.log || "/var/log/#{app}"
|
||||
template_root = self.template
|
||||
|
||||
run_template = export_template('runit', 'run.erb', template_root)
|
||||
log_run_template = export_template('runit', 'log_run.erb', template_root)
|
||||
|
||||
engine.procfile.entries.each do |process|
|
||||
1.upto(self.concurrency[process.name]) do |num|
|
||||
process_directory = "#{location}/#{app}-#{process.name}-#{num}"
|
||||
process_env_directory = "#{process_directory}/env"
|
||||
process_log_directory = "#{process_directory}/log"
|
||||
|
||||
create_directory process_directory
|
||||
create_directory process_env_directory
|
||||
create_directory process_log_directory
|
||||
|
||||
run = ERB.new(run_template).result(binding)
|
||||
write_file "#{process_directory}/run", run
|
||||
FileUtils.chmod 0755, "#{process_directory}/run"
|
||||
|
||||
port = engine.port_for(process, num, self.port)
|
||||
environment_variables = {'PORT' => port}.
|
||||
merge(engine.environment).
|
||||
merge(inline_variables(process.command))
|
||||
|
||||
environment_variables.each_pair do |var, env|
|
||||
write_file "#{process_env_directory}/#{var.upcase}", env
|
||||
end
|
||||
|
||||
log_run = ERB.new(log_run_template).result(binding)
|
||||
write_file "#{process_log_directory}/run", log_run
|
||||
FileUtils.chmod 0755, "#{process_log_directory}/run"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
private
|
||||
def create_directory(location)
|
||||
say "creating: #{location}"
|
||||
FileUtils.mkdir_p(location)
|
||||
end
|
||||
|
||||
def inline_variables(command)
|
||||
variable_name_regex =
|
||||
Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
|
||||
end
|
||||
end
|
||||
@@ -3,36 +3,35 @@ require "foreman/export"
|
||||
|
||||
class Foreman::Export::Upstart < Foreman::Export::Base
|
||||
|
||||
def export(location, options={})
|
||||
def export
|
||||
error("Must specify a location") unless location
|
||||
|
||||
FileUtils.mkdir_p location
|
||||
|
||||
app = options[:app] || File.basename(engine.directory)
|
||||
user = options[:user] || app
|
||||
log_root = options[:log] || "/var/log/#{app}"
|
||||
template_root = options[:template]
|
||||
app = self.app || File.basename(engine.directory)
|
||||
user = self.user || app
|
||||
log_root = self.log || "/var/log/#{app}"
|
||||
template_root = self.template
|
||||
|
||||
Dir["#{location}/#{app}*.conf"].each do |file|
|
||||
say "cleaning up: #{file}"
|
||||
FileUtils.rm(file)
|
||||
end
|
||||
|
||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||
|
||||
master_template = export_template("upstart", "master.conf.erb", template_root)
|
||||
master_config = ERB.new(master_template).result(binding)
|
||||
write_file "#{location}/#{app}.conf", master_config
|
||||
|
||||
process_template = export_template("upstart", "process.conf.erb", template_root)
|
||||
|
||||
engine.processes.values.each do |process|
|
||||
engine.procfile.entries.each do |process|
|
||||
next if (conc = self.concurrency[process.name]) < 1
|
||||
process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
|
||||
process_master_config = ERB.new(process_master_template).result(binding)
|
||||
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
|
||||
|
||||
1.upto(concurrency[process.name]) do |num|
|
||||
port = engine.port_for(process, num, options[:port])
|
||||
1.upto(self.concurrency[process.name]) do |num|
|
||||
port = engine.port_for(process, num, self.port)
|
||||
process_config = ERB.new(process_template).result(binding)
|
||||
write_file "#{location}/#{app}-#{process.name}-#{num}.conf", process_config
|
||||
end
|
||||
|
||||
45
lib/foreman/helpers.rb
Normal file
45
lib/foreman/helpers.rb
Normal file
@@ -0,0 +1,45 @@
|
||||
module Foreman::Helpers
|
||||
# Copied whole sale from, https://github.com/defunkt/resque/
|
||||
|
||||
# Given a word with dashes, returns a camel cased version of it.
|
||||
#
|
||||
# classify('job-name') # => 'JobName'
|
||||
def classify(dashed_word)
|
||||
dashed_word.split('-').each { |part| part[0] = part[0].chr.upcase }.join
|
||||
end # Tries to find a constant with the name specified in the argument string:
|
||||
|
||||
#
|
||||
# constantize("Module") # => Module
|
||||
# constantize("Test::Unit") # => Test::Unit
|
||||
#
|
||||
# The name is assumed to be the one of a top-level constant, no matter
|
||||
# whether it starts with "::" or not. No lexical context is taken into
|
||||
# account:
|
||||
#
|
||||
# C = 'outside'
|
||||
# module M
|
||||
# C = 'inside'
|
||||
# C # => 'inside'
|
||||
# constantize("C") # => 'outside', same as ::C
|
||||
# end
|
||||
#
|
||||
# NameError is raised when the constant is unknown.
|
||||
def constantize(camel_cased_word)
|
||||
camel_cased_word = camel_cased_word.to_s
|
||||
|
||||
names = camel_cased_word.split('::')
|
||||
names.shift if names.empty? || names.first.empty?
|
||||
|
||||
constant = Object
|
||||
names.each do |name|
|
||||
args = Module.method(:const_get).arity != 1 ? [false] : []
|
||||
|
||||
if constant.const_defined?(name, *args)
|
||||
constant = constant.const_get(name)
|
||||
else
|
||||
constant = constant.const_missing(name)
|
||||
end
|
||||
end
|
||||
constant
|
||||
end
|
||||
end
|
||||
@@ -1,14 +1,96 @@
|
||||
require "foreman"
|
||||
require "rubygems"
|
||||
|
||||
class Foreman::Process
|
||||
|
||||
attr_reader :name
|
||||
attr_reader :command
|
||||
attr_accessor :color
|
||||
attr_reader :entry
|
||||
attr_reader :num
|
||||
attr_reader :pid
|
||||
attr_reader :port
|
||||
|
||||
def initialize(name, command)
|
||||
@name = name
|
||||
@command = command
|
||||
def initialize(entry, num, port)
|
||||
@entry = entry
|
||||
@num = num
|
||||
@port = port
|
||||
end
|
||||
|
||||
def run(pipe, basedir, environment)
|
||||
with_environment(environment.merge("PORT" => port.to_s)) do
|
||||
run_process basedir, entry.command, pipe
|
||||
end
|
||||
end
|
||||
|
||||
def name
|
||||
"%s.%s" % [ entry.name, num ]
|
||||
end
|
||||
|
||||
def kill(signal)
|
||||
pid && Process.kill(signal, pid)
|
||||
rescue Errno::ESRCH
|
||||
false
|
||||
end
|
||||
|
||||
def detach
|
||||
pid && Process.detach(pid)
|
||||
end
|
||||
|
||||
def alive?
|
||||
kill(0)
|
||||
end
|
||||
|
||||
def dead?
|
||||
!alive?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fork_with_io(command, basedir)
|
||||
reader, writer = IO.pipe
|
||||
command = replace_command_env(command)
|
||||
pid = if Foreman.windows?
|
||||
Dir.chdir(basedir) do
|
||||
Process.spawn command, :out => writer, :err => writer
|
||||
end
|
||||
elsif Foreman.jruby?
|
||||
require "posix/spawn"
|
||||
POSIX::Spawn.spawn(Foreman.runner, "-d", basedir, command, {
|
||||
:out => writer, :err => writer
|
||||
})
|
||||
else
|
||||
fork do
|
||||
writer.sync = true
|
||||
$stdout.reopen writer
|
||||
$stderr.reopen writer
|
||||
reader.close
|
||||
exec Foreman.runner, "-d", basedir, command
|
||||
end
|
||||
end
|
||||
[ reader, pid ]
|
||||
end
|
||||
|
||||
def run_process(basedir, command, pipe)
|
||||
io, @pid = fork_with_io(command, basedir)
|
||||
output pipe, "started with pid %d" % @pid
|
||||
Thread.new do
|
||||
until io.eof?
|
||||
output pipe, io.gets
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def output(pipe, message)
|
||||
pipe.puts "%s,%s" % [ name, message ]
|
||||
end
|
||||
|
||||
def replace_command_env(command)
|
||||
command.gsub(/\$(\w+)/) { |e| ENV[e[1..-1]] }
|
||||
end
|
||||
|
||||
def with_environment(environment)
|
||||
original = ENV.to_hash
|
||||
ENV.update environment
|
||||
yield
|
||||
ensure
|
||||
ENV.replace original
|
||||
end
|
||||
end
|
||||
|
||||
38
lib/foreman/procfile.rb
Normal file
38
lib/foreman/procfile.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
require "foreman"
|
||||
require "foreman/procfile_entry"
|
||||
|
||||
# A valid Procfile entry is captured by this regex.
|
||||
# All other lines are ignored.
|
||||
#
|
||||
# /^([A-Za-z0-9_]+):\s*(.+)$/
|
||||
#
|
||||
# $1 = name
|
||||
# $2 = command
|
||||
#
|
||||
class Foreman::Procfile
|
||||
|
||||
attr_reader :entries
|
||||
|
||||
def initialize(filename)
|
||||
@entries = parse_procfile(filename)
|
||||
end
|
||||
|
||||
def [](name)
|
||||
entries.detect { |entry| entry.name == name }
|
||||
end
|
||||
|
||||
def process_names
|
||||
entries.map(&:name)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse_procfile(filename)
|
||||
File.read(filename).split("\n").map do |line|
|
||||
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
||||
Foreman::ProcfileEntry.new($1, $2)
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
|
||||
end
|
||||
22
lib/foreman/procfile_entry.rb
Normal file
22
lib/foreman/procfile_entry.rb
Normal file
@@ -0,0 +1,22 @@
|
||||
require "foreman"
|
||||
|
||||
class Foreman::ProcfileEntry
|
||||
|
||||
attr_reader :name
|
||||
attr_reader :command
|
||||
attr_accessor :color
|
||||
|
||||
def initialize(name, command)
|
||||
@name = name
|
||||
@command = command
|
||||
end
|
||||
|
||||
def spawn(num, pipe, basedir, environment, base_port)
|
||||
(1..num).to_a.map do |n|
|
||||
process = Foreman::Process.new(self, n, base_port + (n-1))
|
||||
process.run(pipe, basedir, environment)
|
||||
process
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -3,9 +3,12 @@ require "foreman"
|
||||
class Foreman::Utils
|
||||
|
||||
def self.parse_concurrency(concurrency)
|
||||
@concurrency ||= begin
|
||||
begin
|
||||
pairs = concurrency.to_s.gsub(/\s/, "").split(",")
|
||||
pairs.inject(Hash.new(1)) do |hash, pair|
|
||||
|
||||
default = concurrency.nil? ? 1 : 0
|
||||
|
||||
pairs.inject(Hash.new(default)) do |hash, pair|
|
||||
process, amount = pair.split("=")
|
||||
hash.update(process => amount.to_i)
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module Foreman
|
||||
|
||||
VERSION = "0.20.0"
|
||||
VERSION = "0.37.0"
|
||||
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "FOREMAN" "1" "August 2011" "Foreman 0.19.0" "Foreman Manual"
|
||||
.TH "FOREMAN" "1" "January 2012" "Foreman 0.33.1" "Foreman Manual"
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBforeman\fR \- manage Procfile\-based applications
|
||||
@@ -68,8 +68,12 @@ 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\-\-directory\fR
|
||||
Specify an alternate application root\. This defaults to the directory containing the Procfile\.
|
||||
.
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-env\fR
|
||||
Specify an alternate environment file\.
|
||||
Specify an alternate environment file\. You can specify more than one file by using: \fB\-\-env file1,file2\fR\.
|
||||
.
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-procfile\fR
|
||||
@@ -79,9 +83,15 @@ Specify an alternate location for the application\'s Procfile\. This file\'s con
|
||||
foreman currently supports the following output formats:
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
bluepill
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
inittab
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
runit
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
upstart
|
||||
.
|
||||
.IP "" 0
|
||||
@@ -129,7 +139,7 @@ job: bundle exec rake jobs:work
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
You can validate your Procfile format using the \fBcheck\fR command
|
||||
A process name may contain letters, numbers amd the underscore character\. You can validate your Procfile format using the \fBcheck\fR command:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
@@ -162,7 +172,7 @@ If a \fB\.foreman\fR file exists in the current directory, default options will
|
||||
.
|
||||
.nf
|
||||
|
||||
concurrency: alpha=0
|
||||
concurrency: alpha=0,bravo=1
|
||||
port: 15000
|
||||
.
|
||||
.fi
|
||||
|
||||
@@ -66,8 +66,13 @@ The following options control how the application is run:
|
||||
|
||||
These options control all modes of foreman's operation.
|
||||
|
||||
* `-d`, `--directory`:
|
||||
Specify an alternate application root. This defaults to the directory
|
||||
containing the Procfile.
|
||||
|
||||
* `-e`, `--env`:
|
||||
Specify an alternate environment file.
|
||||
Specify an alternate environment file. You can specify more than one
|
||||
file by using: `--env file1,file2`.
|
||||
|
||||
* `-f`, `--procfile`:
|
||||
Specify an alternate location for the application's Procfile. This file's
|
||||
@@ -78,8 +83,12 @@ These options control all modes of foreman's operation.
|
||||
|
||||
foreman currently supports the following output formats:
|
||||
|
||||
* bluepill
|
||||
|
||||
* inittab
|
||||
|
||||
* runit
|
||||
|
||||
* upstart
|
||||
|
||||
## INITTAB EXPORT
|
||||
@@ -110,7 +119,8 @@ to run it.
|
||||
web: bundle exec thin start
|
||||
job: bundle exec rake jobs:work
|
||||
|
||||
You can validate your Procfile format using the `check` command
|
||||
A process name may contain letters, numbers amd the underscore character.
|
||||
You can validate your Procfile format using the `check` command:
|
||||
|
||||
$ foreman check
|
||||
|
||||
@@ -129,7 +139,7 @@ If a `.foreman` file exists in the current directory, default options will
|
||||
be read from it. This file should be in YAML format with the long option
|
||||
name as keys. Example:
|
||||
|
||||
concurrency: alpha=0
|
||||
concurrency: alpha=0,bravo=1
|
||||
port: 15000
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require "spec_helper"
|
||||
require "foreman/cli"
|
||||
|
||||
describe "Foreman::CLI" do
|
||||
describe "Foreman::CLI", :fakefs do
|
||||
subject { Foreman::CLI.new }
|
||||
|
||||
describe "start" do
|
||||
@@ -19,13 +19,33 @@ describe "Foreman::CLI" do
|
||||
|
||||
it "runs successfully" do
|
||||
dont_allow(subject).error
|
||||
mock.instance_of(Foreman::Engine).start({})
|
||||
mock.instance_of(Foreman::Engine).start
|
||||
subject.start
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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")
|
||||
mock_export = mock(Foreman::Export::Upstart)
|
||||
mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "env" => "envfile" }) { mock_export }
|
||||
mock_export.export
|
||||
foreman %{ export upstart /upstart --env envfile }
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a non-existent Procfile" do
|
||||
it "prints an error" do
|
||||
mock_error(subject, "Procfile does not exist.") do
|
||||
@@ -38,10 +58,18 @@ describe "Foreman::CLI" 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.") do
|
||||
subject.export("invalidformatter")
|
||||
mock_error(subject, "foo") do
|
||||
subject.export("errorful")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -51,7 +79,9 @@ describe "Foreman::CLI" do
|
||||
|
||||
it "runs successfully" do
|
||||
dont_allow(subject).error
|
||||
mock.instance_of(Foreman::Export::Upstart).export("/tmp/foo", {})
|
||||
mock_export = mock(Foreman::Export::Upstart)
|
||||
mock(Foreman::Export::Upstart).new("/tmp/foo", is_a(Foreman::Engine), {}) { mock_export }
|
||||
mock_export.export
|
||||
subject.export("upstart", "/tmp/foo")
|
||||
end
|
||||
end
|
||||
@@ -63,7 +93,7 @@ describe "Foreman::CLI" 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
|
||||
@@ -81,4 +111,53 @@ describe "Foreman::CLI" do
|
||||
end
|
||||
end
|
||||
|
||||
describe "run" do
|
||||
describe "with a valid Procfile" do
|
||||
before { write_procfile }
|
||||
|
||||
describe "and a command" do
|
||||
let(:command) { ["ls", "-l"] }
|
||||
|
||||
before(:each) do
|
||||
stub(subject).exec
|
||||
end
|
||||
|
||||
it "should load the environment file" do
|
||||
write_env
|
||||
preserving_env do
|
||||
subject.run *command
|
||||
ENV["FOO"].should == "bar"
|
||||
end
|
||||
|
||||
ENV["FOO"].should be_nil
|
||||
end
|
||||
|
||||
it "should runute the command as a string" do
|
||||
mock(subject).exec(command.join(" "))
|
||||
subject.run *command
|
||||
end
|
||||
end
|
||||
|
||||
describe "and a non-existent command" do
|
||||
let(:command) { "iuhtngrglhulhdfg" }
|
||||
|
||||
it "should print an error" do
|
||||
mock_error(subject, "command not found: #{command}") do
|
||||
subject.run command
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "and a non-executable command" do
|
||||
let(:command) { __FILE__ }
|
||||
|
||||
it "should print an error" do
|
||||
mock_error(subject, "not executable: #{command}") do
|
||||
subject.run command
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
|
||||
describe "Foreman::Engine" do
|
||||
subject { Foreman::Engine.new("Procfile") }
|
||||
describe "Foreman::Engine", :fakefs do
|
||||
subject { Foreman::Engine.new("Procfile", {}) }
|
||||
|
||||
describe "initialize" do
|
||||
describe "without an existing Procfile" do
|
||||
@@ -15,21 +15,8 @@ describe "Foreman::Engine" do
|
||||
before { write_procfile }
|
||||
|
||||
it "reads the processes" do
|
||||
subject.processes["alpha"].command.should == "./alpha"
|
||||
subject.processes["bravo"].command.should == "./bravo"
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a deprecated Procfile" do
|
||||
before do
|
||||
File.open("Procfile", "w") do |file|
|
||||
file.puts "name command"
|
||||
end
|
||||
end
|
||||
|
||||
it "should print a deprecation warning" do
|
||||
mock(subject).warn_deprecated_procfile!
|
||||
subject.processes.length.should == 1
|
||||
subject.procfile["alpha"].command.should == "./alpha"
|
||||
subject.procfile["bravo"].command.should == "./bravo"
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -37,28 +24,21 @@ describe "Foreman::Engine" do
|
||||
describe "start" do
|
||||
it "forks the processes" do
|
||||
write_procfile
|
||||
mock(subject).fork(subject.processes["alpha"], {}, {})
|
||||
mock(subject).fork(subject.processes["bravo"], {}, {})
|
||||
mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./alpha", is_a(IO))
|
||||
mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./bravo", is_a(IO))
|
||||
mock(subject).watch_for_output
|
||||
mock(subject).watch_for_termination
|
||||
subject.start
|
||||
end
|
||||
|
||||
it "handles concurrency" do
|
||||
write_procfile
|
||||
mock(subject).fork_individual(subject.processes["alpha"], 1, 5000, {})
|
||||
mock(subject).fork_individual(subject.processes["alpha"], 2, 5001, {})
|
||||
mock(subject).fork_individual(subject.processes["bravo"], 1, 5100, {})
|
||||
mock(subject).watch_for_termination
|
||||
subject.start(:concurrency => "alpha=2")
|
||||
end
|
||||
end
|
||||
|
||||
describe "execute" do
|
||||
it "runs the processes" do
|
||||
write_procfile
|
||||
mock(subject).fork(subject.processes["alpha"], {}, {})
|
||||
mock(subject).watch_for_termination
|
||||
subject.execute("alpha")
|
||||
engine = Foreman::Engine.new("Procfile",:concurrency => "alpha=2")
|
||||
mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./alpha", is_a(IO)).twice
|
||||
mock.instance_of(Foreman::Process).run_process(Dir.pwd, "./bravo", is_a(IO)).never
|
||||
mock(engine).watch_for_output
|
||||
mock(engine).watch_for_termination
|
||||
engine.start
|
||||
end
|
||||
end
|
||||
|
||||
@@ -66,24 +46,41 @@ describe "Foreman::Engine" do
|
||||
before(:each) do
|
||||
write_procfile
|
||||
stub(Process).fork
|
||||
stub(subject).info
|
||||
mock(subject).watch_for_termination
|
||||
end
|
||||
|
||||
it "should read if specified" do
|
||||
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
|
||||
subject.execute("alpha", :env => "/tmp/env")
|
||||
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
|
||||
stub(engine).info
|
||||
mock(engine).spawn_processes
|
||||
mock(engine).watch_for_termination
|
||||
engine.environment.should == {"FOO"=>"baz"}
|
||||
engine.start
|
||||
end
|
||||
|
||||
it "should read more than one if specified" do
|
||||
File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
|
||||
File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") }
|
||||
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2")
|
||||
stub(engine).info
|
||||
mock(engine).spawn_processes
|
||||
mock(engine).watch_for_termination
|
||||
engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" }
|
||||
engine.start
|
||||
end
|
||||
|
||||
it "should fail if specified and doesnt exist" do
|
||||
mock(subject).error("No such file: /tmp/env")
|
||||
subject.execute("alpha", :env => "/tmp/env")
|
||||
mock.instance_of(Foreman::Engine).error("No such file: /tmp/env")
|
||||
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
|
||||
end
|
||||
|
||||
it "should read .env if none specified" do
|
||||
File.open(".env", "w") { |f| f.puts("FOO=qoo") }
|
||||
mock(subject).fork_individual(anything, anything, anything, { "FOO" => "qoo" })
|
||||
subject.execute("bravo")
|
||||
engine = Foreman::Engine.new("Procfile")
|
||||
mock(engine).spawn_processes
|
||||
mock(engine).watch_for_termination
|
||||
engine.environment.should == {"FOO"=>"qoo"}
|
||||
engine.start
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
22
spec/foreman/export/base_spec.rb
Normal file
22
spec/foreman/export/base_spec.rb
Normal 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
|
||||
36
spec/foreman/export/bluepill_spec.rb
Normal file
36
spec/foreman/export/bluepill_spec.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
require "foreman/export/bluepill"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Bluepill, :fakefs do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||
let(:engine) { Foreman::Engine.new(procfile) }
|
||||
let(:options) { Hash.new }
|
||||
let(:bluepill) { Foreman::Export::Bluepill.new("/tmp/init", engine, options) }
|
||||
|
||||
before(:each) { load_export_templates_into_fakefs("bluepill") }
|
||||
before(:each) { stub(bluepill).say }
|
||||
|
||||
it "exports to the filesystem" do
|
||||
bluepill.export
|
||||
normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app.pill"))
|
||||
end
|
||||
|
||||
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
|
||||
bluepill.export
|
||||
normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app-concurrency.pill"))
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
40
spec/foreman/export/inittab_spec.rb
Normal file
40
spec/foreman/export/inittab_spec.rb
Normal 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
|
||||
41
spec/foreman/export/runit_spec.rb
Normal file
41
spec/foreman/export/runit_spec.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
require "foreman/export/runit"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Runit, :fakefs do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", 'bar=baz') }
|
||||
let(:engine) { Foreman::Engine.new(procfile) }
|
||||
let(:runit) { Foreman::Export::Runit.new('/tmp/init', engine, :concurrency => 'alpha=2,bravo=1') }
|
||||
|
||||
before(:each) { load_export_templates_into_fakefs("runit") }
|
||||
before(:each) { stub(runit).say }
|
||||
before(:each) { stub(FakeFS::FileUtils).chmod }
|
||||
|
||||
it "exports to the filesystem" do
|
||||
FileUtils.mkdir_p('/tmp/init')
|
||||
|
||||
runit.export
|
||||
|
||||
File.read("/tmp/init/app-alpha-1/run").should == example_export_file('runit/app-alpha-1-run')
|
||||
File.read("/tmp/init/app-alpha-1/log/run").should ==
|
||||
example_export_file('runit/app-alpha-1-log-run')
|
||||
File.read("/tmp/init/app-alpha-1/env/PORT").should == "5000\n"
|
||||
File.read("/tmp/init/app-alpha-1/env/BAR").should == "baz\n"
|
||||
|
||||
File.read("/tmp/init/app-alpha-2/run").should == example_export_file('runit/app-alpha-2-run')
|
||||
File.read("/tmp/init/app-alpha-2/log/run").should ==
|
||||
example_export_file('runit/app-alpha-2-log-run')
|
||||
File.read("/tmp/init/app-alpha-2/env/PORT").should == "5001\n"
|
||||
File.read("/tmp/init/app-alpha-2/env/BAR").should == "baz\n"
|
||||
|
||||
File.read("/tmp/init/app-bravo-1/run").should == example_export_file('runit/app-bravo-1-run')
|
||||
File.read("/tmp/init/app-bravo-1/log/run").should ==
|
||||
example_export_file('runit/app-bravo-1-log-run')
|
||||
File.read("/tmp/init/app-bravo-1/env/PORT").should == "5100\n"
|
||||
end
|
||||
|
||||
it "creates a full path to the export directory" do
|
||||
expect { runit.export }.to_not raise_error(Errno::ENOENT)
|
||||
end
|
||||
end
|
||||
@@ -3,27 +3,53 @@ require "foreman/engine"
|
||||
require "foreman/export/upstart"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Upstart do
|
||||
describe Foreman::Export::Upstart, :fakefs do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||
let(:engine) { Foreman::Engine.new(procfile) }
|
||||
let(:upstart) { Foreman::Export::Upstart.new(engine) }
|
||||
let(:engine) { Foreman::Engine.new(procfile) }
|
||||
let(:options) { Hash.new }
|
||||
let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, options) }
|
||||
|
||||
before(:each) { load_export_templates_into_fakefs("upstart") }
|
||||
before(:each) { stub(upstart).say }
|
||||
|
||||
it "exports to the filesystem" do
|
||||
upstart.export("/tmp/init")
|
||||
upstart.export
|
||||
|
||||
File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
|
||||
File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
|
||||
File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("upstart/app-alpha-1.conf")
|
||||
File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("upstart/app-alpha-2.conf")
|
||||
File.read("/tmp/init/app-bravo.conf").should == example_export_file("upstart/app-bravo.conf")
|
||||
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"] }
|
||||
|
||||
it "exports to the filesystem with concurrency" do
|
||||
upstart.export
|
||||
|
||||
File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
|
||||
File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
|
||||
File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("upstart/app-alpha-1.conf")
|
||||
File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("upstart/app-alpha-2.conf")
|
||||
File.exists?("/tmp/init/app-bravo-1.conf").should == false
|
||||
end
|
||||
end
|
||||
|
||||
context "with alternate templates" do
|
||||
let(:template_root) { "/tmp/alternate" }
|
||||
let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, :template => template_root) }
|
||||
|
||||
before do
|
||||
FileUtils.mkdir_p template_root
|
||||
@@ -31,22 +57,28 @@ describe Foreman::Export::Upstart do
|
||||
end
|
||||
|
||||
it "can export with alternate template files" do
|
||||
upstart.export("/tmp/init", :template => template_root)
|
||||
upstart.export
|
||||
|
||||
File.read("/tmp/init/app.conf").should == "alternate_template\n"
|
||||
end
|
||||
end
|
||||
|
||||
context "with alternate templates from home dir" do
|
||||
let(:default_template_root) {File.expand_path("~/.foreman/templates")}
|
||||
let(:default_template_root) {File.expand_path("#{ENV['HOME']}/.foreman/templates")}
|
||||
|
||||
before do
|
||||
ENV['_FOREMAN_SPEC_HOME'] = ENV['HOME']
|
||||
ENV['HOME'] = "/home/appuser"
|
||||
FileUtils.mkdir_p default_template_root
|
||||
File.open("#{default_template_root}/master.conf.erb", "w") { |f| f.puts "default_alternate_template" }
|
||||
end
|
||||
|
||||
after do
|
||||
ENV['HOME'] = ENV.delete('_FOREMAN_SPEC_HOME')
|
||||
end
|
||||
|
||||
it "can export with alternate template files" do
|
||||
upstart.export("/tmp/init")
|
||||
upstart.export
|
||||
|
||||
File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
|
||||
end
|
||||
|
||||
@@ -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
|
||||
|
||||
26
spec/foreman/helpers_spec.rb
Normal file
26
spec/foreman/helpers_spec.rb
Normal 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
|
||||
@@ -1,2 +1,131 @@
|
||||
require "spec_helper"
|
||||
require "foreman/process"
|
||||
require 'spec_helper'
|
||||
require 'foreman/process'
|
||||
require 'ostruct'
|
||||
require 'timeout'
|
||||
require 'tmpdir'
|
||||
|
||||
describe Foreman::Process do
|
||||
subject { described_class.new entry, number, port }
|
||||
|
||||
let(:number) { 1 }
|
||||
let(:port) { 777 }
|
||||
let(:command) { "script" }
|
||||
let(:name) { "foobar" }
|
||||
let(:entry) { OpenStruct.new :name => name, :command => command }
|
||||
|
||||
its(:entry) { entry }
|
||||
its(:num) { number }
|
||||
its(:port) { port }
|
||||
its(:name) { "#{name}.#{port}" }
|
||||
its(:pid) { nil }
|
||||
|
||||
describe '#run' do
|
||||
let(:pipe) { :pipe }
|
||||
let(:basedir) { Dir.mktmpdir }
|
||||
let(:env) {{ 'foo' => 'bar' }}
|
||||
let(:init_delta) { 0.1 }
|
||||
|
||||
after { FileUtils.remove_entry_secure basedir }
|
||||
|
||||
def run(cmd=command)
|
||||
entry.command = cmd
|
||||
subject.run pipe, basedir, env
|
||||
subject.detach && sleep(init_delta)
|
||||
end
|
||||
|
||||
def run_file(executable, code)
|
||||
file = File.open("#{basedir}/script", 'w') {|it| it << code }
|
||||
run "#{executable} #{file.path}"
|
||||
end
|
||||
|
||||
context 'options' do
|
||||
it 'should set PORT for environment' do
|
||||
mock(subject).run_process(basedir, command, pipe) do
|
||||
ENV['PORT'].should == port.to_s
|
||||
end
|
||||
run
|
||||
end
|
||||
|
||||
it 'should set custom variables for environment' do
|
||||
mock(subject).run_process(basedir, command, pipe) do
|
||||
ENV['foo'].should == 'bar'
|
||||
end
|
||||
run
|
||||
end
|
||||
|
||||
it 'should restore environment afterwards' do
|
||||
mock(subject).run_process(basedir, command, pipe)
|
||||
run
|
||||
ENV.should_not include('PORT', 'foo')
|
||||
end
|
||||
end
|
||||
|
||||
context 'process' do
|
||||
around do |spec|
|
||||
IO.pipe do |reader, writer|
|
||||
@reader, @writer = reader, writer
|
||||
spec.run
|
||||
end
|
||||
end
|
||||
|
||||
let(:pipe) { @writer }
|
||||
let(:output) { @reader.read_nonblock 1024 }
|
||||
|
||||
it 'should not block' do
|
||||
expect {
|
||||
Timeout.timeout(2*init_delta) { run 'sleep 2' }
|
||||
}.should_not raise_exception
|
||||
end
|
||||
|
||||
it 'should be alive' do
|
||||
run 'sleep 1'
|
||||
subject.should be_alive
|
||||
end
|
||||
|
||||
it 'should be dead' do
|
||||
run 'exit'
|
||||
subject.should be_dead
|
||||
end
|
||||
|
||||
it 'should be killable' do
|
||||
run 'sleep 1'
|
||||
subject.kill 'TERM'
|
||||
subject.should be_dead
|
||||
end
|
||||
|
||||
it 'should send different signals' do
|
||||
run_file 'ruby', <<-CODE
|
||||
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
|
||||
subject.kill 'KILL'
|
||||
subject.should be_dead
|
||||
end
|
||||
|
||||
it 'should redirect stdout' do
|
||||
run 'echo hey'
|
||||
output.should include('hey')
|
||||
end
|
||||
|
||||
it 'should redirect stderr' do
|
||||
run 'echo hey >2'
|
||||
output.should include('hey')
|
||||
end
|
||||
|
||||
it 'should handle variables' do
|
||||
run 'echo $PORT'
|
||||
output.should include('777')
|
||||
end
|
||||
|
||||
it 'should handle arguments' do
|
||||
pending
|
||||
run %{ sh -c "trap '' TERM; sleep 10" }
|
||||
subject.should be_alive
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,4 +8,27 @@ describe Foreman do
|
||||
it { should be_a String }
|
||||
end
|
||||
|
||||
describe "::load_env!(env_file)", :fakefs do
|
||||
after do
|
||||
ENV['FOO'] = nil
|
||||
end
|
||||
|
||||
it "should load env_file into ENV" do
|
||||
File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
|
||||
Foreman.load_env!("/tmp/env1")
|
||||
ENV['FOO'].should == 'bar'
|
||||
end
|
||||
|
||||
it "should assume env_file in ./.env" do
|
||||
File.open("./.env", "w") { |f| f.puts("FOO=bar") }
|
||||
Foreman.load_env!
|
||||
ENV['FOO'].should == 'bar'
|
||||
end
|
||||
end
|
||||
|
||||
describe "runner" do
|
||||
it "should exist" do
|
||||
File.exists?(Foreman.runner).should == true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
18
spec/helper_spec.rb
Normal file
18
spec/helper_spec.rb
Normal file
@@ -0,0 +1,18 @@
|
||||
require "spec_helper"
|
||||
|
||||
describe "spec helpers" do
|
||||
describe "#preserving_env" do
|
||||
after { ENV.delete "FOO" }
|
||||
|
||||
it "should remove added environment vars" do
|
||||
preserving_env { ENV["FOO"] = "baz" }
|
||||
ENV["FOO"].should == nil
|
||||
end
|
||||
|
||||
it "should reset modified environment vars" do
|
||||
ENV["FOO"] = "bar"
|
||||
preserving_env { ENV["FOO"] = "baz"}
|
||||
ENV["FOO"].should == "bar"
|
||||
end
|
||||
end
|
||||
end
|
||||
47
spec/resources/export/bluepill/app-concurrency.pill
Normal file
47
spec/resources/export/bluepill/app-concurrency.pill
Normal file
@@ -0,0 +1,47 @@
|
||||
Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepill.log") do |app|
|
||||
|
||||
app.uid = "app"
|
||||
app.gid = "app"
|
||||
|
||||
|
||||
|
||||
|
||||
app.process("alpha-1") do |process|
|
||||
process.start_command = "./alpha"
|
||||
|
||||
process.working_dir = "/tmp/app"
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "5000"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
children.stop_command "kill -QUIT {{PID}}"
|
||||
end
|
||||
|
||||
process.group = "app-alpha"
|
||||
end
|
||||
|
||||
|
||||
app.process("alpha-2") do |process|
|
||||
process.start_command = "./alpha"
|
||||
|
||||
process.working_dir = "/tmp/app"
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "5001"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-alpha-2.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
children.stop_command "kill -QUIT {{PID}}"
|
||||
end
|
||||
|
||||
process.group = "app-alpha"
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
44
spec/resources/export/bluepill/app.pill
Normal file
44
spec/resources/export/bluepill/app.pill
Normal file
@@ -0,0 +1,44 @@
|
||||
Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepill.log") do |app|
|
||||
|
||||
app.uid = "app"
|
||||
app.gid = "app"
|
||||
|
||||
|
||||
|
||||
|
||||
app.process("alpha-1") do |process|
|
||||
process.start_command = "./alpha"
|
||||
|
||||
process.working_dir = "/tmp/app"
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "5000"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
children.stop_command "kill -QUIT {{PID}}"
|
||||
end
|
||||
|
||||
process.group = "app-alpha"
|
||||
end
|
||||
|
||||
app.process("bravo-1") do |process|
|
||||
process.start_command = "./bravo"
|
||||
|
||||
process.working_dir = "/tmp/app"
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "5100"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-bravo-1.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
children.stop_command "kill -QUIT {{PID}}"
|
||||
end
|
||||
|
||||
process.group = "app-bravo"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
4
spec/resources/export/inittab/inittab.concurrency
Normal file
4
spec/resources/export/inittab/inittab.concurrency
Normal 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 -----
|
||||
4
spec/resources/export/inittab/inittab.default
Normal file
4
spec/resources/export/inittab/inittab.default
Normal 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 -----
|
||||
7
spec/resources/export/runit/app-alpha-1-log-run
Normal file
7
spec/resources/export/runit/app-alpha-1-log-run
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
LOG=/var/log/app/alpha-1
|
||||
|
||||
test -d "$LOG" || mkdir -p m2750 "$LOG" && chown app "$LOG"
|
||||
exec chpst -u app svlogd "$LOG"
|
||||
3
spec/resources/export/runit/app-alpha-1-run
Normal file
3
spec/resources/export/runit/app-alpha-1-run
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
cd /tmp/app
|
||||
exec chpst -u app -e /tmp/init/app-alpha-1/env ./alpha bar=baz
|
||||
7
spec/resources/export/runit/app-alpha-2-log-run
Normal file
7
spec/resources/export/runit/app-alpha-2-log-run
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
LOG=/var/log/app/alpha-2
|
||||
|
||||
test -d "$LOG" || mkdir -p m2750 "$LOG" && chown app "$LOG"
|
||||
exec chpst -u app svlogd "$LOG"
|
||||
3
spec/resources/export/runit/app-alpha-2-run
Normal file
3
spec/resources/export/runit/app-alpha-2-run
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
cd /tmp/app
|
||||
exec chpst -u app -e /tmp/init/app-alpha-2/env ./alpha bar=baz
|
||||
7
spec/resources/export/runit/app-bravo-1-log-run
Normal file
7
spec/resources/export/runit/app-bravo-1-log-run
Normal file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
LOG=/var/log/app/bravo-1
|
||||
|
||||
test -d "$LOG" || mkdir -p m2750 "$LOG" && chown app "$LOG"
|
||||
exec chpst -u app svlogd "$LOG"
|
||||
3
spec/resources/export/runit/app-bravo-1-run
Normal file
3
spec/resources/export/runit/app-bravo-1-run
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
cd /tmp/app
|
||||
exec chpst -u app -e /tmp/init/app-bravo-1/env ./bravo
|
||||
@@ -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}")
|
||||
@@ -12,6 +22,10 @@ def mock_error(subject, message)
|
||||
end
|
||||
end
|
||||
|
||||
def foreman(args)
|
||||
Foreman::CLI.start(args.split(" "))
|
||||
end
|
||||
|
||||
def mock_exit(&block)
|
||||
block.should raise_error(SystemExit)
|
||||
end
|
||||
@@ -24,15 +38,23 @@ def write_foreman_config(app)
|
||||
end
|
||||
end
|
||||
|
||||
def write_procfile(procfile="Procfile")
|
||||
def write_procfile(procfile="Procfile", alpha_env="")
|
||||
File.open(procfile, "w") do |file|
|
||||
file.puts "alpha: ./alpha"
|
||||
file.puts "alpha: ./alpha" + " #{alpha_env}".rstrip
|
||||
file.puts "\n"
|
||||
file.puts "bravo: ./bravo"
|
||||
file.puts "bravo:\t./bravo"
|
||||
end
|
||||
File.expand_path(procfile)
|
||||
end
|
||||
|
||||
def write_env(env=".env", options={"FOO"=>"bar"})
|
||||
File.open(env, "w") do |file|
|
||||
options.each do |key, val|
|
||||
file.puts "#{key}=#{val}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def load_export_templates_into_fakefs(type)
|
||||
FakeFS.deactivate!
|
||||
files = Dir[File.expand_path("../../data/export/#{type}/**", __FILE__)].inject({}) do |hash, file|
|
||||
@@ -53,8 +75,24 @@ def example_export_file(filename)
|
||||
data
|
||||
end
|
||||
|
||||
def preserving_env
|
||||
old_env = ENV.to_hash
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
ENV.clear
|
||||
ENV.update(old_env)
|
||||
end
|
||||
end
|
||||
|
||||
def normalize_space(s)
|
||||
s.gsub(/\n[\n\s]*/, "\n")
|
||||
end
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.treat_symbols_as_metadata_keys_with_true_values = true
|
||||
config.color_enabled = true
|
||||
config.include FakeFS::SpecHelpers
|
||||
config.order = 'rand'
|
||||
config.include FakeFS::SpecHelpers, :fakefs
|
||||
config.mock_with :rr
|
||||
end
|
||||
|
||||
113
tasks/dist.rake
Normal file
113
tasks/dist.rake
Normal file
@@ -0,0 +1,113 @@
|
||||
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
|
||||
81
tasks/release.rake
Normal file
81
tasks/release.rake
Normal file
@@ -0,0 +1,81 @@
|
||||
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.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
|
||||
|
||||
desc "Generate an authors list"
|
||||
task :authors do
|
||||
authors = %x{ git log --pretty=format:"%an" | sort -u }.split("\n")
|
||||
puts authors.join(", ")
|
||||
end
|
||||
|
||||
def latest_release
|
||||
latest = File.read("Changelog.md").split("\n").first.split(" ").last
|
||||
end
|
||||
|
||||
def newer_release
|
||||
release = %x{ git tag --contains #{latest_release} }.split("\n")[1]
|
||||
end
|
||||
|
||||
desc "Generate a Changelog"
|
||||
task :changelog do
|
||||
while release = newer_release
|
||||
entry = %x{ git show --format="%h %cd" #{release} | head -n 1 }
|
||||
commit, date_raw = entry.split(" ", 2)
|
||||
date = Time.parse(date_raw).strftime("%Y-%m-%d")
|
||||
|
||||
message = "## #{release[1..-1]} #{date} #{commit}\n"
|
||||
message += %x{ git log --format="%s [%an]" #{latest_release}..#{release} }
|
||||
|
||||
changelog = File.read("Changelog.md")
|
||||
changelog = message + "\n" + changelog
|
||||
|
||||
File.open("Changelog.md", "w") do |file|
|
||||
file.puts changelog
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# date =
|
||||
# message = "## #{release[1..-1]}
|
||||
# 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
|
||||
end
|
||||
8
tasks/rspec.rake
Normal file
8
tasks/rspec.rake
Normal 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
|
||||
Reference in New Issue
Block a user