Compare commits
475 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
26bb0ed54e | ||
|
|
44002953f6 | ||
|
|
bb2c3a2d04 | ||
|
|
88cdaacc67 | ||
|
|
faf7b3c40f | ||
|
|
21dd610eaf | ||
|
|
fe65c7510d | ||
|
|
cd2c255296 | ||
|
|
92c1909217 | ||
|
|
af57bf3d52 | ||
|
|
443994d3b5 | ||
|
|
2faa3fb6ff | ||
|
|
0d53f6bd6c | ||
|
|
1d2bcdbc56 | ||
|
|
aceea1472a | ||
|
|
44726e377e | ||
|
|
61eca5a1d8 | ||
|
|
553ac7f81f | ||
|
|
6790cf02a9 | ||
|
|
7ad41da592 | ||
|
|
8ee7b7afdf | ||
|
|
2620b90808 | ||
|
|
89bdc3ab8e | ||
|
|
60a11eb981 | ||
|
|
61c222deb8 | ||
|
|
8fe86e98c8 | ||
|
|
5c1ffdb7dc | ||
|
|
8b49256175 | ||
|
|
37d777f224 | ||
|
|
73fc059634 | ||
|
|
f7b7029cc0 | ||
|
|
3a101ec7dd | ||
|
|
6c931ea15e | ||
|
|
d173570d98 | ||
|
|
0cd41fee7f | ||
|
|
9da4e38209 | ||
|
|
5d9dfd294e | ||
|
|
8998e9a47c | ||
|
|
8238a86942 | ||
|
|
1153fb0f0c | ||
|
|
f69c755d9a | ||
|
|
64f0749d16 | ||
|
|
6b77ca1e46 | ||
|
|
8fbee31a2d | ||
|
|
7b4eabf0c5 | ||
|
|
6a44dd3fd3 | ||
|
|
e99f3173ef | ||
|
|
6c04dab649 | ||
|
|
96e26e7412 | ||
|
|
a2c03cc402 | ||
|
|
803115c0c4 | ||
|
|
a4343187ad | ||
|
|
98af1f0943 | ||
|
|
af2d4762a8 | ||
|
|
1d06124457 | ||
|
|
698e6ae092 | ||
|
|
c617ddb3b2 | ||
|
|
f6d4badcd2 | ||
|
|
f29bf49a35 | ||
|
|
e06d36b27c | ||
|
|
85e62dfbeb | ||
|
|
574e852710 | ||
|
|
68f098c1d2 | ||
|
|
e8bdafdfc1 | ||
|
|
4abd3ebedb | ||
|
|
f765436dde | ||
|
|
b5c513b4b5 | ||
|
|
ee761ff098 | ||
|
|
27c22deb6c | ||
|
|
681a9f7e61 | ||
|
|
8335a2b1ba | ||
|
|
407425ca78 | ||
|
|
6ca505b4cd | ||
|
|
612eae5e21 | ||
|
|
497b5ea1eb | ||
|
|
cd384e0d59 | ||
|
|
8921cac35b | ||
|
|
7d6de5b2a7 | ||
|
|
cc4306492e | ||
|
|
6042783e82 | ||
|
|
b7b3a9f898 | ||
|
|
8b204db2f0 | ||
|
|
15643dcb3f | ||
|
|
5f0f2f5378 | ||
|
|
c1b57b59cf | ||
|
|
21d53818f2 | ||
|
|
359d6f1c34 | ||
|
|
584f251e4a | ||
|
|
7d9c2b2ac4 | ||
|
|
fba4d9beff | ||
|
|
0bde5fdab5 | ||
|
|
ebe160d425 | ||
|
|
1beab80c1f | ||
|
|
7b270f9f4a | ||
|
|
03e5342067 | ||
|
|
b1d57426fb | ||
|
|
06e5c52f35 | ||
|
|
64ca839c0b | ||
|
|
80aebda023 | ||
|
|
5dee2281a2 | ||
|
|
efb6d2f11d | ||
|
|
ac528d3b50 | ||
|
|
cefd4e351e | ||
|
|
9849f4558a | ||
|
|
4b53b42be1 | ||
|
|
219acaf690 | ||
|
|
f8118d7b40 | ||
|
|
7fdade277c | ||
|
|
1a0943c495 | ||
|
|
4a732abd77 | ||
|
|
3e71fea777 | ||
|
|
ae7aeabb63 | ||
|
|
0a61b1a62f | ||
|
|
9901b9f924 | ||
|
|
4c2d569810 | ||
|
|
26f9c8186d | ||
|
|
24ed8946f3 | ||
|
|
83b2a9cc50 | ||
|
|
a0228b9fa0 | ||
|
|
b1a2a4a0cd | ||
|
|
7774b7f150 | ||
|
|
b01355a093 | ||
|
|
8b18143281 | ||
|
|
e06886ed57 | ||
|
|
d6a00d7262 | ||
|
|
d9d1346640 | ||
|
|
0df1a4d784 | ||
|
|
51a704939e | ||
|
|
f41cc552c7 | ||
|
|
065bbf1cd8 | ||
|
|
b628ddc608 | ||
|
|
f745b16217 | ||
|
|
a87a882e60 | ||
|
|
6ba9252d0f | ||
|
|
efd9e2119b | ||
|
|
bf9bdbf118 | ||
|
|
929a138e54 | ||
|
|
d6514b4f33 | ||
|
|
8696a36833 | ||
|
|
3ea5de42aa | ||
|
|
4a13122082 | ||
|
|
9b2987c3f0 | ||
|
|
6274f99225 | ||
|
|
0b0324fed9 | ||
|
|
32db70b778 | ||
|
|
3db1ad6fbc | ||
|
|
003b466a17 | ||
|
|
2a896a0fb5 | ||
|
|
6a8c81a38b | ||
|
|
a83dab363e | ||
|
|
53e0f4ecf9 | ||
|
|
e0fe5baf1b | ||
|
|
1aa1f15b8f | ||
|
|
fa46a605bb | ||
|
|
3077857ab7 | ||
|
|
08aa8f9d5d | ||
|
|
771489dec9 | ||
|
|
053ae8f0be | ||
|
|
af58af1c60 | ||
|
|
9075d93370 | ||
|
|
0c2e9df722 | ||
|
|
047f1ff5c4 | ||
|
|
26b54a62c5 | ||
|
|
7b85ad7c1a | ||
|
|
91a87049db | ||
|
|
f46408e8be | ||
|
|
93f04e42ac | ||
|
|
97c4582acc | ||
|
|
5689d75a87 | ||
|
|
1325b6750e | ||
|
|
638005403f | ||
|
|
55f274532f | ||
|
|
66f76c2036 | ||
|
|
865cabb525 | ||
|
|
7c3c4bc58f | ||
|
|
2dbe8c733b | ||
|
|
84352b82cc | ||
|
|
383c1f87af | ||
|
|
ce3003b026 | ||
|
|
e06f4b2f9e | ||
|
|
b721fd894e | ||
|
|
b8ea6fd4b3 | ||
|
|
dbda63263b | ||
|
|
93cdc31be0 | ||
|
|
4dfbe46690 | ||
|
|
0033f9caeb | ||
|
|
74839800a9 | ||
|
|
b75337e21e | ||
|
|
d94f941189 | ||
|
|
0b34f067cb | ||
|
|
dbe51832b0 | ||
|
|
3a2a53be95 | ||
|
|
b2bf95479e | ||
|
|
48f764e347 | ||
|
|
de62d0655e | ||
|
|
38aecff886 | ||
|
|
e4a3215257 | ||
|
|
c705b5fbef | ||
|
|
2fcb64959b | ||
|
|
69216b4c5e | ||
|
|
5d2930745a | ||
|
|
a5465bf55e | ||
|
|
8fc3d1ef24 | ||
|
|
d33e4fb0ed | ||
|
|
39b48b566f | ||
|
|
2f982ff9c7 | ||
|
|
1217ef1b56 | ||
|
|
c9943d70ec | ||
|
|
08dca57eb4 | ||
|
|
bb3377407a | ||
|
|
084b9493d1 | ||
|
|
279a251c78 | ||
|
|
a49ef286e8 | ||
|
|
0b3da59947 | ||
|
|
1f725dd68a | ||
|
|
2272f76479 | ||
|
|
cf4762071c | ||
|
|
d86b0bed1f | ||
|
|
12f825204b | ||
|
|
62c9d1db45 | ||
|
|
1127551369 | ||
|
|
8cb58b8517 | ||
|
|
d03e931b67 | ||
|
|
9ba2b32b36 | ||
|
|
cf5689a77a | ||
|
|
c23dbb79af | ||
|
|
7e55d8d3e2 | ||
|
|
d4f23d45a4 | ||
|
|
93fa1645e7 | ||
|
|
7bdada4a10 | ||
|
|
2b47d24ab7 | ||
|
|
24695348fb | ||
|
|
38b6482af5 | ||
|
|
501bc138c5 | ||
|
|
df8c05cd6c | ||
|
|
edcc4f3567 | ||
|
|
3759dbb463 | ||
|
|
b673931c05 | ||
|
|
137e43b040 | ||
|
|
79211d9bbf | ||
|
|
16d4b84a5d | ||
|
|
5ea4537046 | ||
|
|
d51433ff82 | ||
|
|
54ab74d305 | ||
|
|
e8b8f34f41 | ||
|
|
7535f1d3d8 | ||
|
|
3af0dfb4ae | ||
|
|
78547b8175 | ||
|
|
976fbc0bb0 | ||
|
|
28a9aa774f | ||
|
|
51f5ff3842 | ||
|
|
e1e18f62bb | ||
|
|
0a09117328 | ||
|
|
c3df12746f | ||
|
|
2ec6a23fb3 | ||
|
|
0d6b784de1 | ||
|
|
2dcd2c03db | ||
|
|
9d6d0bbb7d | ||
|
|
89c1314abe | ||
|
|
f33211d100 | ||
|
|
4317079bf3 | ||
|
|
c745c282c9 | ||
|
|
7dca45db57 | ||
|
|
de3c47fe21 | ||
|
|
a8a255db4a | ||
|
|
307d63b631 | ||
|
|
de54f6a5a5 | ||
|
|
0dff116340 | ||
|
|
e053dc8434 | ||
|
|
20f6ba1563 | ||
|
|
557a08ea77 | ||
|
|
8eccc819d6 | ||
|
|
347d4a0184 | ||
|
|
0147f5d284 | ||
|
|
1da034ce66 | ||
|
|
5fc7552572 | ||
|
|
083efe3ae9 | ||
|
|
9d2382a2d2 | ||
|
|
f731daae1a | ||
|
|
b7f0e3f573 | ||
|
|
e7c523dab7 | ||
|
|
9f4f15a29c | ||
|
|
b2dc89c50e | ||
|
|
389bf05800 | ||
|
|
a632a62efd | ||
|
|
2edf6e1c68 | ||
|
|
56158e881b | ||
|
|
6b5ed495d5 | ||
|
|
9652c285f1 | ||
|
|
ba171cc10d | ||
|
|
ebb191adba | ||
|
|
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 | ||
|
|
47008fb921 |
4
.gitignore
vendored
4
.gitignore
vendored
@@ -1,8 +1,10 @@
|
||||
/.bundle
|
||||
/.rbenv-version
|
||||
/.yardoc
|
||||
/coverage
|
||||
/example/log/*
|
||||
/man/*.html
|
||||
/man/*.markdown
|
||||
/pkg
|
||||
/tags
|
||||
|
||||
/vendor
|
||||
|
||||
21
.travis.yml
Normal file
21
.travis.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
script: bundle exec rake spec
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- rvm: 1.8.7
|
||||
- rvm: jruby
|
||||
- rvm: rbx
|
||||
- rvm: ree
|
||||
|
||||
notifications:
|
||||
email: false
|
||||
webhooks:
|
||||
on_success: always
|
||||
on_failure: always
|
||||
urls:
|
||||
- http://dx-helper.herokuapp.com/travis
|
||||
|
||||
rvm:
|
||||
- 1.9.2
|
||||
- 1.9.3
|
||||
- jruby
|
||||
312
Changelog.md
Normal file
312
Changelog.md
Normal file
@@ -0,0 +1,312 @@
|
||||
## 0.60.2 (2012-10-08)
|
||||
|
||||
* Fix for nil value on io select loop, fixes #260 [Silvio Relli]
|
||||
|
||||
## 0.60.1 (2012-10-08)
|
||||
|
||||
* sleep on select() to avoid spinning the cpu [Silvio Relli]
|
||||
|
||||
## 0.60.0 (2012-09-25)
|
||||
|
||||
* foreman run can run things from the Procfile like heroku run. [Dan Peterson]
|
||||
|
||||
## 0.59.0 (2012-09-15)
|
||||
|
||||
* Use /bin/sh instead of bash for foreman-runner [Jeremy Evans]
|
||||
|
||||
## 0.58.0 (2012-09-14)
|
||||
|
||||
* dont set HOME [David Dollar]
|
||||
* Add StandardOutPath to launchd export [Aaron Kalin]
|
||||
* Add command argument string splitting [Aaron Kalin]
|
||||
* Cleanup launchd exporter [Aaron Kalin]
|
||||
* Enable trim_mode via '-' in ERB templates [Aaron Kalin]
|
||||
* Add support for setting environment variables [Aaron Kalin]
|
||||
* foreman run should exit with the same code as its command [Omar Khan]
|
||||
* Handle multiline strings in .env file [Szymon Nowak]
|
||||
* Use path and env variables in the inittab export [Indrek Juhkam]
|
||||
* fixed the directory option [Arnaud Lachaume]
|
||||
* Add capistrano export support [Daniel Farrell]
|
||||
|
||||
## 0.57.0 (2012-08-21)
|
||||
|
||||
* fix startup checks for upstart exporter [Aditya Sanghi]
|
||||
|
||||
## 0.56.0 (2012-08-19)
|
||||
|
||||
* read .profile, not .profile.d [David Dollar]
|
||||
|
||||
## 0.55.0 (2012-08-14)
|
||||
|
||||
* use a forked process to exec a run with environment [David Dollar]
|
||||
|
||||
## 0.54.0 (2012-08-14)
|
||||
|
||||
* use Foreman::Process to extract command running [David Dollar]
|
||||
* changed to check env for bash [brntbeer]
|
||||
|
||||
## 0.53.0 (2012-07-24)
|
||||
|
||||
* put app root in $HOME [David Dollar]
|
||||
|
||||
## 0.52.0 (2012-07-24)
|
||||
|
||||
* wrap command in a runner that sources .profile.d scripts [David Dollar]
|
||||
* fix upstart export specs [David Dollar]
|
||||
* Make upstart export start/stop with network [Daniel Farrell]
|
||||
|
||||
## 0.51.0 (2012-07-11)
|
||||
|
||||
* dont try to colorize windows [David Dollar]
|
||||
|
||||
## 0.50.0 (2012-07-11)
|
||||
|
||||
* handle windows [David Dollar]
|
||||
|
||||
## 0.49.0 (2012-07-11)
|
||||
|
||||
* 1.8 compatibility [David Dollar]
|
||||
* use one pgroup for all of foreman and kill that since ruby 1.8 sucks at pgroups [David Dollar]
|
||||
* better debugging [David Dollar]
|
||||
|
||||
## 0.48.0 (2012-07-10)
|
||||
|
||||
* allow old exporter format to work, but with deprecation warning [David Dollar]
|
||||
* remove debugging code [David Dollar]
|
||||
* Merge pull request #219 from MarkDBlackwell/patch-1 [David Dollar]
|
||||
* Avoid crash by verifying the existence of SIGHUP before accessing it. [Mark D. Blackwell]
|
||||
* allow color to be forced on [David Dollar]
|
||||
* terminate gracefully if stdout goes away [David Dollar]
|
||||
* always flush output [David Dollar]
|
||||
* Merge pull request #212 from morgoth/added-version-command [David Dollar]
|
||||
* added command for displaying foreman version [Wojciech Wnętrzak]
|
||||
* Merge pull request #211 from morgoth/fixed-yaml-usage [David Dollar]
|
||||
* fixed using YAML [Wojciech Wnętrzak]
|
||||
* test on more things, but don't fail [David Dollar]
|
||||
* changelog [David Dollar]
|
||||
* 0.48.0.pre1 [David Dollar]
|
||||
* foreman doesn't work on ruby 1.8, may try to fix later [David Dollar]
|
||||
* use bash [David Dollar]
|
||||
* massive refactoring for programmatic control and stability [David Dollar]
|
||||
* Merge pull request #164 from hsume2/master [David Dollar]
|
||||
* Only run tmux specs if tmux is installed [Henry Hsu]
|
||||
* Do not assume BUNDLE_GEMFILE [Henry Hsu]
|
||||
* Add support for starting procfile in tmux session [Henry Hsu]
|
||||
|
||||
## 0.47.0 (2012-06-07)
|
||||
|
||||
* Fix multi-word argument handling in `foreman run`. [Daniel Brockman]
|
||||
* Make 'PORT=5000 foreman start' work [Koen Van der Auwera]
|
||||
* Terminate gracefully upon SIGHUP [Stefan Schüßler]
|
||||
* Set port from .env if specified [Koen Van der Auwera]
|
||||
* Updated bluepill exporter to use environment variables from .env [Aneeth]
|
||||
* Added launchd exporter [Maxwell Swadling]
|
||||
* Quote and escape environment variables in upstart templates [Matt Griffin]
|
||||
* Added list of ports to other languages to README [elf Pavlik]
|
||||
|
||||
## 0.46.0 (2012-05-02)
|
||||
|
||||
* Add Profile load/write/append API [Michael Granger]
|
||||
* Guard against missing Procfile in engine.rb [Brian Kaney]
|
||||
|
||||
## 0.45.0 (2012-04-26)
|
||||
|
||||
* create and chown log dir in upstart export. [Phil Hagelberg]
|
||||
* remove parka from dist files [David Dollar]
|
||||
|
||||
## 0.44.0 (2012-04-23)
|
||||
|
||||
* make var output order repeatable in supervisord export [David Dollar]
|
||||
* make --procfile and --app-root influence each other in a more intuitive way [David Dollar]
|
||||
* Look for .env and app_root in the same dir as the Procfile. [Phil Hagelberg]
|
||||
|
||||
## 0.43.0 (2012-04-20)
|
||||
|
||||
* wrap supervisord env vars in quotes [Raphael Randschau]
|
||||
|
||||
## 0.42.0 (2012-04-18)
|
||||
|
||||
* Move read_environment to a public class method. [Phil Hagelberg]
|
||||
* Drop parka dependency [Phil Hagelberg]
|
||||
* add group support for supervisord [Raphael Randschau]
|
||||
* fix enviroment export [Raphael Randschau]
|
||||
|
||||
## 0.41.0 (2012-03-16)
|
||||
|
||||
* replace term-ansicolor with built-in colorization [David Dollar]
|
||||
* supervisord export template [Raphael Randschau]
|
||||
|
||||
## 0.40.0 (2012-02-24)
|
||||
|
||||
* support various quoting styles in .env [David Dollar]
|
||||
* remove load_env! as it's made unnecessary by foreman run [David Dollar]
|
||||
* Provide a useful error if `foreman check` fails to find a Procfile [R. Tyler Croy]
|
||||
* update docs [David Dollar]
|
||||
|
||||
## 0.39.0 (2012-02-07)
|
||||
|
||||
* rename bin/runner to bin/foreman-runner [David Dollar]
|
||||
* fix tgz release [David Dollar]
|
||||
* bundle update hpricot [John Firebaugh]
|
||||
* touch up .pkg release tasks [David Dollar]
|
||||
|
||||
## 0.38.0 (2012-02-02)
|
||||
|
||||
* bring back single process starting [David Dollar]
|
||||
* more attempts at getting ci working with jruby [David Dollar]
|
||||
* ignore .rbenv-version [David Dollar]
|
||||
* force to binary encoding if supported [David Dollar]
|
||||
|
||||
## 0.37.2 (2012-01-29)
|
||||
|
||||
* handle directories with spaces in runner [David Dollar]
|
||||
* update docs [David Dollar]
|
||||
|
||||
## 0.37.1 (2012-01-29)
|
||||
|
||||
* use binary pipes to better handle UTF-8 data [David Dollar]
|
||||
* set up example procfile with UTF-8 item [David Dollar]
|
||||
* remove autotest [David Dollar]
|
||||
* fix up authors generation [David Dollar]
|
||||
* fix up packaging after moving tasks [David Dollar]
|
||||
* fix up changelog tasks [David Dollar]
|
||||
|
||||
## 0.37.0 (2012-01-29)
|
||||
|
||||
* put an entire line of output inside a single mutex so we don't cross the streams [David Dollar]
|
||||
* fix race condition with process termination [David Dollar]
|
||||
* allow external custom exporters [Chris Lowder]
|
||||
* fix the test for an empty string in bin/runner [Florian Apolloner]
|
||||
* ensure we have non-nil data, fixes #111 [David Dollar]
|
||||
* make sure error method exists, fixes #104 [David Dollar]
|
||||
* clean up chdir usage [David Dollar]
|
||||
* normalize platform names [David Dollar]
|
||||
* add windows support [David Dollar]
|
||||
* add jruby support [David Dollar]
|
||||
* pass basedir along to the runner script [David Dollar]
|
||||
* harden runner script [David Dollar]
|
||||
* add many missing specs [brainopia]
|
||||
* clean up fakefs usage in specs [brainopia]
|
||||
* runit creates a full path to export directory. [Fletcher Nichol]
|
||||
|
||||
## 0.36.1 (2012-01-18)
|
||||
|
||||
* 0.36.1 [David Dollar]
|
||||
* bump term-ansicolor in gemspec [David Dollar]
|
||||
|
||||
## 0.36.0 (2012-01-17)
|
||||
|
||||
* 0.36.0 [David Dollar]
|
||||
* sync the writer stream [David Dollar]
|
||||
* capture stderr as well [David Dollar]
|
||||
|
||||
## 0.35.0 (2012-01-16)
|
||||
|
||||
* update rake [David Dollar]
|
||||
* 0.35.0 [David Dollar]
|
||||
* Merge pull request #132 from Viximo/feature/concurrency [David Dollar]
|
||||
* Fix export specs [Matt Griffin]
|
||||
* Merge branch 'master' of https://github.com/michaeldwan/foreman into feature/concurrency [Matt Griffin]
|
||||
* default process concurrency is 0 when concurrency options specified, otherwise default concurrency is 1 [Michael Dwan]
|
||||
|
||||
## 0.34.1 (2012-01-16)
|
||||
|
||||
* 0.34.1 [David Dollar]
|
||||
* fix missing start desc [David Dollar]
|
||||
|
||||
## 0.34.0 (2012-01-16)
|
||||
|
||||
* 0.34.0 [David Dollar]
|
||||
* update man page [David Dollar]
|
||||
* update docs for -d [David Dollar]
|
||||
* Merge pull request #101 from ndbroadbent/foreman [David Dollar]
|
||||
* Wrap around to the first colour when all the colours are used [Craig R Webster]
|
||||
* run specs in random order [David Dollar]
|
||||
* update rspec [David Dollar]
|
||||
* pedantry [David Dollar]
|
||||
* Set executable bit on runit run scripts. [Matthijs Langenberg]
|
||||
* Merge pull request #114 from gburt/master [David Dollar]
|
||||
* add more colors [Gabriel Burt]
|
||||
* Added option to specify app_root, if executing a Procfile from a shared location [Nathan Broadbent]
|
||||
|
||||
## 0.33.1 (2012-01-16)
|
||||
|
||||
* 0.33.1 [David Dollar]
|
||||
* Merge pull request #129 from fnichol/resolve-home-template [David Dollar]
|
||||
* Expand template path under user's home directory. [Fletcher Nichol]
|
||||
|
||||
## 0.33.0 (2012-01-15)
|
||||
|
||||
* 0.33.0 [David Dollar]
|
||||
* Revert "Merge pull request #125 from brainopia/master" [David Dollar]
|
||||
|
||||
## 0.32.0 (2012-01-12)
|
||||
|
||||
* 0.32.0 [David Dollar]
|
||||
* Merge pull request #125 from brainopia/master [David Dollar]
|
||||
* Merge pull request #121 from Viximo/feature/run [David Dollar]
|
||||
* Return some whitespace that was accidentally removed [Matt Griffin]
|
||||
* Steal the run method back from Thor so that it can be used in place for exec for running commands in the foreman environment. [Matt Griffin]
|
||||
* Remove old cruft [brainopia]
|
||||
* In case someone wants to use bin/runner directly [brainopia]
|
||||
* Fix for double fork [brainopia]
|
||||
* Use ruby exec which works with escaped cmd and replaces shell [brainopia]
|
||||
* Fix foreman to work with cmds containing pipes and redirects [brainopia]
|
||||
* Add "exec" action to allow execution of arbitrary commands with the app's environment. [Matt Griffin]
|
||||
* tweak readme [David Dollar]
|
||||
|
||||
## 0.31.0 (2012-01-04)
|
||||
|
||||
* 0.31.0 [David Dollar]
|
||||
* make fork more robust [David Dollar]
|
||||
* remove unnecessary debug [David Dollar]
|
||||
* add more information when shutting down [David Dollar]
|
||||
* Merge pull request #110 from lstoll/master [David Dollar]
|
||||
* Use different port ranges for each process type [Lincoln Stoll]
|
||||
|
||||
## 0.30.1 (2011-12-23)
|
||||
|
||||
* 0.30.1 [David Dollar]
|
||||
* require thread for mutex [David Dollar]
|
||||
|
||||
## 0.30.0 (2011-12-22)
|
||||
|
||||
* 0.30.0 [David Dollar]
|
||||
* compatibility with ruby 1.8 [David Dollar]
|
||||
|
||||
## 0.29.0 (2011-12-22)
|
||||
|
||||
* 0.29.0 [David Dollar]
|
||||
* 0.28.0.pre2 [David Dollar]
|
||||
* fix pipe error [David Dollar]
|
||||
* 0.28.0.pre1 [David Dollar]
|
||||
* Merge branch 'fork' [David Dollar]
|
||||
* wip [David Dollar]
|
||||
* wip [David Dollar]
|
||||
* wip [David Dollar]
|
||||
* wip [David Dollar]
|
||||
* wip [David Dollar]
|
||||
|
||||
## 0.27.0 (2011-12-05)
|
||||
|
||||
* 0.27.0 [David Dollar]
|
||||
* add changelog [David Dollar]
|
||||
* Merge pull request #103 from csquared/load_env_from_irb [David Dollar]
|
||||
* refactor load_env to apply_environment [Chris Continanza]
|
||||
* rename load! to load_env! [Chris Continanza]
|
||||
* use ./.env as default [Chris Continanza]
|
||||
* load contents from env file [Chris Continanza]
|
||||
* refactor engine to expose env methods [Chris Continanza]
|
||||
* disable email notifications [David Dollar]
|
||||
* add travis config [David Dollar]
|
||||
|
||||
## 0.26.1 2011-12-05
|
||||
|
||||
* Merge pull request #103 from csquared/load_env_from_irb [David Dollar]
|
||||
* refactor load_env to apply_environment [Chris Continanza]
|
||||
* rename load! to load_env! [Chris Continanza]
|
||||
* use ./.env as default [Chris Continanza]
|
||||
* load contents from env file [Chris Continanza]
|
||||
* refactor engine to expose env methods [Chris Continanza]
|
||||
* disable email notifications [David Dollar]
|
||||
* add travis config [David Dollar]
|
||||
20
Gemfile
20
Gemfile
@@ -2,14 +2,22 @@ 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 'aws-s3'
|
||||
gem 'rake'
|
||||
gem 'ronn'
|
||||
gem 'fakefs', '~> 0.2.1'
|
||||
gem 'rcov', '~> 0.9.8'
|
||||
gem 'fakefs', '~> 0.3.2'
|
||||
gem 'rr', '~> 1.0.2'
|
||||
gem 'rspec', '~> 2.6.0'
|
||||
gem 'aws-s3'
|
||||
gem "rubyzip"
|
||||
gem 'rspec', '~> 2.0'
|
||||
gem "simplecov", :require => false
|
||||
gem 'timecop'
|
||||
gem 'yard'
|
||||
end
|
||||
|
||||
61
Gemfile.lock
61
Gemfile.lock
@@ -1,8 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
foreman (0.23.0)
|
||||
term-ansicolor (~> 1.0.5)
|
||||
foreman (0.61.0)
|
||||
thor (>= 0.13.6)
|
||||
|
||||
GEM
|
||||
@@ -13,50 +12,54 @@ GEM
|
||||
mime-types
|
||||
xml-simple
|
||||
builder (3.0.0)
|
||||
crack (0.1.8)
|
||||
diff-lcs (1.1.2)
|
||||
fakefs (0.2.1)
|
||||
hpricot (0.8.2)
|
||||
diff-lcs (1.1.3)
|
||||
fakefs (0.3.2)
|
||||
hpricot (0.8.6)
|
||||
hpricot (0.8.6-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)
|
||||
ronn (0.7.3)
|
||||
hpricot (>= 0.8.2)
|
||||
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)
|
||||
rubyzip (0.9.4)
|
||||
term-ansicolor (1.0.6)
|
||||
thor (0.14.6)
|
||||
rspec-mocks (2.8.0)
|
||||
simplecov (0.5.4)
|
||||
multi_json (~> 1.0.3)
|
||||
simplecov-html (~> 0.5.3)
|
||||
simplecov-html (0.5.3)
|
||||
thor (0.16.0)
|
||||
timecop (0.3.5)
|
||||
win32console (1.3.0-x86-mingw32)
|
||||
xml-simple (1.0.15)
|
||||
yard (0.8.2)
|
||||
|
||||
PLATFORMS
|
||||
java
|
||||
ruby
|
||||
x86-mingw32
|
||||
|
||||
DEPENDENCIES
|
||||
aws-s3
|
||||
fakefs (~> 0.2.1)
|
||||
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)
|
||||
rubyzip
|
||||
rspec (~> 2.0)
|
||||
simplecov
|
||||
timecop
|
||||
win32console (~> 1.3.0)
|
||||
yard
|
||||
|
||||
@@ -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
|
||||
46
README.md
Normal file
46
README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
# 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)
|
||||
* [changelog](https://github.com/ddollar/foreman/blob/master/Changelog.md)
|
||||
|
||||
## Ports
|
||||
|
||||
* [shoreman](https://github.com/hecticjeff/shoreman) - shell
|
||||
* [honcho](https://github.com/nickstenning/honcho) - python
|
||||
* [norman](https://github.com/josh/norman) - node.js
|
||||
|
||||
## Authors
|
||||
|
||||
#### Created and maintained by
|
||||
David Dollar
|
||||
|
||||
#### Patches contributed by
|
||||
[Contributor List](https://github.com/ddollar/foreman/contributors)
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
166
Rakefile
166
Rakefile
@@ -1,168 +1,8 @@
|
||||
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
|
||||
require "bundler/setup"
|
||||
|
||||
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
|
||||
Dir[File.expand_path("../tasks/*.rake", __FILE__)].each do |task|
|
||||
load task
|
||||
end
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Autotest.add_discovery { "rspec2" }
|
||||
41
bin/foreman-runner
Executable file
41
bin/foreman-runner
Executable file
@@ -0,0 +1,41 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
#/ Usage: foreman-runner [-d <dir>] [-p] <command> [<args>...]
|
||||
#/
|
||||
#/ Run a command with exec, optionally changing directory first
|
||||
|
||||
set -e
|
||||
|
||||
error() {
|
||||
echo $@ >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat $0 | grep '^#/' | cut -c4-
|
||||
exit
|
||||
}
|
||||
|
||||
read_profile=""
|
||||
|
||||
while getopts ":hd:p" OPT; do
|
||||
case $OPT in
|
||||
d) cd "$OPTARG" ;;
|
||||
p) read_profile="1" ;;
|
||||
h) usage ;;
|
||||
\?) error "invalid option: -$OPTARG" ;;
|
||||
:) error "option -$OPTARG requires an argument" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND-1))
|
||||
|
||||
[ -z "$1" ] && usage
|
||||
|
||||
if [ "$read_profile" = "1" ]; then
|
||||
if [ -f .profile ]; then
|
||||
. .profile
|
||||
fi
|
||||
fi
|
||||
|
||||
exec "$@"
|
||||
8
bin/taskman
Executable file
8
bin/taskman
Executable file
@@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
$:.unshift File.expand_path("../../lib", __FILE__)
|
||||
|
||||
require "foreman/cli"
|
||||
|
||||
Foreman::CLI.engine_class = Foreman::TmuxEngine
|
||||
Foreman::CLI.start
|
||||
2
data/example/.profile.d/foo.sh
Normal file
2
data/example/.profile.d/foo.sh
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
export FOO=bar
|
||||
@@ -1,2 +1,4 @@
|
||||
ticker: ruby ./ticker $PORT
|
||||
error : ruby ./error
|
||||
ticker: ruby ./ticker $PORT
|
||||
error: ruby ./error
|
||||
utf8: ruby ./utf8
|
||||
spawner: ./spawner
|
||||
|
||||
14
data/example/spawnee
Executable file
14
data/example/spawnee
Executable file
@@ -0,0 +1,14 @@
|
||||
#!/bin/sh
|
||||
|
||||
NAME="$1"
|
||||
|
||||
sigterm() {
|
||||
echo "$NAME: got sigterm"
|
||||
}
|
||||
|
||||
#trap sigterm SIGTERM
|
||||
|
||||
while true; do
|
||||
echo "$NAME: ping $$"
|
||||
sleep 1
|
||||
done
|
||||
7
data/example/spawner
Executable file
7
data/example/spawner
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/bin/sh
|
||||
|
||||
./spawnee A &
|
||||
./spawnee B &
|
||||
./spawnee C &
|
||||
|
||||
wait
|
||||
11
data/example/utf8
Executable file
11
data/example/utf8
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/usr/bin/env ruby
|
||||
# encoding: BINARY
|
||||
|
||||
$stdout.sync = true
|
||||
|
||||
while true
|
||||
puts "\u65e5\u672c\u8a9e\u6587\u5b57\u5217"
|
||||
puts "\u0915\u0932\u094d\u0907\u0928\u0643\u0637\u0628\u041a\u0430\u043b\u0438\u043d\u0430"
|
||||
puts "\xff\x03"
|
||||
sleep 1
|
||||
end
|
||||
@@ -3,25 +3,26 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
|
||||
app.uid = "<%= user %>"
|
||||
app.gid = "<%= user %>"
|
||||
|
||||
<% engine.processes.values.each do |process| %>
|
||||
<% 1.upto(concurrency[process.name]) do |num| %>
|
||||
<% port = engine.port_for(process, num, options[:port]) %>
|
||||
app.process("<%= process.name %>-<%=num%>") do |process|
|
||||
process.start_command = "<%= process.command.gsub("$PORT", port.to_s) %>"
|
||||
<% engine.each_process do |name, process| %>
|
||||
<% 1.upto(engine.formation[name]) do |num| %>
|
||||
<% port = engine.port_for(process, num) %>
|
||||
app.process("<%= name %>-<%= num %>") do |process|
|
||||
process.start_command = "<%= process.command %>"
|
||||
|
||||
process.working_dir = "<%= engine.directory %>"
|
||||
process.working_dir = "<%= engine.root %>"
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "<%= port %>"}
|
||||
process.environment = <%= engine.env.merge("PORT" => port.to_s).inspect %>
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
process.stop_grace_time = 45.seconds
|
||||
|
||||
process.stdout = process.stderr = "<%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log"
|
||||
process.stdout = process.stderr = "<%= log %>/<%= app %>-<%= name %>-<%= num %>.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
children.stop_command "kill -QUIT {{PID}}"
|
||||
children.stop_command "kill {{PID}}"
|
||||
end
|
||||
|
||||
process.group = "<%= app %>-<%= process.name %>"
|
||||
|
||||
process.group = "<%= app %>-<%= name %>"
|
||||
end
|
||||
<% end %>
|
||||
<% end %>
|
||||
end
|
||||
end
|
||||
|
||||
33
data/export/launchd/launchd.plist.erb
Normal file
33
data/export/launchd/launchd.plist.erb
Normal file
@@ -0,0 +1,33 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string><%= "#{app}-#{name}-#{num}" %></string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<%- engine.env.merge("PORT" => port).each_pair do |var,env| -%>
|
||||
<key><%= var.upcase %></key>
|
||||
<string><%= env %></string>
|
||||
<%- end -%>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<%- command_args.each do |command| -%>
|
||||
<string><%= command %></string>
|
||||
<%- end -%>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string><%= log %>/<%= app %>-<%= name %>-<%=num%>.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string><%= log %>/<%= app %>-<%= name %>-<%=num%>.log</string>
|
||||
<key>UserName</key>
|
||||
<string><%= user %></string>
|
||||
<key>WorkingDirectory</key>
|
||||
<string><%= engine.root %></string>
|
||||
</dict>
|
||||
</plist>
|
||||
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 %>/<%= 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.root %>
|
||||
exec chpst -u <%= user %> -e <%= File.join(location, "#{process_directory}/env") %> <%= process.command %>
|
||||
27
data/export/supervisord/app.conf.erb
Normal file
27
data/export/supervisord/app.conf.erb
Normal file
@@ -0,0 +1,27 @@
|
||||
<%
|
||||
app_names = []
|
||||
engine.each_process do |name, process|
|
||||
1.upto(engine.formation[name]) do |num|
|
||||
port = engine.port_for(process, num)
|
||||
full_name = "#{app}-#{name}-#{num}"
|
||||
environment = engine.env.merge("PORT" => port.to_s).map do |key, value|
|
||||
"#{key}=#{shell_quote(value)}"
|
||||
end
|
||||
app_names << full_name
|
||||
%>
|
||||
[program:<%= full_name %>]
|
||||
command=<%= process.command %>
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopsignal=QUIT
|
||||
stdout_logfile=<%= log %>/<%= name %>-<%= num %>.log
|
||||
stderr_logfile=<%= log %>/<%= name %>-<%= num %>.error.log
|
||||
user=<%= user %>
|
||||
directory=<%= engine.root %>
|
||||
environment=<%= environment.join(',') %><%
|
||||
end
|
||||
end
|
||||
%>
|
||||
|
||||
[group:<%= app %>]
|
||||
programs=<%= app_names.join(',') %>
|
||||
@@ -1,8 +1,16 @@
|
||||
pre-start script
|
||||
|
||||
bash << "EOF"
|
||||
mkdir -p <%= log_root %>
|
||||
chown -R <%= user %> <%= log_root %>
|
||||
mkdir -p <%= log %>
|
||||
chown -R <%= user %> <%= log %>
|
||||
EOF
|
||||
|
||||
end script
|
||||
|
||||
start on (started network-interface
|
||||
or started network-manager
|
||||
or started networking)
|
||||
|
||||
stop on (stopping network-interface
|
||||
or stopping network-manager
|
||||
or stopping networking)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
start on starting <%= app %>-<%= process.name %>
|
||||
stop on stopping <%= app %>-<%= process.name %>
|
||||
start on starting <%= app %>-<%= name %>
|
||||
stop on stopping <%= app %>-<%= name %>
|
||||
respawn
|
||||
|
||||
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'
|
||||
exec su - <%= user %> -c 'cd <%= engine.root %>; export PORT=<%= port %>;<% engine.env.each_pair do |var,env| %> export <%= var.upcase %>=<%= shell_quote(env) %>; <% end %> <%= process.command %> >> <%= log %>/<%=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/foreman-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
|
||||
2
dist/gem.rake
vendored
2
dist/gem.rake
vendored
@@ -10,5 +10,5 @@ task "gem:clean" do
|
||||
end
|
||||
|
||||
task "gem:release" => "gem:build" do |t|
|
||||
sh "parka push -f #{pkg("foreman-#{version}.gem")}"
|
||||
sh "gem push #{pkg("foreman-#{version}.gem")} || echo 'error'"
|
||||
end
|
||||
|
||||
16
dist/jruby.rake
vendored
Normal file
16
dist/jruby.rake
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
file pkg("foreman-#{version}-jruby.gem") => distribution_files do |t|
|
||||
Bundler.with_clean_env do
|
||||
sh "env PLATFORM=java gem build foreman.gemspec"
|
||||
end
|
||||
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 "gem push #{pkg("foreman-#{version}-jruby.gem")} || echo 'error'"
|
||||
end
|
||||
16
dist/mingw32.rake
vendored
Normal file
16
dist/mingw32.rake
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
file pkg("foreman-#{version}-mingw32.gem") => distribution_files do |t|
|
||||
Bundler.with_clean_env do
|
||||
sh "env PLATFORM=mingw32 gem build foreman.gemspec"
|
||||
end
|
||||
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 "gem push #{pkg("foreman-#{version}-mingw32.gem")} || echo 'error'"
|
||||
end
|
||||
16
dist/mswin32.rake
vendored
Normal file
16
dist/mswin32.rake
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
file pkg("foreman-#{version}-x86-mswin32.gem") => distribution_files do |t|
|
||||
Bundler.with_clean_env do
|
||||
sh "env PLATFORM=mswin32 gem build foreman.gemspec"
|
||||
end
|
||||
sh "mv foreman-#{version}-x86-mswin32.gem #{t.name}"
|
||||
end
|
||||
|
||||
task "mswin32:build" => pkg("foreman-#{version}-x86-mswin32.gem")
|
||||
|
||||
task "mswin32:clean" do
|
||||
clean pkg("foreman-#{version}-x86-mswin32.gem")
|
||||
end
|
||||
|
||||
task "mswin32:release" => "mswin32:build" do |t|
|
||||
sh "gem push #{pkg("foreman-#{version}-x86-mswin32.gem")} || echo 'error'"
|
||||
end
|
||||
15
dist/pkg.rake
vendored
15
dist/pkg.rake
vendored
@@ -13,7 +13,7 @@ file pkg("foreman-#{version}.pkg") => distribution_files do |t|
|
||||
|
||||
mkdir_p "pkg"
|
||||
mkdir_p "pkg/Resources"
|
||||
mkdir_p "pkg/foreman-#{version}.pkg"
|
||||
mkdir_p "pkg/foreman.pkg"
|
||||
|
||||
dist = File.read(resource("pkg/Distribution.erb"))
|
||||
dist = ERB.new(dist).result(binding)
|
||||
@@ -21,20 +21,21 @@ file pkg("foreman-#{version}.pkg") => distribution_files do |t|
|
||||
|
||||
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 }
|
||||
File.open("pkg/foreman.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"
|
||||
mkdir_p "pkg/foreman.pkg/Scripts"
|
||||
cp resource("pkg/postinstall"), "pkg/foreman.pkg/Scripts/postinstall"
|
||||
chmod 0755, "pkg/foreman.pkg/Scripts/postinstall"
|
||||
|
||||
sh %{ mkbom -s foreman pkg/foreman-#{version}.pkg/Bom }
|
||||
sh %{ mkbom -s foreman pkg/foreman.pkg/Bom }
|
||||
|
||||
Dir.chdir("foreman") do
|
||||
sh %{ pax -wz -x cpio . > ../pkg/foreman-#{version}.pkg/Payload }
|
||||
sh %{ pax -wz -x cpio . > ../pkg/foreman.pkg/Payload }
|
||||
end
|
||||
|
||||
sh %{ pkgutil --flatten pkg foreman-#{version}.pkg }
|
||||
|
||||
FileUtils.mkdir_p(File.dirname(t.name))
|
||||
cp_r "foreman-#{version}.pkg", t.name
|
||||
end
|
||||
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
|
||||
6
dist/resources/pkg/Distribution.erb
vendored
6
dist/resources/pkg/Distribution.erb
vendored
@@ -10,14 +10,14 @@
|
||||
]]></script>
|
||||
<choices-outline>
|
||||
<line choice="git"/>
|
||||
<line choice="foreman-<%= version %>"/>
|
||||
<line choice="foreman"/>
|
||||
</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">
|
||||
<choice id="foreman" 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>
|
||||
<pkg-ref id="io.foreman.installer" installKBytes="<%= kbytes %>" version="<%= version %>" auth="Root">#foreman.pkg</pkg-ref>
|
||||
</installer-script>
|
||||
|
||||
|
||||
1
dist/resources/pkg/PackageInfo.erb
vendored
1
dist/resources/pkg/PackageInfo.erb
vendored
@@ -4,4 +4,3 @@
|
||||
<postinstall file="./postinstall"/>
|
||||
</scripts>
|
||||
</pkg-info>
|
||||
|
||||
|
||||
2
dist/tgz.rake
vendored
2
dist/tgz.rake
vendored
@@ -3,7 +3,7 @@ file pkg("foreman-#{version}.tgz") => distribution_files do |t|
|
||||
mkchdir("foreman") do
|
||||
assemble_distribution
|
||||
assemble_gems
|
||||
rm_rf "bin"
|
||||
rm_f "bin/foreman"
|
||||
assemble resource("tgz/foreman"), "foreman", 0755
|
||||
end
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ require "foreman/version"
|
||||
|
||||
Gem::Specification.new do |gem|
|
||||
gem.name = "foreman"
|
||||
gem.license = "MIT"
|
||||
gem.version = Foreman::VERSION
|
||||
|
||||
gem.author = "David Dollar"
|
||||
@@ -16,6 +17,15 @@ 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 'thor', '>= 0.13.6'
|
||||
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
|
||||
|
||||
@@ -4,5 +4,20 @@ module Foreman
|
||||
|
||||
class AppDoesNotExist < Exception; end
|
||||
|
||||
end
|
||||
def self.runner
|
||||
File.expand_path("../../bin/foreman-runner", __FILE__)
|
||||
end
|
||||
|
||||
def self.jruby_18?
|
||||
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java" and ruby_18?
|
||||
end
|
||||
|
||||
def self.ruby_18?
|
||||
defined?(RUBY_VERSION) and RUBY_VERSION =~ /^1\.8\.\d+/
|
||||
end
|
||||
|
||||
def self.windows?
|
||||
defined?(RUBY_PLATFORM) and RUBY_PLATFORM =~ /(win|w)32$/
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
54
lib/foreman/capistrano.rb
Normal file
54
lib/foreman/capistrano.rb
Normal file
@@ -0,0 +1,54 @@
|
||||
if defined?(Capistrano)
|
||||
Capistrano::Configuration.instance(:must_exist).load do
|
||||
|
||||
namespace :foreman do
|
||||
desc <<-DESC
|
||||
Export the Procfile to upstart. Will use sudo if available.
|
||||
|
||||
You can override any of these defaults by setting the variables shown below.
|
||||
|
||||
set :foreman_format, "upstart"
|
||||
set :foreman_location, "/etc/init"
|
||||
set :foreman_procfile, "Procfile"
|
||||
set :foreman_app, application
|
||||
set :foreman_user, user
|
||||
set :foreman_log, 'shared_path/log'
|
||||
set :foreman_concurrency, false
|
||||
DESC
|
||||
task :export, :roles => :app do
|
||||
bundle_cmd = fetch(:bundle_cmd, "bundle")
|
||||
foreman_format = fetch(:foreman_format, "upstart")
|
||||
foreman_location = fetch(:foreman_location, "/etc/init")
|
||||
foreman_procfile = fetch(:foreman_procfile, "Procfile")
|
||||
foreman_app = fetch(:foreman_app, application)
|
||||
foreman_user = fetch(:foreman_user, user)
|
||||
foreman_log = fetch(:foreman_log, "#{shared_path}/log")
|
||||
foreman_concurrency = fetch(:foreman_concurrency, false)
|
||||
|
||||
args = ["#{foreman_format} #{foreman_location}"]
|
||||
args << "-f #{foreman_procfile}"
|
||||
args << "-a #{foreman_app}"
|
||||
args << "-u #{foreman_user}"
|
||||
args << "-l #{foreman_log}"
|
||||
args << "-c #{foreman_concurrency}" if foreman_concurrency
|
||||
run "cd #{release_path} && #{sudo} #{bundle_cmd} exec foreman export #{args.join(' ')}"
|
||||
end
|
||||
|
||||
desc "Start the application services"
|
||||
task :start, :roles => :app do
|
||||
run "#{sudo} start #{application}"
|
||||
end
|
||||
|
||||
desc "Stop the application services"
|
||||
task :stop, :roles => :app do
|
||||
run "#{sudo} stop #{application}"
|
||||
end
|
||||
|
||||
desc "Restart the application services"
|
||||
task :restart, :roles => :app do
|
||||
run "#{sudo} start #{application} || #{sudo} restart #{application}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,51 +1,62 @@
|
||||
require "foreman"
|
||||
require "foreman/helpers"
|
||||
require "foreman/engine"
|
||||
require "foreman/engine/cli"
|
||||
require "foreman/export"
|
||||
require "thor"
|
||||
require "foreman/version"
|
||||
require "shellwords"
|
||||
require "yaml"
|
||||
require "thor"
|
||||
|
||||
class Foreman::CLI < Thor
|
||||
|
||||
include Foreman::Helpers
|
||||
|
||||
map ["-v", "--version"] => :version
|
||||
|
||||
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
||||
class_option :root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"
|
||||
|
||||
desc "start [PROCESS]", "Start the application, or a specific process"
|
||||
desc "start [PROCESS]", "Start the application (or a specific PROCESS)"
|
||||
|
||||
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"'
|
||||
method_option :color, :type => :boolean, :aliases => "-c", :desc => "Force color to be enabled"
|
||||
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
||||
method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"'
|
||||
method_option :port, :type => :numeric, :aliases => "-p"
|
||||
method_option :timeout, :type => :numeric, :aliases => "-t", :desc => "Specify the amount of time (in seconds) processes have to shudown gracefully before receiving a SIGKILL, defaults to 5."
|
||||
|
||||
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(process=nil)
|
||||
check_procfile!
|
||||
|
||||
if process
|
||||
engine.execute(process)
|
||||
else
|
||||
engine.start
|
||||
end
|
||||
load_environment!
|
||||
engine.load_procfile(procfile)
|
||||
engine.options[:formation] = "#{process}=1" if process
|
||||
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
|
||||
when "bluepill" then Foreman::Export::Bluepill
|
||||
else error "Unknown export format: #{format}."
|
||||
end
|
||||
|
||||
formatter.new(engine).export(location, options)
|
||||
|
||||
load_environment!
|
||||
engine.load_procfile(procfile)
|
||||
formatter = Foreman::Export.formatter(format)
|
||||
formatter.new(location, engine, options).export
|
||||
rescue Foreman::Export::Exception => ex
|
||||
error ex.message
|
||||
end
|
||||
@@ -53,42 +64,91 @@ 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(', ')})"
|
||||
check_procfile!
|
||||
engine.load_procfile(procfile)
|
||||
error "no processes defined" unless engine.processes.length > 0
|
||||
puts "valid procfile detected (#{engine.process_names.join(', ')})"
|
||||
end
|
||||
|
||||
desc "run COMMAND [ARGS...]", "Run a command using your application's environment"
|
||||
|
||||
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
|
||||
|
||||
def run(*args)
|
||||
load_environment!
|
||||
|
||||
if File.exist?(procfile)
|
||||
engine.load_procfile(procfile)
|
||||
end
|
||||
|
||||
pid = fork do
|
||||
begin
|
||||
engine.env.each { |k,v| ENV[k] = v }
|
||||
if args.size == 1 && process = engine.process(args.first)
|
||||
process.exec(:env => engine.env)
|
||||
else
|
||||
exec args.shelljoin
|
||||
end
|
||||
rescue Errno::EACCES
|
||||
error "not executable: #{args.first}"
|
||||
rescue Errno::ENOENT
|
||||
error "command not found: #{args.first}"
|
||||
end
|
||||
end
|
||||
Process.wait(pid)
|
||||
exit $?.exitstatus
|
||||
end
|
||||
|
||||
desc "version", "Display Foreman gem version"
|
||||
|
||||
def version
|
||||
puts Foreman::VERSION
|
||||
end
|
||||
|
||||
no_tasks do
|
||||
def engine
|
||||
@engine ||= begin
|
||||
engine_class = Foreman::Engine::CLI
|
||||
engine = engine_class.new(options)
|
||||
engine
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def check_procfile!
|
||||
error("#{procfile} does not exist.") unless File.exist?(procfile)
|
||||
end
|
||||
|
||||
def engine
|
||||
@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)
|
||||
def check_procfile!
|
||||
error("#{procfile} does not exist.") unless File.exist?(procfile)
|
||||
end
|
||||
|
||||
def load_environment!
|
||||
if options[:env]
|
||||
options[:env].split(",").each do |file|
|
||||
engine.load_env file
|
||||
end
|
||||
else
|
||||
default_env = File.join(engine.root, ".env")
|
||||
engine.load_env default_env if File.exists?(default_env)
|
||||
end
|
||||
end
|
||||
|
||||
def procfile
|
||||
case
|
||||
when options[:procfile] then options[:procfile]
|
||||
when options[:root] then File.expand_path(File.join(options[:root], "Procfile"))
|
||||
else "Procfile"
|
||||
end
|
||||
end
|
||||
|
||||
def options
|
||||
original_options = super
|
||||
return original_options unless File.exists?(".foreman")
|
||||
defaults = YAML::load_file(".foreman") || {}
|
||||
defaults = ::YAML::load_file(".foreman") || {}
|
||||
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
|
||||
end
|
||||
|
||||
|
||||
@@ -1,241 +1,324 @@
|
||||
require "foreman"
|
||||
require "foreman/env"
|
||||
require "foreman/process"
|
||||
require "foreman/utils"
|
||||
require "pty"
|
||||
require "foreman/procfile"
|
||||
require "tempfile"
|
||||
require "timeout"
|
||||
require "term/ansicolor"
|
||||
require "fileutils"
|
||||
require "thread"
|
||||
|
||||
class Foreman::Engine
|
||||
|
||||
attr_reader :procfile
|
||||
attr_reader :directory
|
||||
attr_reader :environment
|
||||
attr_reader :env
|
||||
attr_reader :options
|
||||
attr_reader :processes
|
||||
|
||||
extend Term::ANSIColor
|
||||
# Create an +Engine+ for running processes
|
||||
#
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options [String] :formation (all=1) The process formation to use
|
||||
# @option options [Fixnum] :port (5000) The base port to assign to processes
|
||||
# @option options [String] :root (Dir.pwd) The root directory from which to run processes
|
||||
#
|
||||
def initialize(options={})
|
||||
@options = options.dup
|
||||
|
||||
COLORS = [ cyan, yellow, green, magenta, red ]
|
||||
@options[:formation] ||= (options[:concurrency] || "all=1")
|
||||
@options[:timeout] ||= 5
|
||||
|
||||
def initialize(procfile, options={})
|
||||
@procfile = read_procfile(procfile)
|
||||
@directory = File.expand_path(File.dirname(procfile))
|
||||
@options = options
|
||||
@environment = read_environment_files(options[:env])
|
||||
@env = {}
|
||||
@mutex = Mutex.new
|
||||
@names = {}
|
||||
@processes = []
|
||||
@running = {}
|
||||
@readers = {}
|
||||
end
|
||||
|
||||
def processes
|
||||
@processes ||= begin
|
||||
@order = []
|
||||
procfile.split("\n").inject({}) do |hash, line|
|
||||
next hash if line.strip == ""
|
||||
name, command = line.split(/\s*:\s+/, 2)
|
||||
unless command
|
||||
warn_deprecated_procfile!
|
||||
name, command = line.split(/ +/, 2)
|
||||
# Start the processes registered to this +Engine+
|
||||
#
|
||||
def start
|
||||
# Make sure foreman is the process group leader.
|
||||
Process.setpgrp unless Foreman.windows?
|
||||
|
||||
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
||||
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
||||
trap("HUP") { puts "SIGHUP received"; terminate_gracefully } if ::Signal.list.keys.include? 'HUP'
|
||||
|
||||
startup
|
||||
spawn_processes
|
||||
watch_for_output
|
||||
sleep 0.1
|
||||
watch_for_termination { terminate_gracefully }
|
||||
shutdown
|
||||
end
|
||||
|
||||
# Register a process to be run by this +Engine+
|
||||
#
|
||||
# @param [String] name A name for this process
|
||||
# @param [String] command The command to run
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options [Hash] :env A custom environment for this process
|
||||
#
|
||||
def register(name, command, options={})
|
||||
options[:env] ||= env
|
||||
options[:cwd] ||= File.dirname(command.split(" ").first)
|
||||
process = Foreman::Process.new(command, options)
|
||||
@names[process] = name
|
||||
@processes << process
|
||||
end
|
||||
|
||||
# Clear the processes registered to this +Engine+
|
||||
#
|
||||
def clear
|
||||
@names = {}
|
||||
@processes = []
|
||||
end
|
||||
|
||||
# Register processes by reading a Procfile
|
||||
#
|
||||
# @param [String] filename A Procfile from which to read processes to register
|
||||
#
|
||||
def load_procfile(filename)
|
||||
options[:root] ||= File.dirname(filename)
|
||||
Foreman::Procfile.new(filename).entries do |name, command|
|
||||
register name, command, :cwd => options[:root]
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Load a .env file into the +env+ for this +Engine+
|
||||
#
|
||||
# @param [String] filename A .env file to load into the environment
|
||||
#
|
||||
def load_env(filename)
|
||||
Foreman::Env.new(filename).entries do |name, value|
|
||||
@env[name] = value
|
||||
end
|
||||
end
|
||||
|
||||
# Send a signal to all processesstarted by this +Engine+
|
||||
#
|
||||
# @param [String] signal The signal to send to each process
|
||||
#
|
||||
def killall(signal="SIGTERM")
|
||||
if Foreman.windows?
|
||||
@running.each do |pid, (process, index)|
|
||||
system "sending #{signal} to #{name_for(pid)} at pid #{pid}"
|
||||
begin
|
||||
Process.kill(signal, pid)
|
||||
rescue Errno::ESRCH, Errno::EPERM
|
||||
end
|
||||
process = Foreman::Process.new(name, command)
|
||||
process.color = next_color
|
||||
@order << process.name
|
||||
hash.update(process.name => process)
|
||||
end
|
||||
else
|
||||
begin
|
||||
Process.kill "-#{signal}", Process.getpgrp
|
||||
rescue Errno::ESRCH, Errno::EPERM
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def process_order
|
||||
processes
|
||||
@order.uniq
|
||||
# Get the process formation
|
||||
#
|
||||
# @returns [Fixnum] The formation count for the specified process
|
||||
#
|
||||
def formation
|
||||
@formation ||= parse_formation(options[:formation])
|
||||
end
|
||||
|
||||
def processes_in_order
|
||||
process_order.map do |name|
|
||||
[name, processes[name]]
|
||||
# List the available process names
|
||||
#
|
||||
# @returns [Array] A list of process names
|
||||
#
|
||||
def process_names
|
||||
@processes.map { |p| @names[p] }
|
||||
end
|
||||
|
||||
# Get the +Process+ for a specifid name
|
||||
#
|
||||
# @param [String] name The process name
|
||||
#
|
||||
# @returns [Foreman::Process] The +Process+ for the specified name
|
||||
#
|
||||
def process(name)
|
||||
@names.invert[name]
|
||||
end
|
||||
|
||||
# Yield each +Process+ in order
|
||||
#
|
||||
def each_process
|
||||
process_names.each do |name|
|
||||
yield name, process(name)
|
||||
end
|
||||
end
|
||||
|
||||
def start
|
||||
proctitle "ruby: foreman master"
|
||||
|
||||
processes_in_order.each do |name, process|
|
||||
fork process
|
||||
end
|
||||
|
||||
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
||||
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
||||
|
||||
watch_for_termination
|
||||
# Get the root directory for this +Engine+
|
||||
#
|
||||
# @returns [String] The root directory
|
||||
#
|
||||
def root
|
||||
File.expand_path(options[:root] || Dir.pwd)
|
||||
end
|
||||
|
||||
def execute(name)
|
||||
|
||||
fork processes[name]
|
||||
|
||||
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
|
||||
trap("INT") { puts "SIGINT received"; terminate_gracefully }
|
||||
|
||||
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
|
||||
base_port.to_i + offset + num - 1
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def fork(process)
|
||||
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
|
||||
|
||||
1.upto(concurrency[process.name]) do |num|
|
||||
fork_individual(process, num, port_for(process, num, @options[:port]))
|
||||
# Get the port for a given process and offset
|
||||
#
|
||||
# @param [Foreman::Process] process A +Process+ associated with this engine
|
||||
# @param [Fixnum] instance The instance of the process
|
||||
#
|
||||
# @returns [Fixnum] port The port to use for this instance of this process
|
||||
#
|
||||
def port_for(process, instance, base=nil)
|
||||
if base
|
||||
base + (@processes.index(process.process) * 100) + (instance - 1)
|
||||
else
|
||||
base_port + (@processes.index(process) * 100) + (instance - 1)
|
||||
end
|
||||
end
|
||||
|
||||
def fork_individual(process, num, port)
|
||||
@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
|
||||
# Get the base port for this foreman instance
|
||||
#
|
||||
# @returns [Fixnum] port The base port
|
||||
#
|
||||
def base_port
|
||||
(options[:port] || env["PORT"] || ENV["PORT"] || 5000).to_i
|
||||
end
|
||||
|
||||
def run(process)
|
||||
proctitle "ruby: foreman #{process.name}"
|
||||
trap("SIGINT", "IGNORE")
|
||||
# deprecated
|
||||
def environment
|
||||
env
|
||||
end
|
||||
|
||||
begin
|
||||
Dir.chdir directory do
|
||||
PTY.spawn(process.command) do |stdin, stdout, pid|
|
||||
trap("SIGTERM") { Process.kill("SIGTERM", pid) }
|
||||
until stdin.eof?
|
||||
info stdin.gets, process
|
||||
private
|
||||
|
||||
### Engine API ######################################################
|
||||
|
||||
def startup
|
||||
raise TypeError, "must use a subclass of Foreman::Engine"
|
||||
end
|
||||
|
||||
def output(name, data)
|
||||
raise TypeError, "must use a subclass of Foreman::Engine"
|
||||
end
|
||||
|
||||
def shutdown
|
||||
raise TypeError, "must use a subclass of Foreman::Engine"
|
||||
end
|
||||
|
||||
## Helpers ##########################################################
|
||||
|
||||
def create_pipe
|
||||
IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
|
||||
end
|
||||
|
||||
def name_for(pid)
|
||||
process, index = @running[pid]
|
||||
[ @names[process], index.to_s ].compact.join(".")
|
||||
end
|
||||
|
||||
def parse_formation(formation)
|
||||
pairs = formation.to_s.gsub(/\s/, "").split(",")
|
||||
|
||||
pairs.inject(Hash.new(0)) do |ax, pair|
|
||||
process, amount = pair.split("=")
|
||||
process == "all" ? ax.default = amount.to_i : ax[process] = amount.to_i
|
||||
ax
|
||||
end
|
||||
end
|
||||
|
||||
def output_with_mutex(name, message)
|
||||
@mutex.synchronize do
|
||||
output name, message
|
||||
end
|
||||
end
|
||||
|
||||
def system(message)
|
||||
output_with_mutex "system", message
|
||||
end
|
||||
|
||||
def termination_message_for(status)
|
||||
if status.exited?
|
||||
"exited with code #{status.exitstatus}"
|
||||
elsif status.signaled?
|
||||
"terminated by SIG#{Signal.list.invert[status.termsig]}"
|
||||
else
|
||||
"died a mysterious death"
|
||||
end
|
||||
end
|
||||
|
||||
def flush_reader(reader)
|
||||
until reader.eof?
|
||||
data = reader.gets
|
||||
output_with_mutex name_for(@readers.key(reader)), data
|
||||
end
|
||||
end
|
||||
|
||||
## Engine ###########################################################
|
||||
|
||||
def spawn_processes
|
||||
@processes.each do |process|
|
||||
1.upto(formation[@names[process]]) do |n|
|
||||
reader, writer = create_pipe
|
||||
begin
|
||||
pid = process.run(:output => writer, :env => {
|
||||
"PORT" => port_for(process, n).to_s
|
||||
})
|
||||
writer.puts "started with pid #{pid}"
|
||||
rescue Errno::ENOENT
|
||||
writer.puts "unknown command: #{process.command}"
|
||||
end
|
||||
@running[pid] = [process, n]
|
||||
@readers[pid] = reader
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def watch_for_output
|
||||
Thread.new do
|
||||
begin
|
||||
loop do
|
||||
io = IO.select(@readers.values, nil, nil, 30)
|
||||
(io.nil? ? [] : io.first).each do |reader|
|
||||
if reader.eof?
|
||||
@readers.delete_if { |key, value| value == reader }
|
||||
else
|
||||
data = reader.gets
|
||||
output_with_mutex name_for(@readers.invert[reader]), data
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue PTY::ChildExited, Interrupt, Errno::EIO
|
||||
begin
|
||||
info "process exiting", process
|
||||
rescue Interrupt
|
||||
rescue Exception => ex
|
||||
puts ex.message
|
||||
puts ex.backtrace
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def kill_all(signal="SIGTERM")
|
||||
running_processes.each do |pid, process|
|
||||
Process.kill(signal, pid) rescue Errno::ESRCH
|
||||
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
|
||||
end
|
||||
|
||||
def error(message)
|
||||
puts "ERROR: #{message}"
|
||||
exit 1
|
||||
end
|
||||
|
||||
def longest_process_name
|
||||
@longest_process_name ||= begin
|
||||
longest = processes.keys.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
|
||||
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
|
||||
output_with_mutex name_for(pid), termination_message_for(status)
|
||||
@running.delete(pid)
|
||||
yield if block_given?
|
||||
pid
|
||||
rescue Errno::ECHILD
|
||||
end
|
||||
|
||||
def running_processes
|
||||
@running_processes ||= {}
|
||||
end
|
||||
|
||||
def next_color
|
||||
@current_color ||= -1
|
||||
@current_color += 1
|
||||
@current_color >= COLORS.length ? "" : 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
|
||||
|
||||
def read_environment_files(filenames)
|
||||
environment = {}
|
||||
|
||||
(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_]+)=(.*)\z/
|
||||
hash[$1] = $2
|
||||
end
|
||||
hash
|
||||
end
|
||||
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 }
|
||||
return if @terminating
|
||||
@terminating = true
|
||||
if Foreman.windows?
|
||||
system "sending SIGKILL to all processes"
|
||||
killall "SIGKILL"
|
||||
else
|
||||
system "sending SIGTERM to all processes"
|
||||
killall "SIGTERM"
|
||||
end
|
||||
Timeout.timeout(options[:timeout]) do
|
||||
watch_for_termination while @running.length > 0
|
||||
end
|
||||
rescue Timeout::Error
|
||||
info "sending SIGKILL to all processes"
|
||||
kill_all "SIGKILL"
|
||||
system "sending SIGKILL to all processes"
|
||||
killall "SIGKILL"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
104
lib/foreman/engine/cli.rb
Normal file
104
lib/foreman/engine/cli.rb
Normal file
@@ -0,0 +1,104 @@
|
||||
require "foreman/engine"
|
||||
|
||||
class Foreman::Engine::CLI < Foreman::Engine
|
||||
|
||||
module Color
|
||||
|
||||
ANSI = {
|
||||
:reset => 0,
|
||||
:black => 30,
|
||||
:red => 31,
|
||||
:green => 32,
|
||||
:yellow => 33,
|
||||
:blue => 34,
|
||||
:magenta => 35,
|
||||
:cyan => 36,
|
||||
:white => 37,
|
||||
:bright_black => 30,
|
||||
:bright_red => 31,
|
||||
:bright_green => 32,
|
||||
:bright_yellow => 33,
|
||||
:bright_blue => 34,
|
||||
:bright_magenta => 35,
|
||||
:bright_cyan => 36,
|
||||
:bright_white => 37,
|
||||
}
|
||||
|
||||
def self.enable(io, force=false)
|
||||
io.extend(self)
|
||||
@@color_force = force
|
||||
end
|
||||
|
||||
def color?
|
||||
return true if @@color_force
|
||||
return false if Foreman.windows?
|
||||
return false unless self.respond_to?(:isatty)
|
||||
self.isatty && ENV["TERM"]
|
||||
end
|
||||
|
||||
def color(name)
|
||||
return "" unless color?
|
||||
return "" unless ansi = ANSI[name.to_sym]
|
||||
"\e[#{ansi}m"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
FOREMAN_COLORS = %w( cyan yellow green magenta red blue bright_cyan bright_yellow
|
||||
bright_green bright_magenta bright_red bright_blue )
|
||||
|
||||
def startup
|
||||
@colors = map_colors
|
||||
proctitle "foreman: master" unless Foreman.windows?
|
||||
Color.enable($stdout, options[:color])
|
||||
end
|
||||
|
||||
def output(name, data)
|
||||
data.to_s.lines.map(&:chomp).each do |message|
|
||||
output = ""
|
||||
output += $stdout.color(@colors[name.split(".").first].to_sym)
|
||||
output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
|
||||
output += $stdout.color(:reset)
|
||||
output += message
|
||||
$stdout.puts output
|
||||
$stdout.flush
|
||||
end
|
||||
rescue Errno::EPIPE
|
||||
terminate_gracefully
|
||||
end
|
||||
|
||||
def shutdown
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def name_padding
|
||||
@name_padding ||= begin
|
||||
index_padding = @names.values.map { |n| formation[n] }.max.to_s.length + 1
|
||||
name_padding = @names.values.map { |n| n.length + index_padding }.sort.last
|
||||
[ 6, name_padding ].max
|
||||
end
|
||||
end
|
||||
|
||||
def pad_process_name(name)
|
||||
name.ljust(name_padding, " ")
|
||||
end
|
||||
|
||||
def map_colors
|
||||
colors = Hash.new("white")
|
||||
@names.values.each_with_index do |name, index|
|
||||
colors[name] = FOREMAN_COLORS[index % FOREMAN_COLORS.length]
|
||||
end
|
||||
colors["system"] = "bright_white"
|
||||
colors
|
||||
end
|
||||
|
||||
def proctitle(title)
|
||||
$0 = title
|
||||
end
|
||||
|
||||
def termtitle(title)
|
||||
printf("\033]0;#{title}\007") unless Foreman.windows?
|
||||
end
|
||||
|
||||
end
|
||||
29
lib/foreman/env.rb
Normal file
29
lib/foreman/env.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
require "foreman"
|
||||
|
||||
class Foreman::Env
|
||||
|
||||
attr_reader :entries
|
||||
|
||||
def initialize(filename)
|
||||
@entries = File.read(filename).gsub("\r\n","\n").split("\n").inject({}) do |ax, line|
|
||||
if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
|
||||
key = $1
|
||||
case val = $2
|
||||
# Remove single quotes
|
||||
when /\A'(.*)'\z/ then ax[key] = $1
|
||||
# Remove double quotes and unescape string preserving newline characters
|
||||
when /\A"(.*)"\z/ then ax[key] = $1.gsub('\n', "\n").gsub(/\\(.)/, '\1')
|
||||
else ax[key] = val
|
||||
end
|
||||
end
|
||||
ax
|
||||
end
|
||||
end
|
||||
|
||||
def entries
|
||||
@entries.each do |key, value|
|
||||
yield key, value
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,10 +1,34 @@
|
||||
require "foreman"
|
||||
require "foreman/helpers"
|
||||
require "pathname"
|
||||
|
||||
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"
|
||||
require "foreman/export/supervisord"
|
||||
require "foreman/export/launchd"
|
||||
|
||||
@@ -1,20 +1,81 @@
|
||||
require "foreman/export"
|
||||
require "foreman/utils"
|
||||
require "ostruct"
|
||||
require "pathname"
|
||||
require "shellwords"
|
||||
|
||||
class Foreman::Export::Base
|
||||
|
||||
attr_reader :location
|
||||
attr_reader :engine
|
||||
attr_reader :options
|
||||
attr_reader :formation
|
||||
|
||||
def initialize(engine)
|
||||
@engine = engine
|
||||
# deprecated
|
||||
attr_reader :port
|
||||
|
||||
def initialize(location, engine, options={})
|
||||
@location = location
|
||||
@engine = engine
|
||||
@options = options.dup
|
||||
@formation = engine.formation
|
||||
|
||||
# deprecated
|
||||
def port
|
||||
Foreman::Export::Base.warn_deprecation!
|
||||
engine.base_port
|
||||
end
|
||||
|
||||
# deprecated
|
||||
def template
|
||||
Foreman::Export::Base.warn_deprecation!
|
||||
options[:template]
|
||||
end
|
||||
|
||||
# deprecated
|
||||
def @engine.procfile
|
||||
Foreman::Export::Base.warn_deprecation!
|
||||
@processes.map do |process|
|
||||
OpenStruct.new(
|
||||
:name => @names[process],
|
||||
:process => process
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def export
|
||||
raise "export method must be overridden"
|
||||
error("Must specify a location") unless location
|
||||
FileUtils.mkdir_p(location) rescue error("Could not create: #{location}")
|
||||
FileUtils.mkdir_p(log) rescue error("Could not create: #{log}")
|
||||
FileUtils.chown(user, nil, log) rescue error("Could not chown #{log} to #{user}")
|
||||
end
|
||||
|
||||
def app
|
||||
options[:app] || "app"
|
||||
end
|
||||
|
||||
def log
|
||||
options[:log] || "/var/log/#{app}"
|
||||
end
|
||||
|
||||
def user
|
||||
options[:user] || app
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
def self.warn_deprecation!
|
||||
@@deprecation_warned ||= false
|
||||
return if @@deprecation_warned
|
||||
puts "WARNING: Using deprecated exporter interface. Please update your exporter"
|
||||
puts "the interface shown in the upstart exporter:"
|
||||
puts
|
||||
puts "https://github.com/ddollar/foreman/blob/master/lib/foreman/export/upstart.rb"
|
||||
puts "https://github.com/ddollar/foreman/blob/master/data/export/upstart/process.conf.erb"
|
||||
puts
|
||||
@@deprecation_warned = true
|
||||
end
|
||||
|
||||
def error(message)
|
||||
raise Foreman::Export::Exception.new(message)
|
||||
end
|
||||
@@ -22,20 +83,61 @@ private ######################################################################
|
||||
def say(message)
|
||||
puts "[foreman export] %s" % message
|
||||
end
|
||||
|
||||
def clean(filename)
|
||||
return unless File.exists?(filename)
|
||||
say "cleaning up: #{filename}"
|
||||
FileUtils.rm(filename)
|
||||
end
|
||||
|
||||
def export_template(exporter, file, template_root)
|
||||
def shell_quote(value)
|
||||
Shellwords.escape(value)
|
||||
end
|
||||
|
||||
# deprecated
|
||||
def old_export_template(exporter, file, template_root)
|
||||
if template_root && File.exist?(file_path = File.join(template_root, file))
|
||||
File.read(file_path)
|
||||
elsif File.exist?(file_path = File.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__))
|
||||
end
|
||||
end
|
||||
|
||||
def export_template(name, file=nil, template_root=nil)
|
||||
if file && template_root
|
||||
old_export_template name, file, template_root
|
||||
else
|
||||
name_without_first = name.split("/")[1..-1].join("/")
|
||||
matchers = []
|
||||
matchers << File.join(options[:template], name_without_first) if options[:template]
|
||||
matchers << File.expand_path("~/.foreman/templates/#{name}")
|
||||
matchers << File.expand_path("../../../../data/export/#{name}", __FILE__)
|
||||
File.read(matchers.detect { |m| File.exists?(m) })
|
||||
end
|
||||
end
|
||||
|
||||
def write_template(name, target, binding)
|
||||
compiled = ERB.new(export_template(name), nil, '-').result(binding)
|
||||
write_file target, compiled
|
||||
end
|
||||
|
||||
def chmod(mode, file)
|
||||
say "setting #{file} to mode #{mode}"
|
||||
FileUtils.chmod mode, File.join(location, file)
|
||||
end
|
||||
|
||||
def create_directory(dir)
|
||||
say "creating: #{dir}"
|
||||
FileUtils.mkdir_p(File.join(location, dir))
|
||||
end
|
||||
|
||||
def write_file(filename, contents)
|
||||
say "writing: #{filename}"
|
||||
|
||||
filename = File.join(location, filename) unless Pathname.new(filename).absolute?
|
||||
|
||||
File.open(filename, "w") do |file|
|
||||
file.puts contents
|
||||
end
|
||||
|
||||
@@ -3,27 +3,10 @@ require "foreman/export"
|
||||
|
||||
class Foreman::Export::Bluepill < Foreman::Export::Base
|
||||
|
||||
def export(location, options={})
|
||||
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]
|
||||
|
||||
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
|
||||
|
||||
def export
|
||||
super
|
||||
clean "#{location}/#{app}.pill"
|
||||
write_template "bluepill/master.pill.erb", "#{app}.pill", binding
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -2,36 +2,40 @@ 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
|
||||
error("Must specify a location") unless location
|
||||
|
||||
inittab = []
|
||||
inittab << "# ----- foreman #{app} processes -----"
|
||||
|
||||
engine.processes.values.inject(1) do |index, process|
|
||||
1.upto(concurrency[process.name]) do |num|
|
||||
index = 1
|
||||
engine.each_process do |name, process|
|
||||
1.upto(engine.formation[name]) do |num|
|
||||
id = app.slice(0, 2).upcase + sprintf("%02d", index)
|
||||
port = engine.port_for(process, num, options[:port])
|
||||
inittab << "#{id}:4:respawn:/bin/su - #{user} -c 'PORT=#{port} #{process.command} >> #{log_root}/#{process.name}-#{num}.log 2>&1'"
|
||||
port = engine.port_for(process, num)
|
||||
|
||||
commands = []
|
||||
commands << "cd #{engine.root}"
|
||||
commands << "export PORT=#{port}"
|
||||
engine.env.each_pair do |var, env|
|
||||
commands << "export #{var.upcase}=#{shell_quote(env)}"
|
||||
end
|
||||
commands << "#{process.command} >> #{log}/#{name}-#{num}.log 2>&1"
|
||||
|
||||
inittab << "#{id}:4:respawn:/bin/su - #{user} -c '#{commands.join(";")}'"
|
||||
index += 1
|
||||
end
|
||||
index
|
||||
end
|
||||
|
||||
inittab << "# ----- end foreman #{app} processes -----"
|
||||
|
||||
inittab = inittab.join("\n") + "\n"
|
||||
|
||||
if fname
|
||||
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
|
||||
if location == "-"
|
||||
puts inittab
|
||||
else
|
||||
say "writing: #{location}"
|
||||
File.open(location, "w") { |file| file.puts inittab }
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
17
lib/foreman/export/launchd.rb
Normal file
17
lib/foreman/export/launchd.rb
Normal file
@@ -0,0 +1,17 @@
|
||||
require "erb"
|
||||
require "foreman/export"
|
||||
|
||||
class Foreman::Export::Launchd < Foreman::Export::Base
|
||||
|
||||
def export
|
||||
super
|
||||
engine.each_process do |name, process|
|
||||
1.upto(engine.formation[name]) do |num|
|
||||
port = engine.port_for(process, num)
|
||||
command_args = process.command.split(" ")
|
||||
write_template "launchd/launchd.plist.erb", "#{app}-#{name}-#{num}.plist", binding
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
34
lib/foreman/export/runit.rb
Normal file
34
lib/foreman/export/runit.rb
Normal file
@@ -0,0 +1,34 @@
|
||||
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
|
||||
super
|
||||
|
||||
engine.each_process do |name, process|
|
||||
1.upto(engine.formation[name]) do |num|
|
||||
process_directory = "#{app}-#{name}-#{num}"
|
||||
|
||||
create_directory process_directory
|
||||
create_directory "#{process_directory}/env"
|
||||
create_directory "#{process_directory}/log"
|
||||
|
||||
write_template "runit/run.erb", "#{process_directory}/run", binding
|
||||
chmod 0755, "#{process_directory}/run"
|
||||
|
||||
port = engine.port_for(process, num)
|
||||
engine.env.merge("PORT" => port.to_s).each do |key, value|
|
||||
write_file "#{process_directory}/env/#{key}", value
|
||||
end
|
||||
|
||||
write_template "runit/log/run.erb", "#{process_directory}/log/run", binding
|
||||
chmod 0755, "#{process_directory}/log/run"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
16
lib/foreman/export/supervisord.rb
Normal file
16
lib/foreman/export/supervisord.rb
Normal file
@@ -0,0 +1,16 @@
|
||||
require "erb"
|
||||
require "foreman/export"
|
||||
|
||||
class Foreman::Export::Supervisord < Foreman::Export::Base
|
||||
|
||||
def export
|
||||
super
|
||||
|
||||
Dir["#{location}/#{app}*.conf"].each do |file|
|
||||
clean file
|
||||
end
|
||||
|
||||
write_template "supervisord/app.conf.erb", "#{app}.conf", binding
|
||||
end
|
||||
|
||||
end
|
||||
@@ -3,40 +3,23 @@ require "foreman/export"
|
||||
|
||||
class Foreman::Export::Upstart < Foreman::Export::Base
|
||||
|
||||
def export(location, options={})
|
||||
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]
|
||||
def export
|
||||
super
|
||||
|
||||
Dir["#{location}/#{app}*.conf"].each do |file|
|
||||
say "cleaning up: #{file}"
|
||||
FileUtils.rm(file)
|
||||
clean file
|
||||
end
|
||||
|
||||
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
|
||||
write_template "upstart/master.conf.erb", "#{app}.conf", binding
|
||||
|
||||
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
|
||||
engine.each_process do |name, process|
|
||||
next if engine.formation[name] < 1
|
||||
write_template "upstart/process_master.conf.erb", "#{app}-#{name}.conf", binding
|
||||
|
||||
process_template = export_template("upstart", "process.conf.erb", template_root)
|
||||
|
||||
engine.processes.values.each do |process|
|
||||
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])
|
||||
process_config = ERB.new(process_template).result(binding)
|
||||
write_file "#{location}/#{app}-#{process.name}-#{num}.conf", process_config
|
||||
1.upto(engine.formation[name]) do |num|
|
||||
port = engine.port_for(process, num)
|
||||
write_template "upstart/process.conf.erb", "#{app}-#{name}-#{num}.conf", binding
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
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
|
||||
@@ -2,13 +2,124 @@ require "foreman"
|
||||
|
||||
class Foreman::Process
|
||||
|
||||
attr_reader :name
|
||||
attr_reader :command
|
||||
attr_accessor :color
|
||||
attr_reader :env
|
||||
|
||||
def initialize(name, command)
|
||||
@name = name
|
||||
# Create a Process
|
||||
#
|
||||
# @param [String] command The command to run
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options [String] :cwd (./) Change to this working directory before executing the process
|
||||
# @option options [Hash] :env ({}) Environment variables to set for this process
|
||||
#
|
||||
def initialize(command, options={})
|
||||
@command = command
|
||||
@options = options.dup
|
||||
|
||||
@options[:env] ||= {}
|
||||
end
|
||||
|
||||
# Get environment-expanded command for a +Process+
|
||||
#
|
||||
# @param [Hash] custom_env ({}) Environment variables to merge with defaults
|
||||
#
|
||||
# @return [String] The expanded command
|
||||
#
|
||||
def expanded_command(custom_env={})
|
||||
env = @options[:env].merge(custom_env)
|
||||
expanded_command = command.dup
|
||||
env.each do |key, val|
|
||||
expanded_command.gsub!("$#{key}", val)
|
||||
end
|
||||
expanded_command
|
||||
end
|
||||
|
||||
# Run a +Process+
|
||||
#
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options :env ({}) Environment variables to set for this execution
|
||||
# @option options :output ($stdout) The output stream
|
||||
#
|
||||
# @returns [Fixnum] pid The +pid+ of the process
|
||||
#
|
||||
def run(options={})
|
||||
env = @options[:env].merge(options[:env] || {})
|
||||
output = options[:output] || $stdout
|
||||
|
||||
if Foreman.windows?
|
||||
Dir.chdir(cwd) do
|
||||
Process.spawn env, expanded_command(env), :out => output, :err => output
|
||||
end
|
||||
elsif Foreman.jruby_18?
|
||||
require "posix/spawn"
|
||||
wrapped_command = "#{Foreman.runner} -d '#{cwd}' -p -- #{command}"
|
||||
POSIX::Spawn.spawn env, wrapped_command, :out => output, :err => output
|
||||
elsif Foreman.ruby_18?
|
||||
fork do
|
||||
$stdout.reopen output
|
||||
$stderr.reopen output
|
||||
env.each { |k,v| ENV[k] = v }
|
||||
wrapped_command = "#{Foreman.runner} -d '#{cwd}' -p -- #{command}"
|
||||
Kernel.exec wrapped_command
|
||||
end
|
||||
else
|
||||
wrapped_command = "#{Foreman.runner} -d '#{cwd}' -p -- #{command}"
|
||||
Process.spawn env, wrapped_command, :out => output, :err => output
|
||||
end
|
||||
end
|
||||
|
||||
# Exec a +Process+
|
||||
#
|
||||
# @param [Hash] options
|
||||
#
|
||||
# @option options :env ({}) Environment variables to set for this execution
|
||||
#
|
||||
# @return Does not return
|
||||
def exec(options={})
|
||||
env = @options[:env].merge(options[:env] || {})
|
||||
env.each { |k, v| ENV[k] = v }
|
||||
Dir.chdir(cwd)
|
||||
Kernel.exec expanded_command(env)
|
||||
end
|
||||
|
||||
# Send a signal to this +Process+
|
||||
#
|
||||
# @param [String] signal The signal to send
|
||||
#
|
||||
def kill(signal)
|
||||
if Foreman.windows?
|
||||
pid && Process.kill(signal, pid)
|
||||
else
|
||||
pid && Process.kill("-#{signal}", pid)
|
||||
end
|
||||
rescue Errno::ESRCH
|
||||
false
|
||||
end
|
||||
|
||||
# Test whether or not this +Process+ is still running
|
||||
#
|
||||
# @returns [Boolean]
|
||||
#
|
||||
def alive?
|
||||
kill(0)
|
||||
end
|
||||
|
||||
# Test whether or not this +Process+ has terminated
|
||||
#
|
||||
# @returns [Boolean]
|
||||
#
|
||||
def dead?
|
||||
!alive?
|
||||
end
|
||||
|
||||
# Returns the working directory for this +Process+
|
||||
#
|
||||
# @returns [String]
|
||||
#
|
||||
def cwd
|
||||
File.expand_path(@options[:cwd] || ".")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
92
lib/foreman/procfile.rb
Normal file
92
lib/foreman/procfile.rb
Normal file
@@ -0,0 +1,92 @@
|
||||
require "foreman"
|
||||
|
||||
# Reads and writes Procfiles
|
||||
#
|
||||
# A valid Procfile entry is captured by this regex:
|
||||
#
|
||||
# /^([A-Za-z0-9_]+):\s*(.+)$/
|
||||
#
|
||||
# All other lines are ignored.
|
||||
#
|
||||
class Foreman::Procfile
|
||||
|
||||
# Initialize a Procfile
|
||||
#
|
||||
# @param [String] filename (nil) An optional filename to read from
|
||||
#
|
||||
def initialize(filename=nil)
|
||||
@entries = []
|
||||
load(filename) if filename
|
||||
end
|
||||
|
||||
# Yield each +Procfile+ entry in order
|
||||
#
|
||||
def entries(&blk)
|
||||
@entries.each do |(name, command)|
|
||||
yield name, command
|
||||
end
|
||||
end
|
||||
|
||||
# Retrieve a +Procfile+ command by name
|
||||
#
|
||||
# @param [String] name The name of the Procfile entry to retrieve
|
||||
#
|
||||
def [](name)
|
||||
@entries.detect { |n,c| name == n }.last
|
||||
end
|
||||
|
||||
# Create a +Procfile+ entry
|
||||
#
|
||||
# @param [String] name The name of the +Procfile+ entry to create
|
||||
# @param [String] command The command of the +Procfile+ entry to create
|
||||
#
|
||||
def []=(name, command)
|
||||
delete name
|
||||
@entries << [name, command]
|
||||
end
|
||||
|
||||
# Remove a +Procfile+ entry
|
||||
#
|
||||
# @param [String] name The name of the +Procfile+ entry to remove
|
||||
#
|
||||
def delete(name)
|
||||
@entries.reject! { |n,c| name == n }
|
||||
end
|
||||
|
||||
# Load a Procfile from a file
|
||||
#
|
||||
# @param [String] filename The filename of the +Procfile+ to load
|
||||
#
|
||||
def load(filename)
|
||||
@entries.replace parse(filename)
|
||||
end
|
||||
|
||||
# Save a Procfile to a file
|
||||
#
|
||||
# @param [String] filename Save the +Procfile+ to this file
|
||||
#
|
||||
def save(filename)
|
||||
File.open(filename, 'w') do |file|
|
||||
file.puts self.to_s
|
||||
end
|
||||
end
|
||||
|
||||
# Get the +Procfile+ as a +String+
|
||||
#
|
||||
def to_s
|
||||
@entries.map do |name, command|
|
||||
[ name, command ].join(": ")
|
||||
end.join("\n")
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def parse(filename)
|
||||
File.read(filename).gsub("\r\n","\n").split("\n").map do |line|
|
||||
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
|
||||
[$1, $2]
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,15 +0,0 @@
|
||||
require "foreman"
|
||||
|
||||
class Foreman::Utils
|
||||
|
||||
def self.parse_concurrency(concurrency)
|
||||
@concurrency ||= begin
|
||||
pairs = concurrency.to_s.gsub(/\s/, "").split(",")
|
||||
pairs.inject(Hash.new(1)) do |hash, pair|
|
||||
process, amount = pair.split("=")
|
||||
hash.update(process => amount.to_i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,5 +1,5 @@
|
||||
module Foreman
|
||||
|
||||
VERSION = "0.23.0"
|
||||
VERSION = "0.61.0"
|
||||
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "FOREMAN" "1" "September 2011" "Foreman 0.21.0" "Foreman Manual"
|
||||
.TH "FOREMAN" "1" "October 2012" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBforeman\fR \- manage Procfile\-based applications
|
||||
@@ -10,10 +10,13 @@
|
||||
\fBforeman start [process]\fR
|
||||
.
|
||||
.br
|
||||
\fBforeman run <command>\fR
|
||||
.
|
||||
.br
|
||||
\fBforeman export <format> [location]\fR
|
||||
.
|
||||
.SH "DESCRIPTION"
|
||||
\fBForeman\fR 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\.
|
||||
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\.
|
||||
.
|
||||
.SH "RUNNING"
|
||||
\fBforeman start\fR is used to run your application directly from the command line\.
|
||||
@@ -32,9 +35,24 @@ The following options control how the application is run:
|
||||
Specify the number of each process type to run\. The value passed in should be in the format \fBprocess=num,process=num\fR
|
||||
.
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-env\fR
|
||||
Specify one or more \.env files to load
|
||||
.
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-procfile\fR
|
||||
Specify an alternate Procfile to load, implies \fB\-d\fR at the Procfile root\.
|
||||
.
|
||||
.TP
|
||||
\fB\-p\fR, \fB\-\-port\fR
|
||||
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
|
||||
.
|
||||
.TP
|
||||
\fB\-t\fR, \fB\-\-tmux\fR
|
||||
Runs the processes in a tmux session\. Creates one window for each process and an extra window containing the output of each window (requires gawk)\.
|
||||
.
|
||||
.P
|
||||
\fBforeman run\fR is used to run one\-off commands using the same environment as your defined processes\.
|
||||
.
|
||||
.SH "EXPORTING"
|
||||
\fBforeman export\fR is used to export your application to another process management format\.
|
||||
.
|
||||
@@ -61,15 +79,23 @@ Specify the directory to place process logs in\.
|
||||
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
|
||||
.
|
||||
.TP
|
||||
\fB\-t\fR, \fB\-\-template\fR
|
||||
Specify an alternate template to use for creating export files\. See \fIhttps://github\.com/ddollar/foreman/tree/master/data/export\fR for examples\.
|
||||
.
|
||||
.TP
|
||||
\fB\-u\fR, \fB\-\-user\fR
|
||||
Specify the user the application should be run as\. Defaults to the app name
|
||||
.
|
||||
.SH "OPTIONS"
|
||||
.SH "GLOBAL OPTIONS"
|
||||
These options control all modes of foreman\'s operation\.
|
||||
.
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-root\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
|
||||
@@ -85,6 +111,9 @@ bluepill
|
||||
inittab
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
runit
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
upstart
|
||||
.
|
||||
.IP "" 0
|
||||
@@ -132,7 +161,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
|
||||
.
|
||||
@@ -165,7 +194,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
|
||||
|
||||
@@ -4,11 +4,12 @@ foreman(1) -- manage Procfile-based applications
|
||||
## SYNOPSIS
|
||||
|
||||
`foreman start [process]`<br>
|
||||
`foreman run <command>`<br>
|
||||
`foreman export <format> [location]`
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
**Foreman** is a manager for Procfile-based applications. Its aim is to
|
||||
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.
|
||||
@@ -29,10 +30,23 @@ The following options control how the application is run:
|
||||
Specify the number of each process type to run. The value passed in
|
||||
should be in the format `process=num,process=num`
|
||||
|
||||
* `-e`, `--env`:
|
||||
Specify one or more .env files to load
|
||||
|
||||
* `-f`, `--procfile`:
|
||||
Specify an alternate Procfile to load, implies `-d` at the Procfile root.
|
||||
|
||||
* `-p`, `--port`:
|
||||
Specify which port to use as the base for this application. Should be
|
||||
a multiple of 1000.
|
||||
|
||||
* `-t`, `--tmux`:
|
||||
Runs the processes in a tmux session. Creates one window for each process
|
||||
and an extra window containing the output of each window (requires gawk).
|
||||
|
||||
`foreman run` is used to run one-off commands using the same environment
|
||||
as your defined processes.
|
||||
|
||||
## EXPORTING
|
||||
|
||||
`foreman export` is used to export your application to another process
|
||||
@@ -58,16 +72,25 @@ The following options control how the application is run:
|
||||
Specify which port to use as the base for this application. Should be
|
||||
a multiple of 1000.
|
||||
|
||||
* `-t`, `--template`:
|
||||
Specify an alternate template to use for creating export files.
|
||||
See <https://github.com/ddollar/foreman/tree/master/data/export> for examples.
|
||||
|
||||
* `-u`, `--user`:
|
||||
Specify the user the application should be run as. Defaults to the
|
||||
app name
|
||||
|
||||
## OPTIONS
|
||||
## GLOBAL OPTIONS
|
||||
|
||||
These options control all modes of foreman's operation.
|
||||
|
||||
* `-d`, `--root`:
|
||||
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
|
||||
@@ -82,6 +105,8 @@ foreman currently supports the following output formats:
|
||||
|
||||
* inittab
|
||||
|
||||
* runit
|
||||
|
||||
* upstart
|
||||
|
||||
## INITTAB EXPORT
|
||||
@@ -112,7 +137,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
|
||||
|
||||
@@ -131,7 +157,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,12 +1,25 @@
|
||||
require "spec_helper"
|
||||
require "foreman/cli"
|
||||
|
||||
describe "Foreman::CLI" do
|
||||
describe "Foreman::CLI", :fakefs do
|
||||
subject { Foreman::CLI.new }
|
||||
|
||||
describe ".foreman" do
|
||||
before { File.open(".foreman", "w") { |f| f.puts "formation: alpha=2" } }
|
||||
|
||||
it "provides default options" do
|
||||
subject.send(:options)["formation"].should == "alpha=2"
|
||||
end
|
||||
|
||||
it "is overridden by options at the cli" do
|
||||
subject = Foreman::CLI.new([], :formation => "alpha=3")
|
||||
subject.send(:options)["formation"].should == "alpha=3"
|
||||
end
|
||||
end
|
||||
|
||||
describe "start" do
|
||||
describe "with a non-existent Procfile" do
|
||||
it "prints an error" do
|
||||
describe "when a Procfile doesnt exist", :fakefs do
|
||||
it "displays an error" do
|
||||
mock_error(subject, "Procfile does not exist.") do
|
||||
dont_allow.instance_of(Foreman::Engine).start
|
||||
subject.start
|
||||
@@ -14,70 +27,69 @@ describe "Foreman::CLI" do
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Procfile" do
|
||||
before(:each) { write_procfile }
|
||||
|
||||
it "runs successfully" do
|
||||
dont_allow(subject).error
|
||||
mock.instance_of(Foreman::Engine).start
|
||||
subject.start
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "export" do
|
||||
describe "with a non-existent Procfile" do
|
||||
it "prints an error" do
|
||||
mock_error(subject, "Procfile does not exist.") do
|
||||
dont_allow.instance_of(Foreman::Engine).export
|
||||
subject.export("testapp")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Procfile" do
|
||||
before(:each) { write_procfile }
|
||||
|
||||
describe "with an invalid formatter" do
|
||||
it "prints an error" do
|
||||
mock_error(subject, "Unknown export format: invalidformatter.") do
|
||||
subject.export("invalidformatter")
|
||||
end
|
||||
describe "with a valid Procfile" do
|
||||
it "can run a single command" do
|
||||
without_fakefs do
|
||||
output = foreman("start env -f #{resource_path("Procfile")}")
|
||||
output.should =~ /env.1/
|
||||
output.should_not =~ /test.1/
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a valid config" do
|
||||
before(:each) { write_foreman_config("testapp") }
|
||||
|
||||
it "runs successfully" do
|
||||
dont_allow(subject).error
|
||||
mock.instance_of(Foreman::Export::Upstart).export("/tmp/foo", {})
|
||||
subject.export("upstart", "/tmp/foo")
|
||||
it "can run all commands" do
|
||||
without_fakefs do
|
||||
output = foreman("start -f #{resource_path("Procfile")} -e #{resource_path(".env")}")
|
||||
output.should =~ /echo.1 \| echoing/
|
||||
output.should =~ /env.1 \| bar/
|
||||
output.should =~ /test.1 \| testing/
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "check" do
|
||||
describe "with a valid Procfile" do
|
||||
before { write_procfile }
|
||||
|
||||
it "displays the jobs" do
|
||||
mock(subject).display("valid procfile detected (alpha, bravo)")
|
||||
subject.check
|
||||
end
|
||||
it "with a valid Procfile displays the jobs" do
|
||||
write_procfile
|
||||
foreman("check").should == "valid procfile detected (alpha, bravo)\n"
|
||||
end
|
||||
|
||||
describe "with a blank Procfile" do
|
||||
before do
|
||||
FileUtils.touch("Procfile")
|
||||
end
|
||||
it "with a blank Procfile displays an error" do
|
||||
FileUtils.touch "Procfile"
|
||||
foreman("check").should == "ERROR: no processes defined\n"
|
||||
end
|
||||
|
||||
it "displays an error" do
|
||||
mock_error(subject, "no processes defined") do
|
||||
subject.check
|
||||
end
|
||||
end
|
||||
it "without a Procfile displays an error" do
|
||||
FileUtils.rm_f "Procfile"
|
||||
foreman("check").should == "ERROR: Procfile does not exist.\n"
|
||||
end
|
||||
end
|
||||
|
||||
describe "run" do
|
||||
it "can run a command" do
|
||||
forked_foreman("run echo 1").should == "1\n"
|
||||
end
|
||||
|
||||
it "includes the environment" do
|
||||
forked_foreman("run #{resource_path("bin/env FOO")} -e #{resource_path(".env")}").should == "bar\n"
|
||||
end
|
||||
|
||||
it "can run a command from the Procfile" do
|
||||
forked_foreman("run -f #{resource_path("Procfile")} test").should == "testing\n"
|
||||
end
|
||||
|
||||
it "exits with the same exit code as the command" do
|
||||
fork_and_get_exitstatus("run echo 1").should == 0
|
||||
fork_and_get_exitstatus("run date 'invalid_date'").should == 1
|
||||
end
|
||||
end
|
||||
|
||||
describe "version" do
|
||||
it "displays gem version" do
|
||||
foreman("version").chomp.should == Foreman::VERSION
|
||||
end
|
||||
|
||||
it "displays gem version on shortcut command" do
|
||||
foreman("-v").chomp.should == Foreman::VERSION
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,107 +1,112 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
|
||||
describe "Foreman::Engine" do
|
||||
subject { Foreman::Engine.new("Procfile", {}) }
|
||||
class Foreman::Engine::Tester < Foreman::Engine
|
||||
attr_reader :buffer
|
||||
|
||||
def startup
|
||||
@buffer = ""
|
||||
end
|
||||
|
||||
def output(name, data)
|
||||
@buffer += "#{name}: #{data}"
|
||||
end
|
||||
|
||||
def shutdown
|
||||
end
|
||||
end
|
||||
|
||||
describe "Foreman::Engine", :fakefs do
|
||||
subject do
|
||||
write_procfile "Procfile"
|
||||
Foreman::Engine::Tester.new.load_procfile("Procfile")
|
||||
end
|
||||
|
||||
describe "initialize" do
|
||||
describe "without an existing Procfile" do
|
||||
it "raises an error" do
|
||||
lambda { subject }.should raise_error
|
||||
end
|
||||
end
|
||||
|
||||
describe "with a Procfile" do
|
||||
before { write_procfile }
|
||||
|
||||
it "reads the processes" do
|
||||
subject.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.process("alpha").command.should == "./alpha"
|
||||
subject.process("bravo").command.should == "./bravo"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "start" do
|
||||
it "forks the processes" do
|
||||
write_procfile
|
||||
mock(subject).fork(subject.processes["alpha"])
|
||||
mock(subject).fork(subject.processes["bravo"])
|
||||
mock(subject.process("alpha")).run(anything)
|
||||
mock(subject.process("bravo")).run(anything)
|
||||
mock(subject).watch_for_output
|
||||
mock(subject).watch_for_termination
|
||||
subject.start
|
||||
end
|
||||
|
||||
it "handles concurrency" do
|
||||
write_procfile
|
||||
engine = Foreman::Engine.new("Procfile",:concurrency => "alpha=2")
|
||||
mock(engine).fork_individual(engine.processes["alpha"], 1, 5000)
|
||||
mock(engine).fork_individual(engine.processes["alpha"], 2, 5001)
|
||||
mock(engine).fork_individual(engine.processes["bravo"], 1, 5100)
|
||||
mock(engine).watch_for_termination
|
||||
engine.start
|
||||
subject.options[:formation] = "alpha=2"
|
||||
mock(subject.process("alpha")).run(anything).twice
|
||||
mock(subject.process("bravo")).run(anything).never
|
||||
mock(subject).watch_for_output
|
||||
mock(subject).watch_for_termination
|
||||
subject.start
|
||||
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")
|
||||
describe "directories" do
|
||||
it "has the directory default relative to the Procfile" do
|
||||
write_procfile "/some/app/Procfile"
|
||||
engine = Foreman::Engine.new.load_procfile("/some/app/Procfile")
|
||||
engine.root.should == "/some/app"
|
||||
end
|
||||
end
|
||||
|
||||
describe "environment" do
|
||||
|
||||
before(:each) do
|
||||
write_procfile
|
||||
stub(Process).fork
|
||||
end
|
||||
|
||||
it "should read if specified" do
|
||||
it "should read env files" do
|
||||
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
|
||||
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
|
||||
stub(engine).info
|
||||
mock(engine).watch_for_termination
|
||||
engine.environment.should == {"FOO"=>"baz"}
|
||||
engine.execute("alpha")
|
||||
subject.load_env("/tmp/env")
|
||||
subject.env["FOO"].should == "baz"
|
||||
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).watch_for_termination
|
||||
engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" }
|
||||
engine.execute("alpha")
|
||||
subject.load_env "/tmp/env1"
|
||||
subject.load_env "/tmp/env2"
|
||||
subject.env["FOO"].should == "bar"
|
||||
subject.env["BAZ"].should == "qux"
|
||||
end
|
||||
|
||||
it "should handle quoted values" do
|
||||
File.open("/tmp/env", "w") do |f|
|
||||
f.puts 'FOO=bar'
|
||||
f.puts 'BAZ="qux"'
|
||||
f.puts "FRED='barney'"
|
||||
f.puts 'OTHER="escaped\"quote"'
|
||||
end
|
||||
subject.load_env "/tmp/env"
|
||||
subject.env["FOO"].should == "bar"
|
||||
subject.env["BAZ"].should == "qux"
|
||||
subject.env["FRED"].should == "barney"
|
||||
subject.env["OTHER"].should == 'escaped"quote'
|
||||
end
|
||||
|
||||
it "should handle multiline strings" do
|
||||
File.open("/tmp/env", "w") do |f|
|
||||
f.puts 'FOO="bar\nbaz"'
|
||||
end
|
||||
subject.load_env "/tmp/env"
|
||||
subject.env["FOO"].should == "bar\nbaz"
|
||||
end
|
||||
|
||||
it "should fail if specified and doesnt exist" do
|
||||
mock.instance_of(Foreman::Engine).error("No such file: /tmp/env")
|
||||
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
|
||||
lambda { subject.load_env "/tmp/env" }.should raise_error(Errno::ENOENT)
|
||||
end
|
||||
|
||||
it "should read .env if none specified" do
|
||||
File.open(".env", "w") { |f| f.puts("FOO=qoo") }
|
||||
engine = Foreman::Engine.new("Procfile")
|
||||
stub(engine).info
|
||||
mock(engine).watch_for_termination
|
||||
mock(engine).fork_individual(anything, anything, anything)
|
||||
engine.environment.should == {"FOO"=>"qoo"}
|
||||
engine.execute("bravo")
|
||||
it "should set port from .env if specified" do
|
||||
File.open("/tmp/env", "w") { |f| f.puts("PORT=9000") }
|
||||
subject.load_env "/tmp/env"
|
||||
subject.send(:base_port).should == 9000
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
19
spec/foreman/export/base_spec.rb
Normal file
19
spec/foreman/export/base_spec.rb
Normal file
@@ -0,0 +1,19 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
require "foreman/export"
|
||||
|
||||
describe "Foreman::Export::Base", :fakefs do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||
let(:location) { "/tmp/init" }
|
||||
let(:engine) { Foreman::Engine.new().load_procfile(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 "raises errors as a Foreman::Export::Exception" do
|
||||
lambda { subject.send(:error, "foo") }.should raise_error(Foreman::Export::Exception, "foo")
|
||||
end
|
||||
end
|
||||
@@ -3,18 +3,35 @@ require "foreman/engine"
|
||||
require "foreman/export/bluepill"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Bluepill 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) }
|
||||
describe Foreman::Export::Bluepill, :fakefs do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||
let(:formation) { nil }
|
||||
let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(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")
|
||||
|
||||
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
|
||||
|
||||
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 a process formation" do
|
||||
let(:formation) { "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(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||
let(:location) { "/tmp/inittab" }
|
||||
let(:formation) { nil }
|
||||
let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(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(:formation) { "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
|
||||
31
spec/foreman/export/launchd_spec.rb
Normal file
31
spec/foreman/export/launchd_spec.rb
Normal file
@@ -0,0 +1,31 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
require "foreman/export/launchd"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Launchd, :fakefs do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||
let(:options) { Hash.new }
|
||||
let(:engine) { Foreman::Engine.new().load_procfile(procfile) }
|
||||
let(:launchd) { Foreman::Export::Launchd.new("/tmp/init", engine, options) }
|
||||
|
||||
before(:each) { load_export_templates_into_fakefs("launchd") }
|
||||
before(:each) { stub(launchd).say }
|
||||
|
||||
it "exports to the filesystem" do
|
||||
launchd.export
|
||||
File.read("/tmp/init/app-alpha-1.plist").should == example_export_file("launchd/launchd-a.default")
|
||||
File.read("/tmp/init/app-bravo-1.plist").should == example_export_file("launchd/launchd-b.default")
|
||||
end
|
||||
|
||||
context "with multiple command arguments" do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", "charlie") }
|
||||
|
||||
it "splits each command argument" do
|
||||
launchd.export
|
||||
File.read("/tmp/init/app-alpha-1.plist").should == example_export_file("launchd/launchd-c.default")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
36
spec/foreman/export/runit_spec.rb
Normal file
36
spec/foreman/export/runit_spec.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
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(:formation => "alpha=2,bravo=1").load_procfile(procfile) }
|
||||
let(:options) { Hash.new }
|
||||
let(:runit) { Foreman::Export::Runit.new('/tmp/init', engine, options) }
|
||||
|
||||
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
|
||||
engine.env["BAR"] = "baz"
|
||||
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
|
||||
36
spec/foreman/export/supervisord_spec.rb
Normal file
36
spec/foreman/export/supervisord_spec.rb
Normal file
@@ -0,0 +1,36 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
require "foreman/export/supervisord"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Supervisord, :fakefs do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||
let(:formation) { nil }
|
||||
let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(procfile) }
|
||||
let(:options) { Hash.new }
|
||||
let(:supervisord) { Foreman::Export::Supervisord.new("/tmp/init", engine, options) }
|
||||
|
||||
before(:each) { load_export_templates_into_fakefs("supervisord") }
|
||||
before(:each) { stub(supervisord).say }
|
||||
|
||||
it "exports to the filesystem" do
|
||||
supervisord.export
|
||||
File.read("/tmp/init/app.conf").should == example_export_file("supervisord/app-alpha-1.conf")
|
||||
end
|
||||
|
||||
it "cleans up if exporting into an existing dir" do
|
||||
mock(FileUtils).rm("/tmp/init/app.conf")
|
||||
supervisord.export
|
||||
supervisord.export
|
||||
end
|
||||
|
||||
context "with concurrency" do
|
||||
let(:formation) { "alpha=2" }
|
||||
|
||||
it "exports to the filesystem with concurrency" do
|
||||
supervisord.export
|
||||
File.read("/tmp/init/app.conf").should == example_export_file("supervisord/app-alpha-2.conf")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -3,51 +3,84 @@ require "foreman/engine"
|
||||
require "foreman/export/upstart"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Upstart 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) }
|
||||
describe Foreman::Export::Upstart, :fakefs do
|
||||
let(:procfile) { write_procfile("/tmp/app/Procfile") }
|
||||
let(:formation) { nil }
|
||||
let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(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
|
||||
|
||||
it "quotes and escapes environment variables" do
|
||||
engine.env['KEY'] = 'd"\|d'
|
||||
upstart.export
|
||||
"foobarfoo".should include "bar"
|
||||
File.read("/tmp/init/app-alpha-1.conf").should =~ /KEY=d\\"\\\\\\\|d/
|
||||
end
|
||||
|
||||
context "with a formation" do
|
||||
let(:formation) { "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(:template) { "/tmp/alternate" }
|
||||
let(:options) { { :app => "app", :template => template } }
|
||||
|
||||
before do
|
||||
FileUtils.mkdir_p template_root
|
||||
File.open("#{template_root}/master.conf.erb", "w") { |f| f.puts "alternate_template" }
|
||||
FileUtils.mkdir_p template
|
||||
File.open("#{template}/master.conf.erb", "w") { |f| f.puts "alternate_template" }
|
||||
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")}
|
||||
|
||||
before do
|
||||
FileUtils.mkdir_p default_template_root
|
||||
File.open("#{default_template_root}/master.conf.erb", "w") { |f| f.puts "default_alternate_template" }
|
||||
FileUtils.mkdir_p File.expand_path("~/.foreman/templates/upstart")
|
||||
File.open(File.expand_path("~/.foreman/templates/upstart/master.conf.erb"), "w") do |file|
|
||||
file.puts "default_alternate_template"
|
||||
end
|
||||
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
|
||||
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,48 @@
|
||||
require "spec_helper"
|
||||
require "foreman/process"
|
||||
require 'spec_helper'
|
||||
require 'foreman/process'
|
||||
require 'ostruct'
|
||||
require 'timeout'
|
||||
require 'tmpdir'
|
||||
|
||||
describe Foreman::Process do
|
||||
|
||||
def run(process, options={})
|
||||
rd, wr = IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
|
||||
process.run(options.merge(:output => wr))
|
||||
rd.gets
|
||||
end
|
||||
|
||||
describe "#run" do
|
||||
|
||||
it "runs the process" do
|
||||
process = Foreman::Process.new(resource_path("bin/test"))
|
||||
run(process).should == "testing\n"
|
||||
end
|
||||
|
||||
it "can set environment" do
|
||||
process = Foreman::Process.new(resource_path("bin/env FOO"), :env => { "FOO" => "bar" })
|
||||
run(process).should == "bar\n"
|
||||
end
|
||||
|
||||
it "can set per-run environment" do
|
||||
process = Foreman::Process.new(resource_path("bin/env FOO"))
|
||||
run(process, :env => { "FOO" => "bar "}).should == "bar\n"
|
||||
end
|
||||
|
||||
it "can handle env vars in the command" do
|
||||
process = Foreman::Process.new(resource_path("bin/echo $FOO"), :env => { "FOO" => "bar" })
|
||||
run(process).should == "bar\n"
|
||||
end
|
||||
|
||||
it "can handle per-run env vars in the command" do
|
||||
process = Foreman::Process.new(resource_path("bin/echo $FOO"))
|
||||
run(process, :env => { "FOO" => "bar" }).should == "bar\n"
|
||||
end
|
||||
|
||||
it "should output utf8 properly" do
|
||||
process = Foreman::Process.new(resource_path("bin/utf8"))
|
||||
run(process).should == "\xFF\x03\n"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
41
spec/foreman/procfile_spec.rb
Normal file
41
spec/foreman/procfile_spec.rb
Normal file
@@ -0,0 +1,41 @@
|
||||
require 'spec_helper'
|
||||
require 'foreman/procfile'
|
||||
require 'pathname'
|
||||
require 'tmpdir'
|
||||
|
||||
describe Foreman::Procfile, :fakefs do
|
||||
subject { Foreman::Procfile.new }
|
||||
|
||||
it "can load from a file" do
|
||||
write_procfile
|
||||
subject.load "Procfile"
|
||||
subject["alpha"].should == "./alpha"
|
||||
subject["bravo"].should == "./bravo"
|
||||
end
|
||||
|
||||
it "loads a passed-in Procfile" do
|
||||
write_procfile
|
||||
procfile = Foreman::Procfile.new("Procfile")
|
||||
procfile["alpha"].should == "./alpha"
|
||||
procfile["bravo"].should == "./bravo"
|
||||
end
|
||||
|
||||
it "can have a process appended to it" do
|
||||
subject["charlie"] = "./charlie"
|
||||
subject["charlie"].should == "./charlie"
|
||||
end
|
||||
|
||||
it "can write to a string" do
|
||||
subject["foo"] = "./foo"
|
||||
subject["bar"] = "./bar"
|
||||
subject.to_s.should == "foo: ./foo\nbar: ./bar"
|
||||
end
|
||||
|
||||
it "can write to a file" do
|
||||
subject["foo"] = "./foo"
|
||||
subject["bar"] = "./bar"
|
||||
subject.save "/tmp/proc"
|
||||
File.read("/tmp/proc").should == "foo: ./foo\nbar: ./bar\n"
|
||||
end
|
||||
|
||||
end
|
||||
@@ -8,4 +8,9 @@ describe Foreman do
|
||||
it { should be_a String }
|
||||
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
|
||||
4
spec/resources/Procfile
Normal file
4
spec/resources/Procfile
Normal file
@@ -0,0 +1,4 @@
|
||||
echo: bin/echo echoing
|
||||
env: bin/env FOO
|
||||
test: bin/test
|
||||
utf8: bin/utf8
|
||||
2
spec/resources/bin/echo
Executable file
2
spec/resources/bin/echo
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
echo $*
|
||||
2
spec/resources/bin/env
Executable file
2
spec/resources/bin/env
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/bash
|
||||
echo ${!1}
|
||||
2
spec/resources/bin/test
Executable file
2
spec/resources/bin/test
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
echo "testing"
|
||||
2
spec/resources/bin/utf8
Executable file
2
spec/resources/bin/utf8
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env ruby
|
||||
puts "\xff\x03"
|
||||
49
spec/resources/export/bluepill/app-concurrency.pill
Normal file
49
spec/resources/export/bluepill/app-concurrency.pill
Normal file
@@ -0,0 +1,49 @@
|
||||
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.stop_grace_time = 45.seconds
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
children.stop_command "kill {{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.stop_grace_time = 45.seconds
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-alpha-2.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
children.stop_command "kill {{PID}}"
|
||||
end
|
||||
|
||||
process.group = "app-alpha"
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
@@ -11,53 +11,34 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
|
||||
|
||||
process.working_dir = "/tmp/app"
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "5000"}
|
||||
process.environment = {"PORT"=>"5000"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
process.stop_grace_time = 45.seconds
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
children.stop_command "kill -QUIT {{PID}}"
|
||||
children.stop_command "kill {{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
|
||||
|
||||
|
||||
|
||||
|
||||
app.process("bravo-1") do |process|
|
||||
process.start_command = "./bravo"
|
||||
|
||||
process.working_dir = "/tmp/app"
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT" => "5100"}
|
||||
process.environment = {"PORT"=>"5100"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
process.stop_grace_time = 45.seconds
|
||||
|
||||
process.stdout = process.stderr = "/var/log/app/app-bravo-1.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
children.stop_command "kill -QUIT {{PID}}"
|
||||
children.stop_command "kill {{PID}}"
|
||||
end
|
||||
|
||||
|
||||
process.group = "app-bravo"
|
||||
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 'cd /tmp/app;export PORT=5000;./alpha >> /var/log/app/alpha-1.log 2>&1'
|
||||
AP02:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5001;./alpha >> /var/log/app/alpha-2.log 2>&1'
|
||||
# ----- 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 'cd /tmp/app;export PORT=5000;./alpha >> /var/log/app/alpha-1.log 2>&1'
|
||||
AP02:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5100;./bravo >> /var/log/app/bravo-1.log 2>&1'
|
||||
# ----- end foreman app processes -----
|
||||
29
spec/resources/export/launchd/launchd-a.default
Normal file
29
spec/resources/export/launchd/launchd-a.default
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>app-alpha-1</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PORT</key>
|
||||
<string>5000</string>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>./alpha</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/var/log/app/app-alpha-1.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/log/app/app-alpha-1.log</string>
|
||||
<key>UserName</key>
|
||||
<string>app</string>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/tmp/app</string>
|
||||
</dict>
|
||||
</plist>
|
||||
29
spec/resources/export/launchd/launchd-b.default
Normal file
29
spec/resources/export/launchd/launchd-b.default
Normal file
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>app-bravo-1</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PORT</key>
|
||||
<string>5100</string>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>./bravo</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/var/log/app/app-bravo-1.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/log/app/app-bravo-1.log</string>
|
||||
<key>UserName</key>
|
||||
<string>app</string>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/tmp/app</string>
|
||||
</dict>
|
||||
</plist>
|
||||
30
spec/resources/export/launchd/launchd-c.default
Normal file
30
spec/resources/export/launchd/launchd-c.default
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Label</key>
|
||||
<string>app-alpha-1</string>
|
||||
<key>EnvironmentVariables</key>
|
||||
<dict>
|
||||
<key>PORT</key>
|
||||
<string>5000</string>
|
||||
</dict>
|
||||
<key>ProgramArguments</key>
|
||||
<array>
|
||||
<string>./alpha</string>
|
||||
<string>charlie</string>
|
||||
</array>
|
||||
<key>KeepAlive</key>
|
||||
<true/>
|
||||
<key>RunAtLoad</key>
|
||||
<true/>
|
||||
<key>StandardOutPath</key>
|
||||
<string>/var/log/app/app-alpha-1.log</string>
|
||||
<key>StandardErrorPath</key>
|
||||
<string>/var/log/app/app-alpha-1.log</string>
|
||||
<key>UserName</key>
|
||||
<string>app</string>
|
||||
<key>WorkingDirectory</key>
|
||||
<string>/tmp/app</string>
|
||||
</dict>
|
||||
</plist>
|
||||
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
|
||||
24
spec/resources/export/supervisord/app-alpha-1.conf
Normal file
24
spec/resources/export/supervisord/app-alpha-1.conf
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
[program:app-alpha-1]
|
||||
command=./alpha
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopsignal=QUIT
|
||||
stdout_logfile=/var/log/app/alpha-1.log
|
||||
stderr_logfile=/var/log/app/alpha-1.error.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5000
|
||||
[program:app-bravo-1]
|
||||
command=./bravo
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopsignal=QUIT
|
||||
stdout_logfile=/var/log/app/bravo-1.log
|
||||
stderr_logfile=/var/log/app/bravo-1.error.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5100
|
||||
|
||||
[group:app]
|
||||
programs=app-alpha-1,app-bravo-1
|
||||
24
spec/resources/export/supervisord/app-alpha-2.conf
Normal file
24
spec/resources/export/supervisord/app-alpha-2.conf
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
[program:app-alpha-1]
|
||||
command=./alpha
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopsignal=QUIT
|
||||
stdout_logfile=/var/log/app/alpha-1.log
|
||||
stderr_logfile=/var/log/app/alpha-1.error.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5000
|
||||
[program:app-alpha-2]
|
||||
command=./alpha
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stopsignal=QUIT
|
||||
stdout_logfile=/var/log/app/alpha-2.log
|
||||
stderr_logfile=/var/log/app/alpha-2.error.log
|
||||
user=app
|
||||
directory=/tmp/app
|
||||
environment=PORT=5001
|
||||
|
||||
[group:app]
|
||||
programs=app-alpha-1,app-alpha-2
|
||||
@@ -6,3 +6,11 @@ bash << "EOF"
|
||||
EOF
|
||||
|
||||
end script
|
||||
|
||||
start on (started network-interface
|
||||
or started network-manager
|
||||
or started networking)
|
||||
|
||||
stop on (stopping network-interface
|
||||
or stopping network-manager
|
||||
or stopping networking)
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
require "rubygems"
|
||||
require "simplecov"
|
||||
SimpleCov.start do
|
||||
add_filter "/spec/"
|
||||
end
|
||||
|
||||
require "rspec"
|
||||
require "timecop"
|
||||
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 +21,47 @@ def mock_error(subject, message)
|
||||
end
|
||||
end
|
||||
|
||||
def foreman(args)
|
||||
capture_stdout do
|
||||
begin
|
||||
Foreman::CLI.start(args.split(" "))
|
||||
rescue SystemExit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def forked_foreman(args)
|
||||
rd, wr = IO.pipe("BINARY")
|
||||
Process.spawn("bundle exec bin/foreman #{args}", :out => wr, :err => wr)
|
||||
wr.close
|
||||
rd.read
|
||||
end
|
||||
|
||||
def fork_and_capture(&blk)
|
||||
rd, wr = IO.pipe("BINARY")
|
||||
pid = fork do
|
||||
rd.close
|
||||
wr.sync = true
|
||||
$stdout.reopen wr
|
||||
$stderr.reopen wr
|
||||
blk.call
|
||||
$stdout.flush
|
||||
$stdout.close
|
||||
end
|
||||
wr.close
|
||||
Process.wait pid
|
||||
buffer = ""
|
||||
until rd.eof?
|
||||
buffer += rd.gets
|
||||
end
|
||||
end
|
||||
|
||||
def fork_and_get_exitstatus(args)
|
||||
pid = Process.spawn("bundle exec bin/foreman #{args}", :out => "/dev/null", :err => "/dev/null")
|
||||
Process.wait(pid)
|
||||
$?.exitstatus
|
||||
end
|
||||
|
||||
def mock_exit(&block)
|
||||
block.should raise_error(SystemExit)
|
||||
end
|
||||
@@ -24,37 +74,84 @@ 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:\t./bravo"
|
||||
end
|
||||
File.expand_path(procfile)
|
||||
end
|
||||
|
||||
def load_export_templates_into_fakefs(type)
|
||||
FakeFS.deactivate!
|
||||
files = Dir[File.expand_path("../../data/export/#{type}/**", __FILE__)].inject({}) do |hash, file|
|
||||
hash.update(file => File.read(file))
|
||||
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 without_fakefs
|
||||
FakeFS.deactivate!
|
||||
ret = yield
|
||||
FakeFS.activate!
|
||||
files.each do |filename, contents|
|
||||
ret
|
||||
end
|
||||
|
||||
def load_export_templates_into_fakefs(type)
|
||||
without_fakefs do
|
||||
Dir[File.expand_path("../../data/export/#{type}/**/*", __FILE__)].inject({}) do |hash, file|
|
||||
next(hash) if File.directory?(file)
|
||||
hash.update(file => File.read(file))
|
||||
end
|
||||
end.each do |filename, contents|
|
||||
FileUtils.mkdir_p File.dirname(filename)
|
||||
File.open(filename, "w") do |f|
|
||||
f.puts contents
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def resource_path(filename)
|
||||
File.expand_path("../resources/#{filename}", __FILE__)
|
||||
end
|
||||
|
||||
def example_export_file(filename)
|
||||
FakeFS.deactivate!
|
||||
data = File.read(File.expand_path("../resources/export/#{filename}", __FILE__))
|
||||
data = File.read(File.expand_path(resource_path("export/#{filename}"), __FILE__))
|
||||
FakeFS.activate!
|
||||
data
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
def capture_stdout
|
||||
old_stdout = $stdout.dup
|
||||
rd, wr = IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
|
||||
$stdout = wr
|
||||
yield
|
||||
wr.close
|
||||
rd.read
|
||||
ensure
|
||||
$stdout = old_stdout
|
||||
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
|
||||
|
||||
112
tasks/dist.rake
Normal file
112
tasks/dist.rake
Normal file
@@ -0,0 +1,112 @@
|
||||
require "erb"
|
||||
require "fileutils"
|
||||
require "tmpdir"
|
||||
|
||||
def assemble(source, target, perms=0644)
|
||||
FileUtils.mkdir_p(File.dirname(target))
|
||||
File.open(target, "w") do |f|
|
||||
f.puts ERB.new(File.read(source)).result(binding)
|
||||
end
|
||||
File.chmod(perms, target)
|
||||
end
|
||||
|
||||
def assemble_distribution(target_dir=Dir.pwd)
|
||||
distribution_files.each do |source|
|
||||
target = source.gsub(/^#{project_root}/, target_dir)
|
||||
FileUtils.mkdir_p(File.dirname(target))
|
||||
FileUtils.cp(source, target)
|
||||
end
|
||||
end
|
||||
|
||||
GEM_BLACKLIST = %w( bundler foreman )
|
||||
|
||||
def assemble_gems(target_dir=Dir.pwd)
|
||||
lines = %x{ cd #{project_root} && bundle show }.strip.split("\n")
|
||||
raise "error running bundler" unless $?.success?
|
||||
|
||||
%x{ env BUNDLE_WITHOUT="development:test" bundle show }.split("\n").each do |line|
|
||||
if line =~ /^ \* (.*?) \((.*?)\)/
|
||||
next if GEM_BLACKLIST.include?($1)
|
||||
puts "vendoring: #{$1}-#{$2}"
|
||||
gem_dir = %x{ bundle show #{$1} }.strip
|
||||
FileUtils.mkdir_p "#{target_dir}/vendor/gems"
|
||||
%x{ cp -R "#{gem_dir}" "#{target_dir}/vendor/gems" }
|
||||
end
|
||||
end.compact
|
||||
end
|
||||
|
||||
def beta?
|
||||
Foreman::VERSION.to_s =~ /pre/
|
||||
end
|
||||
|
||||
def clean(file)
|
||||
rm file if File.exists?(file)
|
||||
end
|
||||
|
||||
def distribution_files(type=nil)
|
||||
require "foreman/distribution"
|
||||
base_files = Foreman::Distribution.files
|
||||
type_files = type ?
|
||||
Dir[File.expand_path("../../dist/resources/#{type}/**/*", __FILE__)] : []
|
||||
base_files.concat(type_files)
|
||||
end
|
||||
|
||||
def mkchdir(dir)
|
||||
FileUtils.mkdir_p(dir)
|
||||
Dir.chdir(dir) do |dir|
|
||||
yield(File.expand_path(dir))
|
||||
end
|
||||
end
|
||||
|
||||
def pkg(filename)
|
||||
File.expand_path("../../pkg/#{filename}", __FILE__)
|
||||
end
|
||||
|
||||
def project_root
|
||||
File.expand_path("../..", __FILE__)
|
||||
end
|
||||
|
||||
def resource(name)
|
||||
File.expand_path("../../dist/resources/#{name}", __FILE__)
|
||||
end
|
||||
|
||||
def s3_connect
|
||||
return if @s3_connected
|
||||
|
||||
require "aws/s3"
|
||||
|
||||
unless ENV["DAVID_RELEASE_ACCESS"] && ENV["DAVID_RELEASE_SECRET"]
|
||||
puts "please set DAVID_RELEASE_ACCESS and DAVID_RELEASE_SECRET in your environment"
|
||||
exit 1
|
||||
end
|
||||
|
||||
AWS::S3::Base.establish_connection!(
|
||||
:access_key_id => ENV["DAVID_RELEASE_ACCESS"],
|
||||
:secret_access_key => ENV["DAVID_RELEASE_SECRET"]
|
||||
)
|
||||
|
||||
@s3_connected = true
|
||||
end
|
||||
|
||||
def store(package_file, filename, bucket="assets.foreman.io")
|
||||
s3_connect
|
||||
puts "storing: #{filename}"
|
||||
AWS::S3::S3Object.store(filename, File.open(package_file), bucket, :access => :public_read)
|
||||
end
|
||||
|
||||
def tempdir
|
||||
Dir.mktmpdir do |dir|
|
||||
Dir.chdir(dir) do
|
||||
yield(dir)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def version
|
||||
require "foreman/version"
|
||||
Foreman::VERSION
|
||||
end
|
||||
|
||||
Dir[File.expand_path("../../dist/**/*.rake", __FILE__)].each do |rake|
|
||||
import rake
|
||||
end
|
||||
65
tasks/release.rake
Normal file
65
tasks/release.rake
Normal file
@@ -0,0 +1,65 @@
|
||||
require "time"
|
||||
|
||||
desc "Build the manual"
|
||||
task :man do
|
||||
ENV['RONN_MANUAL'] = "Foreman Manual"
|
||||
ENV['RONN_ORGANIZATION'] = "Foreman #{Foreman::VERSION}"
|
||||
sh "ronn -w -s toc -r5 --markdown man/*.ronn"
|
||||
end
|
||||
|
||||
desc "Commit the manual to git"
|
||||
task "man:commit" => :man do
|
||||
sh "git add README.md"
|
||||
sh "git commit -am 'update docs' || echo 'nothing to commit'"
|
||||
end
|
||||
|
||||
desc "Generate the Github docs"
|
||||
task :pages => "man:commit" do
|
||||
sh %{
|
||||
cp man/foreman.1.html /tmp/foreman.1.html
|
||||
git checkout gh-pages
|
||||
rm ./index.html
|
||||
cp /tmp/foreman.1.html ./index.html
|
||||
git add -u index.html
|
||||
git commit -m "saving man page to github docs"
|
||||
git push origin -f gh-pages
|
||||
git checkout master
|
||||
}
|
||||
end
|
||||
|
||||
def latest_release
|
||||
latest = File.read("Changelog.md").split("\n").first.split(" ")[1]
|
||||
end
|
||||
|
||||
def newer_release
|
||||
tags = %x{ git tag --contains v#{latest_release} | grep -v pre }.split("\n").sort_by do |tag|
|
||||
Gem::Version.new(tag[1..-1])
|
||||
end
|
||||
tags[1]
|
||||
end
|
||||
|
||||
desc "Generate a Changelog"
|
||||
task :changelog do
|
||||
while release = newer_release
|
||||
entry = %x{ git show --format="%cd" #{release} | head -n 1 }
|
||||
date = Time.parse(entry.chomp).strftime("%Y-%m-%d")
|
||||
|
||||
message = "## #{release[1..-1]} (#{date})\n\n"
|
||||
message += %x{ git log --format="* %s [%an]" v#{latest_release}..#{release} }
|
||||
|
||||
changelog = File.read("Changelog.md")
|
||||
changelog = message + "\n" + changelog
|
||||
|
||||
puts release
|
||||
|
||||
File.open("Changelog.md", "w") do |file|
|
||||
file.print changelog
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc "Cut a release"
|
||||
task :release do
|
||||
Rake::Task["changelog"].invoke
|
||||
Rake::Task["pages"].invoke
|
||||
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