Compare commits
519 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1153fb0f0c | ||
|
|
f69c755d9a | ||
|
|
64f0749d16 | ||
|
|
6b77ca1e46 | ||
|
|
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 | ||
|
|
ed44a11e21 | ||
|
|
5719f4fc72 | ||
|
|
47008fb921 | ||
|
|
91edac3197 | ||
|
|
20e598abcc | ||
|
|
ec7f4a480d | ||
|
|
51376058d4 | ||
|
|
f7542083dd | ||
|
|
23124afc7e | ||
|
|
c948858f45 | ||
|
|
f358d897fa | ||
|
|
ec1b06abb5 | ||
|
|
75d4fc562d | ||
|
|
1cbb295b0d | ||
|
|
60fb125b51 | ||
|
|
69d596bb8b | ||
|
|
cbd5a6344c | ||
|
|
4230455859 | ||
|
|
b25dfee62d | ||
|
|
b73da71c9c | ||
|
|
43558758fc | ||
|
|
6da8aca609 | ||
|
|
9df93a64cc | ||
|
|
8fd9b753f4 | ||
|
|
7fc6d02e7b | ||
|
|
ddcaac8749 | ||
|
|
9db97abb10 | ||
|
|
bda6e30323 | ||
|
|
ffd77312bb | ||
|
|
a24c4ce17b | ||
|
|
cf3d7a0b12 | ||
|
|
21a041527c | ||
|
|
6ad344d214 | ||
|
|
3b8fec463d | ||
|
|
4e015b7436 | ||
|
|
2042de7732 | ||
|
|
64338c5a09 | ||
|
|
8cef71766c | ||
|
|
a2ba079665 | ||
|
|
c8d0dba1cb | ||
|
|
e8d2552caa | ||
|
|
927a57f738 | ||
|
|
f65538c1b1 | ||
|
|
470c8043af | ||
|
|
3e69b27f5f | ||
|
|
f4b1e3701b | ||
|
|
fd234b8044 | ||
|
|
f2f09554c8 | ||
|
|
6c465b4ef1 | ||
|
|
c419f8213b | ||
|
|
7f61fb61ea | ||
|
|
577a5c7c5c | ||
|
|
56940c56d9 | ||
|
|
f308ad886d | ||
|
|
55375b9bde | ||
|
|
85fcccffa8 | ||
|
|
58fc18d015 | ||
|
|
f7c9802ef7 | ||
|
|
1c00d65f29 | ||
|
|
9193a675a3 | ||
|
|
8f0b14810c | ||
|
|
124e27ed22 | ||
|
|
f34f161899 | ||
|
|
191581fe85 | ||
|
|
b98d558bed | ||
|
|
180f63624e | ||
|
|
13fd1188ad | ||
|
|
92c2b15785 | ||
|
|
4a2d7565b7 | ||
|
|
49720e0458 | ||
|
|
c4de19f4da | ||
|
|
490c8b73bf | ||
|
|
f622f43cf4 | ||
|
|
9ab701f5b9 | ||
|
|
1a5d2428b9 | ||
|
|
564aa740e1 | ||
|
|
95115f4e78 | ||
|
|
e5828f8442 | ||
|
|
f121d04bf6 | ||
|
|
3dc113fcd4 | ||
|
|
22ad6c5dd8 | ||
|
|
b117ef5b08 | ||
|
|
35474ad1a5 | ||
|
|
fc5e86b269 | ||
|
|
7f8ca7da39 | ||
|
|
31239d98d0 |
17
.gitignore
vendored
17
.gitignore
vendored
@@ -1,7 +1,10 @@
|
||||
.bundle
|
||||
coverage
|
||||
example/log/*
|
||||
man/*.?
|
||||
man/*.html
|
||||
man/*.markdown
|
||||
pkg
|
||||
/.bundle
|
||||
/.rbenv-version
|
||||
/.yardoc
|
||||
/coverage
|
||||
/example/log/*
|
||||
/man/*.html
|
||||
/man/*.markdown
|
||||
/pkg
|
||||
/tags
|
||||
/vendor
|
||||
|
||||
22
.travis.yml
Normal file
22
.travis.yml
Normal file
@@ -0,0 +1,22 @@
|
||||
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
|
||||
- ree
|
||||
282
Changelog.md
Normal file
282
Changelog.md
Normal file
@@ -0,0 +1,282 @@
|
||||
## 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]
|
||||
27
Gemfile
27
Gemfile
@@ -1,18 +1,23 @@
|
||||
source "http://rubygems.org"
|
||||
|
||||
gemspec
|
||||
|
||||
platform :mingw do
|
||||
gem "win32console", "~> 1.3.0"
|
||||
end
|
||||
|
||||
platform :jruby do
|
||||
gem "posix-spawn", "~> 0.3.6"
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem 'parka'
|
||||
gem 'aws-s3'
|
||||
gem 'rake'
|
||||
gem 'ronn'
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'fakefs', '~> 0.2.1'
|
||||
gem 'rcov', '~> 0.9.8'
|
||||
gem 'fakefs', '~> 0.3.2'
|
||||
gem 'rr', '~> 1.0.2'
|
||||
gem 'rspec', '~> 2.0.0'
|
||||
gem 'rspec', '~> 2.0'
|
||||
gem "simplecov", :require => false
|
||||
gem 'timecop'
|
||||
gem 'yard'
|
||||
end
|
||||
|
||||
gem 'json', '~> 1.5.1'
|
||||
gem 'term-ansicolor', '~> 1.0.5'
|
||||
gem 'thor', '~> 0.13.6'
|
||||
|
||||
76
Gemfile.lock
76
Gemfile.lock
@@ -1,49 +1,65 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
foreman (0.60.0)
|
||||
thor (>= 0.13.6)
|
||||
|
||||
GEM
|
||||
remote: http://rubygems.org/
|
||||
specs:
|
||||
diff-lcs (1.1.2)
|
||||
fakefs (0.2.1)
|
||||
hpricot (0.8.2)
|
||||
json (1.5.1)
|
||||
aws-s3 (0.6.2)
|
||||
builder
|
||||
mime-types
|
||||
xml-simple
|
||||
builder (3.0.0)
|
||||
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.3.1)
|
||||
rest-client
|
||||
thor
|
||||
rake (0.8.7)
|
||||
rcov (0.9.8)
|
||||
posix-spawn (0.3.6)
|
||||
rake (0.9.2.2)
|
||||
rdiscount (1.6.5)
|
||||
rest-client (1.6.0)
|
||||
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.0.1)
|
||||
rspec-core (~> 2.0.1)
|
||||
rspec-expectations (~> 2.0.1)
|
||||
rspec-mocks (~> 2.0.1)
|
||||
rspec-core (2.0.1)
|
||||
rspec-expectations (2.0.1)
|
||||
diff-lcs (>= 1.1.2)
|
||||
rspec-mocks (2.0.1)
|
||||
rspec-core (~> 2.0.1)
|
||||
rspec-expectations (~> 2.0.1)
|
||||
term-ansicolor (1.0.5)
|
||||
thor (0.13.8)
|
||||
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.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
|
||||
fakefs (~> 0.2.1)
|
||||
json (~> 1.5.1)
|
||||
parka
|
||||
aws-s3
|
||||
fakefs (~> 0.3.2)
|
||||
foreman!
|
||||
posix-spawn (~> 0.3.6)
|
||||
rake
|
||||
rcov (~> 0.9.8)
|
||||
ronn
|
||||
rr (~> 1.0.2)
|
||||
rspec (~> 2.0.0)
|
||||
term-ansicolor (~> 1.0.5)
|
||||
thor (~> 0.13.6)
|
||||
rspec (~> 2.0)
|
||||
simplecov
|
||||
timecop
|
||||
win32console (~> 1.3.0)
|
||||
yard
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
# Foreman
|
||||
|
||||
## Manual
|
||||
|
||||
See the [man page](http://ddollar.github.com/foreman) for usage.
|
||||
|
||||
## 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
|
||||
50
Rakefile
50
Rakefile
@@ -1,52 +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"
|
||||
sh "git add README.markdown"
|
||||
sh "git commit -m 'update readme' || echo 'nothing to commit'"
|
||||
end
|
||||
|
||||
desc "Generate the Github docs"
|
||||
task :pages => :man 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 "rebuilding man page"
|
||||
git push origin -f gh-pages
|
||||
git checkout master
|
||||
}
|
||||
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: ./ticker $PORT
|
||||
error : ./error
|
||||
ticker: ruby ./ticker $PORT
|
||||
error: ruby ./error
|
||||
utf8: ruby ./utf8
|
||||
spawner: ./spawner
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
$stdout.sync = true
|
||||
|
||||
puts "will error in 10s"
|
||||
sleep 5
|
||||
raise "Dying"
|
||||
|
||||
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
|
||||
@@ -1,6 +1,14 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
$stdout.sync = true
|
||||
|
||||
%w( SIGINT SIGTERM ).each do |signal|
|
||||
trap(signal) do
|
||||
puts "received #{signal} but i'm ignoring it!"
|
||||
end
|
||||
end
|
||||
|
||||
while true
|
||||
puts "tick: #{ARGV.inspect}"
|
||||
puts "tick: #{ARGV.inspect} -- FOO:#{ENV["FOO"]}"
|
||||
sleep 1
|
||||
end
|
||||
|
||||
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
|
||||
28
data/export/bluepill/master.pill.erb
Normal file
28
data/export/bluepill/master.pill.erb
Normal file
@@ -0,0 +1,28 @@
|
||||
Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/bluepill.log") do |app|
|
||||
|
||||
app.uid = "<%= user %>"
|
||||
app.gid = "<%= user %>"
|
||||
|
||||
<% 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.root %>"
|
||||
process.daemonize = true
|
||||
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 %>/<%= app %>-<%= name %>-<%= num %>.log"
|
||||
|
||||
process.monitor_children do |children|
|
||||
children.stop_command "kill {{PID}}"
|
||||
end
|
||||
|
||||
process.group = "<%= app %>-<%= name %>"
|
||||
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,6 +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
|
||||
|
||||
chdir <%= engine.directory %>
|
||||
exec su - <%= user %> -c 'export PORT=<%= port %>; <%= 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
|
||||
14
dist/gem.rake
vendored
Normal file
14
dist/gem.rake
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
file pkg("foreman-#{version}.gem") => distribution_files do |t|
|
||||
sh "gem build foreman.gemspec"
|
||||
sh "mv foreman-#{version}.gem #{t.name}"
|
||||
end
|
||||
|
||||
task "gem:build" => pkg("foreman-#{version}.gem")
|
||||
|
||||
task "gem:clean" do
|
||||
clean pkg("foreman-#{version}.gem")
|
||||
end
|
||||
|
||||
task "gem:release" => "gem:build" do |t|
|
||||
sh "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
|
||||
53
dist/pkg.rake
vendored
Normal file
53
dist/pkg.rake
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
require "erb"
|
||||
|
||||
file pkg("foreman-#{version}.pkg") => distribution_files do |t|
|
||||
tempdir do |dir|
|
||||
mkchdir("foreman") do
|
||||
assemble_distribution
|
||||
assemble_gems
|
||||
assemble resource("pkg/foreman"), "bin/foreman", 0755
|
||||
end
|
||||
|
||||
kbytes = %x{ du -ks foreman | cut -f 1 }
|
||||
num_files = %x{ find foreman | wc -l }
|
||||
|
||||
mkdir_p "pkg"
|
||||
mkdir_p "pkg/Resources"
|
||||
mkdir_p "pkg/foreman.pkg"
|
||||
|
||||
dist = File.read(resource("pkg/Distribution.erb"))
|
||||
dist = ERB.new(dist).result(binding)
|
||||
File.open("pkg/Distribution", "w") { |f| f.puts dist }
|
||||
|
||||
dist = File.read(resource("pkg/PackageInfo.erb"))
|
||||
dist = ERB.new(dist).result(binding)
|
||||
File.open("pkg/foreman.pkg/PackageInfo", "w") { |f| f.puts dist }
|
||||
|
||||
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.pkg/Bom }
|
||||
|
||||
Dir.chdir("foreman") do
|
||||
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
|
||||
|
||||
task "pkg:build" => pkg("foreman-#{version}.pkg")
|
||||
|
||||
task "pkg:clean" do
|
||||
clean pkg("foreman-#{version}.pkg")
|
||||
end
|
||||
|
||||
task "pkg:release" => "pkg:build" do |t|
|
||||
store pkg("foreman-#{version}.pkg"), "foreman/foreman-#{version}.pkg"
|
||||
store pkg("foreman-#{version}.pkg"), "foreman/foreman-beta.pkg" if beta?
|
||||
store pkg("foreman-#{version}.pkg"), "foreman/foreman.pkg" unless beta?
|
||||
end
|
||||
12
dist/resources/deb/control
vendored
Normal file
12
dist/resources/deb/control
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
Package: foreman
|
||||
Version: <%= version %>
|
||||
Section: main
|
||||
Priority: standard
|
||||
Architecture: all
|
||||
Depends: ruby1.9.1
|
||||
Maintainer: Heroku
|
||||
Description: Manage Procfile-based applications.
|
||||
Foreman is a manager for Procfile-based applications. Its aim is to
|
||||
abstract away the details of the Procfile format, and allow you to
|
||||
either run your application directly or export it to some other
|
||||
process management format.
|
||||
18
dist/resources/deb/foreman
vendored
Normal file
18
dist/resources/deb/foreman
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
#!/usr/bin/env ruby1.9.1
|
||||
|
||||
# resolve bin path, ignoring symlinks
|
||||
require "pathname"
|
||||
bin_file = Pathname.new(__FILE__).realpath
|
||||
|
||||
# add locally vendored gems to libpath
|
||||
gem_dir = File.expand_path("../../vendor/gems", bin_file)
|
||||
Dir["#{gem_dir}/**/lib"].each do |libdir|
|
||||
$:.unshift libdir
|
||||
end
|
||||
|
||||
# add self to libpath
|
||||
$:.unshift File.expand_path("../../lib", bin_file)
|
||||
|
||||
require "foreman/cli"
|
||||
|
||||
Foreman::CLI.start
|
||||
30
dist/resources/deb/heroku-release-key.txt
vendored
Normal file
30
dist/resources/deb/heroku-release-key.txt
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
Version: GnuPG v1.4.11 (Darwin)
|
||||
|
||||
mQENBE5SfAEBCADLp056ZgfdtAMXLWpEuL9zY+dIHIY5qLQcDmUivjHLVE4l3Bi3
|
||||
Mn570K0W9rfk7fHBPEO2XJEDdjk8Bg6mWTAeGjdfZgZaL+qO9NjqQ5QmVR+vgp7s
|
||||
yxJYlfY+JYTZvl/JiDWGhuPHSPggXILCMf3SpqWMHGPqe/3RAK+CHCNv/94uaoS4
|
||||
vi4HQT+k4sRceiM8WqkSRYSoc7rzdDejZn+InCYFfR56VeSFF4G4I6neZs/q5T9d
|
||||
Ty2i5d0gZLaX/Iqc+3Dy0vDKClc0HUQJ6ajDPuUqKLHFUpqyuwfJij60+C3GMi8K
|
||||
ckRPti31EPFVzq3GPHU+GqA+e9j84WHr4uJ5ABEBAAG0L0hlcm9rdSBSZWxlYXNl
|
||||
IEVuZ2luZWVyaW5nIDxyZWxlYXNlQGhlcm9rdS5jb20+iQE4BBMBAgAiBQJOUnwB
|
||||
AhsDBgsJCAcDAgYVCAIJCgsEFgIDAQIeAQIXgAAKCRDJJ+vgDxsFIChECAC9h4Ay
|
||||
Nx4AQFu85cjR9rijyBflPeVqi7Xhzd7IvLg2+kZSexlb2oidj7iVSMy+vy5tG9g9
|
||||
8Az/JqMCVjcZ7ltn60OGU8gIYpJqt6VmH3vfJBxXu/Sm9tym3UCYGVvMAN5Oq6yB
|
||||
HlQkQ8F3p0cW69PmF+fibkgo9RE0EYlBIt2rUHNilTGFS6vXGr5reFFp3/rRHq3k
|
||||
bixnUwFSqNujJgnBKDPwtSYKc4pMpnhuv88xEpLH7vU8NLXQZMitKQguV8XEmcsu
|
||||
43LXlsx5uVr239/XNW+h412gIHFDSzB/YuLWlVUXMfquC96z/wxMqWWZyskDNgr0
|
||||
WDdMgzK6CUfXSqQhuQENBE5SfAEBCADbnGKcXpdVauQpINQLtRnrT0BJIrIo1Yxv
|
||||
LQRb3G7RU+Eq6aHXwk9fSKa6nEv9RsmqiW874yODnr0d/DTUWMHT+jRvPHm1wlbE
|
||||
pGR1aPSo7GgkSUdaT6CVBN3JWZ2kVJGqohNoJMYbfVaWd/kpa/LiMFWzS8LfWT2K
|
||||
xiO2vIh4qBfeRCGR7s8rADCHuHJ0eibADrgqcRfdPrChB1JiYLeTdV4yRmSzJ7TM
|
||||
zWX7OVpGfIFLbCw9NeN65pI9ePs2mSPM7DYkhhKSXWMwJNXFzn1blOGiwAwKb48P
|
||||
a/QpE6TG3PQzbYyTTP0Td1XgKAHcprvbc89a/nAk3a+PJQ/MqvDzABEBAAGJAR8E
|
||||
GAECAAkFAk5SfAECGwwACgkQySfr4A8bBSD4mAgAnCT5WRiDl0259Px9Z9J9Wk8Z
|
||||
SxugDct2Yhzca4aw1Ou4cfaIFCDXzFlBzSJfqk0HoVhp9r2gzEPUCKnSjRDyxaMo
|
||||
wZCUtqigBua+z4NB4AWgeOl/2S06I2ki1K7pfl4piYcHtEThHamnhVPJ2Hi6HsHq
|
||||
mUU+8SxleHE4GCXmKkuvxelUq9jrhHikIkm1RoqFOPb9zV3WRy4YzVHQSYfHmfk0
|
||||
9kXlM/CS0sfNv2UKCX+5e6eFIZv0rdtpp6VEh0tsFmsIClY6Z9MX7bgp8MnUJpyk
|
||||
OeIzOzQgkb4aeT0Whl+EPcTeDZfqIhVBoNXupUanmWNppFcMngxfqG2NGi1vvQ==
|
||||
=aUAq
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
3
dist/resources/deb/postinst
vendored
Executable file
3
dist/resources/deb/postinst
vendored
Executable file
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
ln -sf /usr/local/foreman/bin/foreman /usr/bin/foreman
|
||||
23
dist/resources/pkg/Distribution.erb
vendored
Normal file
23
dist/resources/pkg/Distribution.erb
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<installer-script minSpecVersion="1.000000" authoringTool="org.ruby-lang.rake" authoringToolVersion="0.8.7">
|
||||
<title>Foreman</title>
|
||||
<options customize="never" allow-external-scripts="no"/>
|
||||
<domains enable_localSystem="true"/>
|
||||
<script><![CDATA[
|
||||
function needs_git() {
|
||||
return (system.compareVersion(system.version.ProductVersion, "10.7.0") < 0);
|
||||
}
|
||||
]]></script>
|
||||
<choices-outline>
|
||||
<line choice="git"/>
|
||||
<line choice="foreman"/>
|
||||
</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" title="foreman">
|
||||
<pkg-ref id="io.foreman.installer"/>
|
||||
</choice>
|
||||
<pkg-ref id="io.foreman.installer" installKBytes="<%= kbytes %>" version="<%= version %>" auth="Root">#foreman.pkg</pkg-ref>
|
||||
</installer-script>
|
||||
|
||||
6
dist/resources/pkg/PackageInfo.erb
vendored
Normal file
6
dist/resources/pkg/PackageInfo.erb
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
<pkg-info format-version="2" identifier="io.foreman.installer" version="<%= version %>" install-location="/usr/local/foreman" auth="root">
|
||||
<payload installKBytes="<%= kbytes %>" numberOfFiles="<%= num_files %>"/>
|
||||
<scripts>
|
||||
<postinstall file="./postinstall"/>
|
||||
</scripts>
|
||||
</pkg-info>
|
||||
15
dist/resources/pkg/foreman
vendored
Normal file
15
dist/resources/pkg/foreman
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/ruby
|
||||
|
||||
require "pathname"
|
||||
bin_file = Pathname.new(__FILE__).realpath
|
||||
|
||||
gem_dir = File.expand_path("../../vendor/gems", bin_file)
|
||||
Dir["#{gem_dir}/**/lib"].each do |libdir|
|
||||
$:.unshift libdir
|
||||
end
|
||||
|
||||
$:.unshift File.expand_path("../../lib", bin_file)
|
||||
|
||||
require "foreman/cli"
|
||||
|
||||
Foreman::CLI.start
|
||||
2
dist/resources/pkg/postinstall
vendored
Normal file
2
dist/resources/pkg/postinstall
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
ln -sf /usr/local/foreman/bin/foreman /usr/bin/foreman
|
||||
15
dist/resources/tgz/foreman
vendored
Normal file
15
dist/resources/tgz/foreman
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "pathname"
|
||||
bin_file = Pathname.new(__FILE__).realpath
|
||||
|
||||
gem_dir = File.expand_path("../vendor/gems", bin_file)
|
||||
Dir["#{gem_dir}/**/lib"].each do |libdir|
|
||||
$:.unshift libdir
|
||||
end
|
||||
|
||||
$:.unshift File.expand_path("../lib", bin_file)
|
||||
|
||||
require "foreman/cli"
|
||||
|
||||
Foreman::CLI.start
|
||||
24
dist/tgz.rake
vendored
Normal file
24
dist/tgz.rake
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
file pkg("foreman-#{version}.tgz") => distribution_files do |t|
|
||||
tempdir do |dir|
|
||||
mkchdir("foreman") do
|
||||
assemble_distribution
|
||||
assemble_gems
|
||||
rm_f "bin/foreman"
|
||||
assemble resource("tgz/foreman"), "foreman", 0755
|
||||
end
|
||||
|
||||
sh "tar czvf #{t.name} foreman"
|
||||
end
|
||||
end
|
||||
|
||||
task "tgz:build" => pkg("foreman-#{version}.tgz")
|
||||
|
||||
task "tgz:clean" do
|
||||
clean pkg("foreman-#{version}.tgz")
|
||||
end
|
||||
|
||||
task "tgz:release" => "tgz:build" do |t|
|
||||
store pkg("foreman-#{version}.tgz"), "foreman/foreman-#{version}.tgz"
|
||||
store pkg("foreman-#{version}.tgz"), "foreman/foreman-beta.tgz" if beta?
|
||||
store pkg("foreman-#{version}.tgz"), "foreman/foreman.tgz" unless beta?
|
||||
end
|
||||
@@ -1,12 +1,30 @@
|
||||
require "rubygems"
|
||||
require "parka/specification"
|
||||
$:.unshift File.expand_path("../lib", __FILE__)
|
||||
require "foreman/version"
|
||||
|
||||
Parka::Specification.new do |gem|
|
||||
Gem::Specification.new do |gem|
|
||||
gem.name = "foreman"
|
||||
gem.version = Foreman::VERSION
|
||||
gem.summary = "Process manager for applications with multiple components"
|
||||
gem.homepage = "http://github.com/ddollar/foreman"
|
||||
|
||||
gem.executables = "foreman"
|
||||
gem.files << "man/foreman.1"
|
||||
gem.author = "David Dollar"
|
||||
gem.email = "ddollar@gmail.com"
|
||||
gem.homepage = "http://github.com/ddollar/foreman"
|
||||
gem.summary = "Process manager for applications with multiple components"
|
||||
|
||||
gem.description = gem.summary
|
||||
|
||||
gem.executables = "foreman"
|
||||
gem.files = Dir["**/*"].select { |d| d =~ %r{^(README|bin/|data/|ext/|lib/|spec/|test/)} }
|
||||
gem.files << "man/foreman.1"
|
||||
|
||||
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
|
||||
|
||||
@@ -1,8 +1,23 @@
|
||||
module Foreman
|
||||
require "foreman/version"
|
||||
|
||||
VERSION = "0.11.0"
|
||||
module Foreman
|
||||
|
||||
class AppDoesNotExist < Exception; end
|
||||
|
||||
end
|
||||
def self.runner
|
||||
File.expand_path("../../bin/foreman-runner", __FILE__)
|
||||
end
|
||||
|
||||
def self.jruby?
|
||||
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java"
|
||||
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,49 +1,61 @@
|
||||
require "foreman"
|
||||
require "foreman/helpers"
|
||||
require "foreman/engine"
|
||||
require "foreman/engine/cli"
|
||||
require "foreman/export"
|
||||
require "foreman/version"
|
||||
require "shellwords"
|
||||
require "yaml"
|
||||
require "thor"
|
||||
|
||||
class Foreman::CLI < Thor
|
||||
|
||||
include Foreman::Helpers
|
||||
|
||||
map ["-v", "--version"] => :version
|
||||
|
||||
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
|
||||
class_option :root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"
|
||||
|
||||
desc "start [PROCESS]", "Start the application, or a specific process"
|
||||
desc "start [PROCESS]", "Start the application (or a specific PROCESS)"
|
||||
|
||||
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"
|
||||
|
||||
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, options)
|
||||
else
|
||||
engine.start(options)
|
||||
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 :concurrency, :type => :string, :aliases => "-c",
|
||||
:banner => '"alpha=5,bar=3"'
|
||||
method_option :template, :type => :string, :aliases => "-t"
|
||||
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
|
||||
|
||||
def export(format, location=nil)
|
||||
check_procfile!
|
||||
|
||||
formatter = case format
|
||||
when "json" then Foreman::Export::JSON
|
||||
when "inittab" then Foreman::Export::Inittab
|
||||
when "upstart" then Foreman::Export::Upstart
|
||||
else error "Unknown export format: #{format}."
|
||||
end
|
||||
|
||||
formatter.new(engine).export(location, options)
|
||||
|
||||
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
|
||||
@@ -51,38 +63,92 @@ 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)
|
||||
end
|
||||
|
||||
def procfile
|
||||
options[:procfile] || "Procfile"
|
||||
end
|
||||
|
||||
private ######################################################################
|
||||
|
||||
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") || {}
|
||||
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
9
lib/foreman/distribution.rb
Normal file
9
lib/foreman/distribution.rb
Normal file
@@ -0,0 +1,9 @@
|
||||
module Foreman
|
||||
module Distribution
|
||||
def self.files
|
||||
Dir[File.expand_path("../../../{bin,data,lib}/**/*", __FILE__)].select do |file|
|
||||
File.file?(file)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,193 +1,315 @@
|
||||
require "foreman"
|
||||
require "foreman/env"
|
||||
require "foreman/process"
|
||||
require "foreman/utils"
|
||||
require "pty"
|
||||
require "foreman/procfile"
|
||||
require "tempfile"
|
||||
require "term/ansicolor"
|
||||
require "timeout"
|
||||
require "fileutils"
|
||||
require "thread"
|
||||
|
||||
class Foreman::Engine
|
||||
|
||||
attr_reader :procfile
|
||||
attr_reader :directory
|
||||
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")
|
||||
|
||||
def initialize(procfile)
|
||||
@procfile = read_procfile(procfile)
|
||||
@directory = File.expand_path(File.dirname(procfile))
|
||||
@env = {}
|
||||
@mutex = Mutex.new
|
||||
@names = {}
|
||||
@processes = []
|
||||
@running = {}
|
||||
@readers = {}
|
||||
end
|
||||
|
||||
def processes
|
||||
@processes ||= begin
|
||||
@order = []
|
||||
procfile.split("\n").inject({}) do |hash, line|
|
||||
next if line.strip == ""
|
||||
name, command = line.split(/ *: +/, 2)
|
||||
unless command
|
||||
warn_deprecated_procfile!
|
||||
name, command = line.split(/ +/, 2)
|
||||
# Start the processes registered to this +Engine+
|
||||
#
|
||||
def start
|
||||
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.pid
|
||||
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(options={})
|
||||
proctitle "ruby: foreman master"
|
||||
|
||||
processes_in_order.each do |name, process|
|
||||
fork process, options
|
||||
end
|
||||
|
||||
trap("TERM") { puts "SIGTERM received"; kill_all("TERM") }
|
||||
trap("INT") { puts "SIGINT received"; kill_all("INT") }
|
||||
|
||||
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, options={})
|
||||
fork processes[name], options
|
||||
|
||||
trap("TERM") { puts "SIGTERM received"; kill_all("TERM") }
|
||||
trap("INT") { puts "SIGINT received"; kill_all("INT") }
|
||||
|
||||
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, options={})
|
||||
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)
|
||||
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}"
|
||||
# deprecated
|
||||
def environment
|
||||
env
|
||||
end
|
||||
|
||||
begin
|
||||
Dir.chdir directory do
|
||||
command = process.command
|
||||
private
|
||||
|
||||
PTY.spawn("#{process.command} 2>&1") do |stdin, stdout, pid|
|
||||
until stdin.eof?
|
||||
info stdin.gets, process
|
||||
### 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.select(@readers.values).first || []).each do |reader|
|
||||
data = reader.gets
|
||||
output_with_mutex name_for(@readers.invert[reader]), data
|
||||
end
|
||||
end
|
||||
end
|
||||
rescue PTY::ChildExited, Interrupt
|
||||
begin
|
||||
info "process exiting", process
|
||||
rescue Interrupt
|
||||
rescue Exception => ex
|
||||
puts ex.message
|
||||
puts ex.backtrace
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def kill_all(signal="TERM")
|
||||
info "terminating"
|
||||
running_processes.each do |pid, process|
|
||||
info "killing #{process.name} in pid #{pid}"
|
||||
Process.kill(signal, pid)
|
||||
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 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
|
||||
kill_all
|
||||
Process.waitall
|
||||
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"
|
||||
def terminate_gracefully
|
||||
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(5) do
|
||||
watch_for_termination while @running.length > 0
|
||||
end
|
||||
rescue Timeout::Error
|
||||
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 intense_cyan intense_yellow
|
||||
intense_green intense_magenta intense_red, intense_blue )
|
||||
|
||||
def startup
|
||||
@colors = map_colors
|
||||
proctitle "foreman: master" unless Foreman.windows?
|
||||
Color.enable($stdout, options[:color])
|
||||
end
|
||||
|
||||
def output(name, data)
|
||||
data.to_s.chomp.split("\n").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"] = "intense_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).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,9 +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/json"
|
||||
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,14 +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(name)
|
||||
File.read(File.expand_path("../../../../data/export/#{name}", __FILE__))
|
||||
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.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
|
||||
|
||||
12
lib/foreman/export/bluepill.rb
Normal file
12
lib/foreman/export/bluepill.rb
Normal file
@@ -0,0 +1,12 @@
|
||||
require "erb"
|
||||
require "foreman/export"
|
||||
|
||||
class Foreman::Export::Bluepill < Foreman::Export::Base
|
||||
|
||||
def export
|
||||
super
|
||||
clean "#{location}/#{app}.pill"
|
||||
write_template "bluepill/master.pill.erb", "#{app}.pill", binding
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,37 +1,41 @@
|
||||
require "foreman/export/base"
|
||||
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
|
||||
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
require "foreman/export/base"
|
||||
require "json"
|
||||
|
||||
class Foreman::Export::JSON < Foreman::Export::Base
|
||||
|
||||
def export(fname=nil, options={})
|
||||
processes = engine.processes.values.inject({}) do |hash, process|
|
||||
hash.update(process.name => { "command" => process.command })
|
||||
end
|
||||
puts processes.to_json
|
||||
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
|
||||
@@ -1,41 +1,25 @@
|
||||
require "erb"
|
||||
require "foreman/export/base"
|
||||
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}"
|
||||
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")
|
||||
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")
|
||||
|
||||
engine.processes.values.each do |process|
|
||||
process_master_template = export_template("upstart/process_master.conf.erb")
|
||||
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
|
||||
@@ -1,14 +1,126 @@
|
||||
require "foreman"
|
||||
require "rubygems"
|
||||
|
||||
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?
|
||||
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).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
|
||||
5
lib/foreman/version.rb
Normal file
5
lib/foreman/version.rb
Normal file
@@ -0,0 +1,5 @@
|
||||
module Foreman
|
||||
|
||||
VERSION = "0.60.0"
|
||||
|
||||
end
|
||||
244
man/foreman.1
Normal file
244
man/foreman.1
Normal file
@@ -0,0 +1,244 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "FOREMAN" "1" "July 2012" "Foreman 0.57.0" "Foreman Manual"
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBforeman\fR \- manage Procfile\-based applications
|
||||
.
|
||||
.SH "SYNOPSIS"
|
||||
\fBforeman start [process]\fR
|
||||
.
|
||||
.br
|
||||
\fBforeman run <command>\fR
|
||||
.
|
||||
.br
|
||||
\fBforeman export <format> [location]\fR
|
||||
.
|
||||
.SH "DESCRIPTION"
|
||||
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\.
|
||||
.
|
||||
.P
|
||||
If no additional parameters are passed, foreman will run one instance of each type of process defined in your Procfile\.
|
||||
.
|
||||
.P
|
||||
If a parameter is passed, foreman will run one instance of the specified application type\.
|
||||
.
|
||||
.P
|
||||
The following options control how the application is run:
|
||||
.
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-concurrency\fR
|
||||
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\.
|
||||
.
|
||||
.P
|
||||
An location to export can be passed as an argument\. This argument may be either required or optional depending on the export format\.
|
||||
.
|
||||
.P
|
||||
The following options control how the application is run:
|
||||
.
|
||||
.TP
|
||||
\fB\-a\fR, \fB\-\-app\fR
|
||||
Use this name rather than the application\'s root directory name as the name of the application when exporting\.
|
||||
.
|
||||
.TP
|
||||
\fB\-c\fR, \fB\-\-concurrency\fR
|
||||
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\-l\fR, \fB\-\-log\fR
|
||||
Specify the directory to place process logs in\.
|
||||
.
|
||||
.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\-\-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 "GLOBAL OPTIONS"
|
||||
These options control all modes of foreman\'s operation\.
|
||||
.
|
||||
.TP
|
||||
\fB\-d\fR, \fB\-\-directory\fR
|
||||
Specify an alternate application root\. This defaults to the directory containing the Procfile\.
|
||||
.
|
||||
.TP
|
||||
\fB\-e\fR, \fB\-\-env\fR
|
||||
Specify an alternate environment file\. You can specify more than one file by using: \fB\-\-env file1,file2\fR\.
|
||||
.
|
||||
.TP
|
||||
\fB\-f\fR, \fB\-\-procfile\fR
|
||||
Specify an alternate location for the application\'s Procfile\. This file\'s containing directory will be assumed to be the root directory of the application\.
|
||||
.
|
||||
.SH "EXPORT FORMATS"
|
||||
foreman currently supports the following output formats:
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
bluepill
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
inittab
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
runit
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
upstart
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "INITTAB EXPORT"
|
||||
Will export a chunk of inittab\-compatible configuration:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
# \-\-\-\-\- foreman example processes \-\-\-\-\-
|
||||
EX01:4:respawn:/bin/su \- example \-c \'PORT=5000 bundle exec thin start >> /var/log/web\-1\.log 2>&1\'
|
||||
EX02:4:respawn:/bin/su \- example \-c \'PORT=5100 bundle exec rake jobs:work >> /var/log/job\-1\.log 2>&1\'
|
||||
# \-\-\-\-\- end foreman example processes \-\-\-\-\-
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "UPSTART EXPORT"
|
||||
Will create a series of upstart scripts in the location you specify\. Scripts will be structured to make the following commands valid:
|
||||
.
|
||||
.P
|
||||
\fBstart appname\fR
|
||||
.
|
||||
.P
|
||||
\fBstop appname\-processname\fR
|
||||
.
|
||||
.P
|
||||
\fBrestart appname\-processname\-3\fR
|
||||
.
|
||||
.SH "PROCFILE"
|
||||
A Procfile should contain both a name for the process and the command used to run it\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
web: bundle exec thin start
|
||||
job: bundle exec rake jobs:work
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
A process name may contain letters, numbers amd the underscore character\. You can validate your Procfile format using the \fBcheck\fR command:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ foreman check
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "ENVIRONMENT"
|
||||
If a \fB\.env\fR file exists in the current directory, the default environment will be read from it\. This file should contain key/value pairs, separated by \fB=\fR, with one key/value pair per line\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
FOO=bar
|
||||
BAZ=qux
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "DEFAULT OPTIONS"
|
||||
If a \fB\.foreman\fR 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:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
concurrency: alpha=0,bravo=1
|
||||
port: 15000
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "EXAMPLES"
|
||||
Start one instance of each process type, interleave the output on stdout:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ foreman start
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Export the application in upstart format:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ foreman export upstart /etc/init
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Run one process type from the application defined in a specific Procfile:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ foreman start alpha \-p ~/myapp/Procfile
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "COPYRIGHT"
|
||||
Foreman is Copyright (C) 2010 David Dollar \fIhttp://daviddollar\.org\fR
|
||||
@@ -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,14 +72,26 @@ 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`, `--directory`:
|
||||
Specify an alternate application root. This defaults to the directory
|
||||
containing the Procfile.
|
||||
|
||||
* `-e`, `--env`:
|
||||
Specify an alternate environment file. You can specify more than one
|
||||
file by using: `--env file1,file2`.
|
||||
|
||||
* `-f`, `--procfile`:
|
||||
Specify an alternate location for the application's Procfile. This file's
|
||||
containing directory will be assumed to be the root directory of the
|
||||
@@ -75,18 +101,14 @@ These options control all modes of foreman's operation.
|
||||
|
||||
foreman currently supports the following output formats:
|
||||
|
||||
* json
|
||||
* bluepill
|
||||
|
||||
* inittab
|
||||
|
||||
* runit
|
||||
|
||||
* upstart
|
||||
|
||||
## JSON EXPORT
|
||||
|
||||
Will export your processes as JSON:
|
||||
|
||||
{ "web": { "command": "bundle exec thin start" } }
|
||||
|
||||
## INITTAB EXPORT
|
||||
|
||||
Will export a chunk of inittab-compatible configuration:
|
||||
@@ -115,10 +137,29 @@ 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
|
||||
|
||||
## ENVIRONMENT
|
||||
|
||||
If a `.env` file exists in the current directory, the default environment will
|
||||
be read from it. This file should contain key/value pairs, separated by `=`, with
|
||||
one key/value pair per line.
|
||||
|
||||
FOO=bar
|
||||
BAZ=qux
|
||||
|
||||
## DEFAULT OPTIONS
|
||||
|
||||
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,bravo=1
|
||||
port: 15000
|
||||
|
||||
## EXAMPLES
|
||||
|
||||
Start one instance of each process type, interleave the output on stdout:
|
||||
|
||||
@@ -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,64 +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
|
||||
mock(subject).fork_individual(subject.processes["alpha"], 1, 5000)
|
||||
mock(subject).fork_individual(subject.processes["alpha"], 2, 5001)
|
||||
mock(subject).fork_individual(subject.processes["bravo"], 1, 5100)
|
||||
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(:concurrency => "alpha=2")
|
||||
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
|
||||
it "should read env files" do
|
||||
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
|
||||
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") }
|
||||
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
|
||||
lambda { subject.load_env "/tmp/env" }.should raise_error(Errno::ENOENT)
|
||||
end
|
||||
|
||||
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
|
||||
37
spec/foreman/export/bluepill_spec.rb
Normal file
37
spec/foreman/export/bluepill_spec.rb
Normal file
@@ -0,0 +1,37 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
require "foreman/export/bluepill"
|
||||
require "tmpdir"
|
||||
|
||||
describe Foreman::Export::Bluepill, :fakefs do
|
||||
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
|
||||
let(: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
|
||||
normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app.pill"))
|
||||
end
|
||||
|
||||
it "cleans up if exporting into an existing dir" do
|
||||
mock(FileUtils).rm("/tmp/init/app.pill")
|
||||
|
||||
bluepill.export
|
||||
bluepill.export
|
||||
end
|
||||
|
||||
context "with 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
|
||||
@@ -1,2 +1,88 @@
|
||||
require "spec_helper"
|
||||
require "foreman/engine"
|
||||
require "foreman/export/upstart"
|
||||
require "tmpdir"
|
||||
|
||||
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
|
||||
|
||||
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-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) { "/tmp/alternate" }
|
||||
let(:options) { { :app => "app", :template => template } }
|
||||
|
||||
before do
|
||||
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
|
||||
File.read("/tmp/init/app.conf").should == "alternate_template\n"
|
||||
end
|
||||
end
|
||||
|
||||
context "with alternate templates from home dir" do
|
||||
|
||||
before do
|
||||
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
|
||||
File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
|
||||
end
|
||||
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
|
||||
1
spec/resources/.env
Normal file
1
spec/resources/.env
Normal file
@@ -0,0 +1 @@
|
||||
FOO=bar
|
||||
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
|
||||
46
spec/resources/export/bluepill/app.pill
Normal file
46
spec/resources/export/bluepill/app.pill
Normal file
@@ -0,0 +1,46 @@
|
||||
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("bravo-1") do |process|
|
||||
process.start_command = "./bravo"
|
||||
|
||||
process.working_dir = "/tmp/app"
|
||||
process.daemonize = true
|
||||
process.environment = {"PORT"=>"5100"}
|
||||
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
|
||||
process.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 {{PID}}"
|
||||
end
|
||||
|
||||
process.group = "app-bravo"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
4
spec/resources/export/inittab/inittab.concurrency
Normal file
4
spec/resources/export/inittab/inittab.concurrency
Normal file
@@ -0,0 +1,4 @@
|
||||
# ----- foreman app processes -----
|
||||
AP01:4:respawn:/bin/su - app -c '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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user