Compare commits
129 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 | ||
|
|
8bc8cb4b2e | ||
|
|
33aa1efc90 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,4 +5,4 @@
|
||||
/man/*.markdown
|
||||
/pkg
|
||||
/tags
|
||||
|
||||
/vendor
|
||||
|
||||
@@ -11,4 +11,4 @@ notifications:
|
||||
on_success: always
|
||||
on_failure: always
|
||||
urls:
|
||||
- https://dx-helper.herokuapp.com/travis
|
||||
- http://dx-helper.herokuapp.com/travis
|
||||
|
||||
11
Changelog
11
Changelog
@@ -1,11 +0,0 @@
|
||||
0.26.1 12/05/2011 6160246da0fafe9cf8fde188d94bbc6babc667dc
|
||||
==========================================================
|
||||
|
||||
Merge pull request #103 from csquared/load_env_from_irb [David Dollar]
|
||||
refactor load_env to apply_environment [Chris Continanza]
|
||||
rename load! to load_env! [Chris Continanza]
|
||||
use ./.env as default [Chris Continanza]
|
||||
load contents from env file [Chris Continanza]
|
||||
refactor engine to expose env methods [Chris Continanza]
|
||||
disable email notifications [David Dollar]
|
||||
add travis config [David Dollar]
|
||||
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]
|
||||
13
Gemfile
13
Gemfile
@@ -2,14 +2,23 @@ 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 'rcov', '~> 0.9.8'
|
||||
gem 'rr', '~> 1.0.2'
|
||||
gem 'rspec', '~> 2.6.0'
|
||||
gem 'rspec', '~> 2.0'
|
||||
gem 'ZenTest'
|
||||
gem 'aws-s3'
|
||||
gem "rubyzip"
|
||||
gem "simplecov", :require => false
|
||||
end
|
||||
|
||||
41
Gemfile.lock
41
Gemfile.lock
@@ -1,30 +1,33 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
foreman (0.30.1)
|
||||
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)
|
||||
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.9.2)
|
||||
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)
|
||||
@@ -33,30 +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)
|
||||
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
|
||||
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,49 +0,0 @@
|
||||
# Foreman
|
||||
|
||||
## Installation
|
||||
|
||||
* Rubygems
|
||||
|
||||
gem install foreman
|
||||
|
||||
* OSX
|
||||
|
||||
http://assets.foreman.io/foreman/foreman.pkg
|
||||
|
||||
* Standalone Tarball
|
||||
|
||||
http://assets.foreman.io/foreman/foreman.tgz
|
||||
|
||||
## 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
|
||||
* Dan Peterson
|
||||
* Hunter Nield
|
||||
* Jay Zeschin
|
||||
* Keith Rarick
|
||||
* Khaja Minhajuddin
|
||||
* Matt Haynes
|
||||
* Michael van Rooijen
|
||||
* Mike Javorski
|
||||
* Nathan L Smith
|
||||
* Nick Zadrozny
|
||||
* Ricardo Chimal, Jr
|
||||
* Thom May
|
||||
* clifff
|
||||
* Greg Reinacker
|
||||
|
||||
## 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
|
||||
185
Rakefile
185
Rakefile
@@ -1,187 +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
|
||||
}
|
||||
end
|
||||
|
||||
## dist
|
||||
|
||||
require "erb"
|
||||
require "fileutils"
|
||||
require "tmpdir"
|
||||
|
||||
def assemble(source, target, perms=0644)
|
||||
FileUtils.mkdir_p(File.dirname(target))
|
||||
File.open(target, "w") do |f|
|
||||
f.puts ERB.new(File.read(source)).result(binding)
|
||||
end
|
||||
File.chmod(perms, target)
|
||||
end
|
||||
|
||||
def assemble_distribution(target_dir=Dir.pwd)
|
||||
distribution_files.each do |source|
|
||||
target = source.gsub(/^#{project_root}/, target_dir)
|
||||
FileUtils.mkdir_p(File.dirname(target))
|
||||
FileUtils.cp(source, target)
|
||||
end
|
||||
end
|
||||
|
||||
GEM_BLACKLIST = %w( bundler foreman )
|
||||
|
||||
def assemble_gems(target_dir=Dir.pwd)
|
||||
lines = %x{ bundle show }.strip.split("\n")
|
||||
raise "error running bundler" unless $?.success?
|
||||
|
||||
%x{ env BUNDLE_WITHOUT="development:test" bundle show }.split("\n").each do |line|
|
||||
if line =~ /^ \* (.*?) \((.*?)\)/
|
||||
next if GEM_BLACKLIST.include?($1)
|
||||
puts "vendoring: #{$1}-#{$2}"
|
||||
gem_dir = %x{ bundle show #{$1} }.strip
|
||||
FileUtils.mkdir_p "#{target_dir}/vendor/gems"
|
||||
%x{ cp -R "#{gem_dir}" "#{target_dir}/vendor/gems" }
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
|
||||
def beta?
|
||||
Foreman::VERSION.to_s =~ /pre/
|
||||
end
|
||||
|
||||
def clean(file)
|
||||
rm file if File.exists?(file)
|
||||
end
|
||||
|
||||
def distribution_files
|
||||
require "foreman/distribution"
|
||||
Foreman::Distribution.files
|
||||
end
|
||||
|
||||
def mkchdir(dir)
|
||||
FileUtils.mkdir_p(dir)
|
||||
Dir.chdir(dir) do |dir|
|
||||
yield(File.expand_path(dir))
|
||||
end
|
||||
end
|
||||
|
||||
def pkg(filename)
|
||||
File.expand_path("../pkg/#{filename}", __FILE__)
|
||||
end
|
||||
|
||||
def project_root
|
||||
File.dirname(__FILE__)
|
||||
end
|
||||
|
||||
def resource(name)
|
||||
File.expand_path("../dist/resources/#{name}", __FILE__)
|
||||
end
|
||||
|
||||
def s3_connect
|
||||
return if @s3_connected
|
||||
|
||||
require "aws/s3"
|
||||
|
||||
unless ENV["DAVID_RELEASE_ACCESS"] && ENV["DAVID_RELEASE_SECRET"]
|
||||
puts "please set DAVID_RELEASE_ACCESS and DAVID_RELEASE_SECRET in your environment"
|
||||
exit 1
|
||||
end
|
||||
|
||||
AWS::S3::Base.establish_connection!(
|
||||
:access_key_id => ENV["DAVID_RELEASE_ACCESS"],
|
||||
:secret_access_key => ENV["DAVID_RELEASE_SECRET"]
|
||||
)
|
||||
|
||||
@s3_connected = true
|
||||
end
|
||||
|
||||
def store(package_file, filename, bucket="assets.foreman.io")
|
||||
s3_connect
|
||||
puts "storing: #{filename}"
|
||||
AWS::S3::S3Object.store(filename, File.open(package_file), bucket, :access => :public_read)
|
||||
end
|
||||
|
||||
def tempdir
|
||||
Dir.mktmpdir do |dir|
|
||||
Dir.chdir(dir) do
|
||||
yield(dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def version
|
||||
require "foreman/version"
|
||||
Foreman::VERSION
|
||||
end
|
||||
|
||||
Dir[File.expand_path("../dist/**/*.rake", __FILE__)].each do |rake|
|
||||
import rake
|
||||
end
|
||||
|
||||
task :changelog do
|
||||
timestamp = Time.now.utc.strftime('%m/%d/%Y')
|
||||
sha = `git log | head -1`.split(' ').last
|
||||
changelog = ["#{version} #{timestamp} #{sha}"]
|
||||
changelog << ('=' * changelog[0].length)
|
||||
changelog << ''
|
||||
|
||||
last_sha = `cat Changelog | head -1`.split(' ').last
|
||||
shortlog = `git log #{last_sha}..HEAD --pretty=format:'%s [%an]'`
|
||||
changelog << shortlog.split("\n")
|
||||
changelog.concat ['', '', '']
|
||||
|
||||
old_changelog = File.read('Changelog')
|
||||
File.open('Changelog', 'w') do |file|
|
||||
file.write(changelog.join("\n"))
|
||||
file.write(old_changelog)
|
||||
end
|
||||
Dir[File.expand_path("../tasks/*.rake", __FILE__)].each do |task|
|
||||
load task
|
||||
end
|
||||
|
||||
36
bin/runner
36
bin/runner
@@ -1,2 +1,36 @@
|
||||
#!/bin/sh
|
||||
exec $1 2>&1
|
||||
#
|
||||
#/ 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
|
||||
|
||||
@@ -5,7 +5,7 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
||||
|
||||
<% engine.procfile.entries.each do |process| %>
|
||||
<% 1.upto(concurrency[process.name]) do |num| %>
|
||||
<% port = engine.port_for(process, num, options[:port]) %>
|
||||
<% 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) %>"
|
||||
|
||||
|
||||
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/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
|
||||
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
|
||||
@@ -16,6 +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'
|
||||
|
||||
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
|
||||
|
||||
@@ -14,5 +14,12 @@ module Foreman
|
||||
File.expand_path("../../bin/runner", __FILE__)
|
||||
end
|
||||
|
||||
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,19 +1,32 @@
|
||||
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", "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"'
|
||||
|
||||
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
|
||||
@@ -27,22 +40,12 @@ class Foreman::CLI < Thor
|
||||
method_option :port, :type => :numeric, :aliases => "-p"
|
||||
method_option :user, :type => :string, :aliases => "-u"
|
||||
method_option :template, :type => :string, :aliases => "-t"
|
||||
method_option :concurrency, :type => :string, :aliases => "-c",
|
||||
:banner => '"alpha=5,bar=3"'
|
||||
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
|
||||
|
||||
def export(format, location=nil)
|
||||
check_procfile!
|
||||
|
||||
formatter = case format
|
||||
when "inittab" then Foreman::Export::Inittab
|
||||
when "upstart" then Foreman::Export::Upstart
|
||||
when "bluepill" then Foreman::Export::Bluepill
|
||||
when "runit" then Foreman::Export::Runit
|
||||
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
|
||||
@@ -51,7 +54,20 @@ class Foreman::CLI < Thor
|
||||
|
||||
def check
|
||||
error "no processes defined" unless engine.procfile.entries.length > 0
|
||||
display "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
|
||||
puts "valid procfile detected (#{engine.procfile.process_names.join(', ')})"
|
||||
end
|
||||
|
||||
desc "run COMMAND", "Run a command using your application's environment"
|
||||
|
||||
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 ######################################################################
|
||||
@@ -68,24 +84,15 @@ private ######################################################################
|
||||
options[:procfile] || "Procfile"
|
||||
end
|
||||
|
||||
def display(message)
|
||||
puts message
|
||||
end
|
||||
|
||||
def error(message)
|
||||
puts "ERROR: #{message}"
|
||||
exit 1
|
||||
end
|
||||
|
||||
def procfile_exists?(procfile)
|
||||
File.exist?(procfile)
|
||||
end
|
||||
|
||||
def options
|
||||
original_options = super
|
||||
return original_options unless File.exists?(".foreman")
|
||||
defaults = YAML::load_file(".foreman") || {}
|
||||
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -2,7 +2,6 @@ require "foreman"
|
||||
require "foreman/process"
|
||||
require "foreman/procfile"
|
||||
require "foreman/utils"
|
||||
require "pty"
|
||||
require "tempfile"
|
||||
require "timeout"
|
||||
require "term/ansicolor"
|
||||
@@ -17,11 +16,13 @@ class Foreman::Engine
|
||||
|
||||
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, options={})
|
||||
@procfile = Foreman::Procfile.new(procfile)
|
||||
@directory = File.expand_path(File.dirname(procfile))
|
||||
@directory = options[:app_root] || File.expand_path(File.dirname(procfile))
|
||||
@options = options
|
||||
@environment = read_environment_files(options[:env])
|
||||
@output_mutex = Mutex.new
|
||||
@@ -58,7 +59,7 @@ private ######################################################################
|
||||
|
||||
procfile.entries.each do |entry|
|
||||
reader, writer = IO.pipe
|
||||
entry.spawn(concurrency[entry.name], writer, @directory, @environment, base_port).each do |process|
|
||||
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
|
||||
@@ -71,14 +72,23 @@ private ######################################################################
|
||||
|
||||
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 terminate_gracefully
|
||||
return if @terminating
|
||||
@terminating = true
|
||||
info "sending SIGTERM to all processes"
|
||||
kill_all "SIGTERM"
|
||||
Timeout.timeout(5) { Process.waitall }
|
||||
Timeout.timeout(5) do
|
||||
while running_processes.length > 0
|
||||
pid, status = Process.wait2
|
||||
process = running_processes.delete(pid)
|
||||
info "process terminated", process.name
|
||||
end
|
||||
end
|
||||
rescue Timeout::Error
|
||||
info "sending SIGKILL to all processes"
|
||||
kill_all "SIGKILL"
|
||||
@@ -86,11 +96,14 @@ private ######################################################################
|
||||
|
||||
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|
|
||||
ps, message = r.gets.split(",", 2)
|
||||
data = r.gets
|
||||
next unless data
|
||||
ps, message = data.split(",", 2)
|
||||
color = colors[ps.split(".").first]
|
||||
info message, ps, color
|
||||
end
|
||||
@@ -107,16 +120,16 @@ private ######################################################################
|
||||
process = running_processes.delete(pid)
|
||||
info "process terminated", process.name
|
||||
terminate_gracefully
|
||||
kill_all
|
||||
rescue Errno::ECHILD
|
||||
end
|
||||
|
||||
def info(message, name="system", color=Term::ANSIColor.white)
|
||||
print color
|
||||
print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
|
||||
print Term::ANSIColor.reset
|
||||
print message.chomp
|
||||
puts ""
|
||||
output = ""
|
||||
output += color
|
||||
output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
|
||||
output += Term::ANSIColor.reset
|
||||
output += message.chomp
|
||||
puts output
|
||||
end
|
||||
|
||||
def print(message=nil)
|
||||
@@ -131,11 +144,6 @@ private ######################################################################
|
||||
end
|
||||
end
|
||||
|
||||
def error(message)
|
||||
puts "ERROR: #{message}"
|
||||
exit 1
|
||||
end
|
||||
|
||||
def longest_process_name
|
||||
@longest_process_name ||= begin
|
||||
longest = procfile.process_names.map { |name| name.length }.sort.last
|
||||
@@ -153,7 +161,7 @@ private ######################################################################
|
||||
end
|
||||
|
||||
def termtitle(title)
|
||||
printf("\033]0;#{title}\007")
|
||||
printf("\033]0;#{title}\007") unless Foreman.windows?
|
||||
end
|
||||
|
||||
def running_processes
|
||||
@@ -181,7 +189,8 @@ private ######################################################################
|
||||
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
|
||||
|
||||
module Env
|
||||
@@ -213,6 +222,11 @@ private ######################################################################
|
||||
def apply_environment!
|
||||
@environment.each { |k,v| ENV[k] = v }
|
||||
end
|
||||
|
||||
def error(message)
|
||||
puts "ERROR: #{message}"
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
include Env
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
require "foreman"
|
||||
require "foreman/helpers"
|
||||
|
||||
module Foreman::Export
|
||||
extend Foreman::Helpers
|
||||
|
||||
class Exception < ::Exception; end
|
||||
|
||||
def self.formatter(format)
|
||||
begin
|
||||
require "foreman/export/#{ format.tr('-', '_') }"
|
||||
classy_format = classify(format)
|
||||
formatter = constantize("Foreman::Export::#{ classy_format }")
|
||||
rescue NameError => ex
|
||||
error "Unknown export format: #{format} (no class Foreman::Export::#{ classy_format })."
|
||||
rescue LoadError => ex
|
||||
error "Unknown export format: #{format} (unable to load file 'foreman/export/#{ format.tr('-', '_') }')."
|
||||
end
|
||||
end
|
||||
|
||||
def self.error(message)
|
||||
raise Foreman::Export::Exception.new(message)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
require "foreman/export/base"
|
||||
require "foreman/export/inittab"
|
||||
require "foreman/export/upstart"
|
||||
|
||||
@@ -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__))
|
||||
|
||||
@@ -3,23 +3,21 @@ require "foreman/export"
|
||||
|
||||
class Foreman::Export::Bluepill < 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}.pill"].each do |file|
|
||||
say "cleaning up: #{file}"
|
||||
FileUtils.rm(file)
|
||||
end
|
||||
|
||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||
|
||||
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
|
||||
|
||||
@@ -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.procfile.entries.inject(1) do |index, process|
|
||||
1.upto(concurrency[process.name]) do |num|
|
||||
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
|
||||
|
||||
|
||||
@@ -4,21 +4,19 @@ require "foreman/export"
|
||||
class Foreman::Export::Runit < Foreman::Export::Base
|
||||
ENV_VARIABLE_REGEX = /([a-zA-Z_]+[a-zA-Z0-9_]*)=(\S+)/
|
||||
|
||||
def export(location, options={})
|
||||
def export
|
||||
error("Must specify a location") unless location
|
||||
|
||||
app = options[:app] || File.basename(engine.directory)
|
||||
user = options[:user] || app
|
||||
log_root = options[:log] || "/var/log/#{app}"
|
||||
template_root = options[:template]
|
||||
|
||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||
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(concurrency[process.name]) do |num|
|
||||
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"
|
||||
@@ -29,8 +27,9 @@ class Foreman::Export::Runit < Foreman::Export::Base
|
||||
|
||||
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, options[:port])
|
||||
port = engine.port_for(process, num, self.port)
|
||||
environment_variables = {'PORT' => port}.
|
||||
merge(engine.environment).
|
||||
merge(inline_variables(process.command))
|
||||
@@ -41,7 +40,7 @@ class Foreman::Export::Runit < Foreman::Export::Base
|
||||
|
||||
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
|
||||
|
||||
@@ -50,7 +49,7 @@ class Foreman::Export::Runit < Foreman::Export::Base
|
||||
private
|
||||
def create_directory(location)
|
||||
say "creating: #{location}"
|
||||
FileUtils.mkdir(location)
|
||||
FileUtils.mkdir_p(location)
|
||||
end
|
||||
|
||||
def inline_variables(command)
|
||||
|
||||
@@ -3,23 +3,21 @@ 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
|
||||
@@ -27,13 +25,13 @@ class Foreman::Export::Upstart < Foreman::Export::Base
|
||||
process_template = export_template("upstart", "process.conf.erb", template_root)
|
||||
|
||||
engine.procfile.entries.each do |process|
|
||||
next if (conc = concurrency[process.name]) < 1
|
||||
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,4 +1,5 @@
|
||||
require "foreman"
|
||||
require "rubygems"
|
||||
|
||||
class Foreman::Process
|
||||
|
||||
@@ -14,10 +15,8 @@ class Foreman::Process
|
||||
end
|
||||
|
||||
def run(pipe, basedir, environment)
|
||||
Dir.chdir(basedir) do
|
||||
with_environment(environment.merge("PORT" => port.to_s)) do
|
||||
run_process entry.command, pipe
|
||||
end
|
||||
with_environment(environment.merge("PORT" => port.to_s)) do
|
||||
run_process basedir, entry.command, pipe
|
||||
end
|
||||
end
|
||||
|
||||
@@ -25,24 +24,52 @@ class Foreman::Process
|
||||
"%s.%s" % [ entry.name, num ]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fork_with_io(command)
|
||||
io = case RUBY_VERSION
|
||||
when /^1\.9\./
|
||||
IO.popen([Foreman.runner, replace_command_env(command)], "w+")
|
||||
when /^1\.8\./
|
||||
full_command = replace_command_env(command).gsub("'", "\\'")
|
||||
IO.popen("#{Foreman.runner} '#{full_command}'", "w+")
|
||||
else
|
||||
raise "Unknown Ruby version: #{RUBY_VERSION}"
|
||||
end
|
||||
[ io, io.pid ]
|
||||
def kill(signal)
|
||||
pid && Process.kill(signal, pid)
|
||||
rescue Errno::ESRCH
|
||||
false
|
||||
end
|
||||
|
||||
def run_process(command, pipe)
|
||||
io, @pid = fork_with_io(command)
|
||||
trap("SIGTERM") { "got sigterm for %d" % @pid }
|
||||
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?
|
||||
@@ -60,12 +87,10 @@ private
|
||||
end
|
||||
|
||||
def with_environment(environment)
|
||||
old_env = ENV.each_pair.inject({}) { |h,(k,v)| h.update(k => v) }
|
||||
environment.each { |k,v| ENV[k] = v }
|
||||
ret = yield
|
||||
ENV.clear
|
||||
old_env.each { |k,v| ENV[k] = v}
|
||||
ret
|
||||
original = ENV.to_hash
|
||||
ENV.update environment
|
||||
yield
|
||||
ensure
|
||||
ENV.replace original
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -5,7 +5,10 @@ class Foreman::Utils
|
||||
def self.parse_concurrency(concurrency)
|
||||
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.30.1"
|
||||
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" "November 2011" "Foreman 0.26.0" "Foreman Manual"
|
||||
.TH "FOREMAN" "1" "January 2012" "Foreman 0.33.1" "Foreman Manual"
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBforeman\fR \- manage Procfile\-based applications
|
||||
@@ -68,6 +68,10 @@ 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\. You can specify more than one file by using: \fB\-\-env file1,file2\fR\.
|
||||
.
|
||||
|
||||
@@ -66,6 +66,10 @@ 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. You can specify more than one
|
||||
file by using: `--env file1,file2`.
|
||||
|
||||
@@ -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
|
||||
@@ -27,10 +27,21 @@ describe "Foreman::CLI" do
|
||||
|
||||
describe "export" do
|
||||
describe "options" do
|
||||
it "uses .foreman" do
|
||||
write_procfile
|
||||
File.open(".foreman", "w") { |f| f.puts "concurrency: alpha=2" }
|
||||
mock_export = mock(Foreman::Export::Upstart)
|
||||
mock(Foreman::Export::Upstart).new("/upstart", is_a(Foreman::Engine), { "concurrency" => "alpha=2" }) { mock_export }
|
||||
mock_export.export
|
||||
foreman %{ export upstart /upstart }
|
||||
end
|
||||
|
||||
it "respects --env" do
|
||||
write_procfile
|
||||
write_env("envfile")
|
||||
mock.instance_of(Foreman::Export::Upstart).export("/upstart", { "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
|
||||
@@ -47,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
|
||||
@@ -60,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
|
||||
@@ -72,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
|
||||
@@ -90,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,7 +1,7 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
|
||||
describe "Foreman::Engine" do
|
||||
describe "Foreman::Engine", :fakefs do
|
||||
subject { Foreman::Engine.new("Procfile", {}) }
|
||||
|
||||
describe "initialize" do
|
||||
@@ -24,8 +24,8 @@ describe "Foreman::Engine" do
|
||||
describe "start" do
|
||||
it "forks the processes" do
|
||||
write_procfile
|
||||
mock.instance_of(Foreman::Process).run_process("./alpha", is_a(IO))
|
||||
mock.instance_of(Foreman::Process).run_process("./bravo", is_a(IO))
|
||||
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
|
||||
@@ -34,8 +34,8 @@ describe "Foreman::Engine" do
|
||||
it "handles concurrency" do
|
||||
write_procfile
|
||||
engine = Foreman::Engine.new("Procfile",:concurrency => "alpha=2")
|
||||
mock.instance_of(Foreman::Process).run_process("./alpha", is_a(IO)).twice
|
||||
mock.instance_of(Foreman::Process).run_process("./bravo", is_a(IO))
|
||||
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
|
||||
|
||||
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
|
||||
@@ -3,17 +3,34 @@ require "foreman/engine"
|
||||
require "foreman/export/bluepill"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Bluepill do
|
||||
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(:bluepill) { Foreman::Export::Bluepill.new(engine) }
|
||||
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("/tmp/init", :concurrency => "alpha=2")
|
||||
File.read("/tmp/init/app.pill").should == example_export_file("bluepill/app.pill")
|
||||
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
|
||||
@@ -3,33 +3,39 @@ require "foreman/engine"
|
||||
require "foreman/export/runit"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Runit do
|
||||
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(engine) }
|
||||
|
||||
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('/tmp/init', :concurrency => 'alpha=2')
|
||||
|
||||
|
||||
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 ==
|
||||
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 ==
|
||||
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 ==
|
||||
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
|
||||
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", :concurrency => "alpha=2")
|
||||
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,13 +8,8 @@ describe Foreman do
|
||||
it { should be_a String }
|
||||
end
|
||||
|
||||
describe "::load_env!(env_file)" do
|
||||
before do
|
||||
FakeFS.activate!
|
||||
end
|
||||
|
||||
describe "::load_env!(env_file)", :fakefs do
|
||||
after do
|
||||
FakeFS.deactivate!
|
||||
ENV['FOO'] = nil
|
||||
end
|
||||
|
||||
@@ -22,7 +17,7 @@ describe Foreman do
|
||||
File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
|
||||
Foreman.load_env!("/tmp/env1")
|
||||
ENV['FOO'].should == 'bar'
|
||||
end
|
||||
end
|
||||
|
||||
it "should assume env_file in ./.env" do
|
||||
File.open("./.env", "w") { |f| f.puts("FOO=bar") }
|
||||
@@ -30,4 +25,10 @@ describe Foreman do
|
||||
ENV['FOO'].should == 'bar'
|
||||
end
|
||||
end
|
||||
|
||||
describe "runner" do
|
||||
it "should exist" do
|
||||
File.exists?(Foreman.runner).should == true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
@@ -23,27 +23,6 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
app.process("bravo-1") do |process|
|
||||
process.start_command = "./bravo"
|
||||
|
||||
|
||||
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 -----
|
||||
@@ -1,10 +1,20 @@
|
||||
require "rubygems"
|
||||
|
||||
require "simplecov"
|
||||
SimpleCov.start do
|
||||
add_filter "/spec/"
|
||||
end
|
||||
|
||||
require "rspec"
|
||||
require "fakefs/safe"
|
||||
require "fakefs/spec_helpers"
|
||||
|
||||
$:.unshift File.expand_path("../../lib", __FILE__)
|
||||
|
||||
def mock_export_error(message)
|
||||
lambda { yield }.should raise_error(Foreman::Export::Exception, message)
|
||||
end
|
||||
|
||||
def mock_error(subject, message)
|
||||
mock_exit do
|
||||
mock(subject).puts("ERROR: #{message}")
|
||||
@@ -37,9 +47,11 @@ def write_procfile(procfile="Procfile", alpha_env="")
|
||||
File.expand_path(procfile)
|
||||
end
|
||||
|
||||
def write_env(env=".env")
|
||||
def write_env(env=".env", options={"FOO"=>"bar"})
|
||||
File.open(env, "w") do |file|
|
||||
file.puts "FOO=bar"
|
||||
options.each do |key, val|
|
||||
file.puts "#{key}=#{val}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -63,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