Compare commits

...

435 Commits

Author SHA1 Message Date
David Dollar
1905a7c310 Merge pull request #388 from brixen/patch-1
Update README.md
2013-09-04 16:49:36 -07:00
Brian Shirai
134b31f599 Update README.md 2013-09-04 16:47:09 -07:00
David Dollar
2b97cb458a Merge pull request #366 from wfarr/tgz-install-foreman-to-bin
Install tgz/foreman to bin/foreman in tgz package
2013-05-27 14:06:42 -07:00
Will Farrington
03d76d4254 Install tgz/foreman to bin/foreman in tgz package 2013-05-26 07:49:18 -07:00
David Dollar
133228f247 Merge pull request #305 from marclennox/master
Add start-stop-daemon export support with upstart
2013-05-03 13:27:54 -07:00
David Dollar
bfba2cad71 Merge pull request #360 from kjwierenga/feature/require-posix-spawn
Feature/require posix spawn
2013-05-03 12:55:16 -07:00
Klaas Jan Wierenga
e245026f65 Fail with an error on Ruby 1.8 when posix-spawn is not present. 2013-05-03 21:44:57 +02:00
Klaas Jan Wierenga
ffc73366b2 Require and use 'posix/spawn' when running ruby 1.8 without using Foreman.ruby_18? (which is the subject under test). 2013-05-03 21:29:17 +02:00
Marc Lennox
284503899a Added start-stop-daemon support. 2013-05-03 12:54:08 -04:00
David Dollar
144be02bbd Merge pull request #353 from austiniam/master
added support for directories with single quote
2013-05-03 07:29:17 -07:00
Austin Cherry
9734a2ed65 fixed conflicts 2013-05-03 07:25:56 -07:00
Austin
0fff148fe0 modified to use shellescape instead of gsub 2013-05-03 07:23:25 -07:00
Austin
d19a9aa043 added support for directories with single quotes. fixes #315 2013-05-03 07:22:16 -07:00
David Dollar
c3abaad353 kill the children, not self 2013-05-03 09:54:20 -04:00
David Dollar
b1f91d4505 fix docs, thanks to @Invizory 2013-05-03 09:49:31 -04:00
David Dollar
0c7b8ddd79 not sure how this snuck in, not in the exporter format 2013-05-03 09:44:25 -04:00
David Dollar
189b751d9f update docs 2013-05-03 09:44:25 -04:00
David Dollar
619bd03bb8 Merge pull request #340 from ldmosquera/set_env_var_with_process_name
Set FOREMAN_PROCESS_NAME env var for spawned procs
2013-05-03 06:42:04 -07:00
David Dollar
fd836b46c0 Merge pull request #355 from JoeyButler/fix_typo_in_man
Fix typo in manual page.
2013-05-03 06:38:42 -07:00
David Dollar
2379259b33 Merge pull request #359 from kjwierenga/feature/make-ruby-18-compatible
Fix specs that were broken on ruby 1.8. Use POSIX::Spawn.spawn to make foreman robust on ruby 1.8.7.
2013-05-03 06:32:01 -07:00
Klaas Jan Wierenga
baf842cdd4 The wrapped_command has spaces which triggers Ruby to fork a system shell (with /bin/sh -c). This causes orphaned processes on some systems (i.e. Linux). Fix this by splitting the command using String#shellsplit and using ruby's splat operator (*) to pass discrete arguments to spawn. 2013-05-02 16:30:39 +02:00
Klaas Jan Wierenga
5c06aaaa57 Enable ruby 1.8.7 building on Travis. All specs should pass now on ruby 1.8.7. 2013-05-02 12:49:05 +02:00
Klaas Jan Wierenga
75b782b664 Use POSIX::Spawn to make foreman ruby 1.8 compatible and have all specs passing. 2013-05-02 12:39:23 +02:00
Klaas Jan Wierenga
9b4bd10cdb Use pipe factory method to handle 1.8 incompatible signature for IO.pipe. 2013-05-02 12:37:06 +02:00
Klaas Jan Wierenga
fff82dc685 Ruby 1.8 doesn't have the concept of string encodings. Compare to the string as is. 2013-05-02 11:00:47 +02:00
David Dollar
da0a9f2de3 Merge pull request #294 from bfulton/master
add systemd export
2013-04-22 20:37:39 -07:00
Bright Fulton
7d77d8ff1a updated systemd export spec after rebasing included 5ef8bbdb 2013-04-20 13:48:27 -04:00
Joey Butler
51e181da29 Fix typo in manual page. 2013-04-17 10:47:03 -07:00
Leonardo Mosquera
9866a341ca Add unit test for PS env var 2013-04-16 22:31:34 -03:00
Leonardo Mosquera
90848e7dea Change FOREMAN_PROCESS_NAME to just PS 2013-04-16 22:31:15 -03:00
David Dollar
a92e24b17c changelog 2013-04-15 15:35:21 -04:00
David Dollar
0da42cf7d2 0.63.0 2013-04-15 15:33:51 -04:00
David Dollar
23561b963c fix dotenv dependency 2013-04-15 15:32:27 -04:00
David Dollar
cc51ab2cb1 fix up tests 2013-04-15 15:32:22 -04:00
David Dollar
403d40b277 Revert "Merge pull request #292 from andrewsmedina/master"
Does not pass tests

This reverts commit 17a8a316b8, reversing
changes made to 9e1d590734.
2013-04-15 15:31:38 -04:00
Bright Fulton
669a920c1e fix spec after d4ab495 2013-04-15 14:43:02 -04:00
Bright Fulton
d357197718 better default for things which intentionally daemonize child processes, the default KillMode is control-group which survives daemonization 2013-04-15 14:43:02 -04:00
bfulton
f6b57d7b92 rough draft for systemd export support
http://0pointer.de/blog/projects/systemd.html

This adds support for exporting systemd targets and services.  The
structure is based on the existing upstart support.

Quality is draft and expected to refine in the coming weeks.

One Foremanism that is not respected by these export templates is the
usual log output location, instead stdout and stderr go to syslog.
2013-04-15 14:43:02 -04:00
Austin
e79588fd40 modified to use shellescape instead of gsub 2013-04-15 08:01:24 -07:00
David Dollar
f1c2347680 Merge pull request #317 from andymorris/fix_deleted_upstart_files
Prevent upstart export from deleting similarly named upstart files
2013-04-14 08:45:21 -07:00
David Dollar
80f8242b11 Merge pull request #316 from avtobiff/add-mit-license
Add MIT license text
2013-04-14 08:43:41 -07:00
David Dollar
383276bb79 Merge pull request #343 from steakknife/fix__issue_341__profile_not_found
Fixes #341 . .profile not found error
2013-04-14 08:41:58 -07:00
David Dollar
5a7692ff2c Merge pull request #326 from pje/patch-1
Update man/foreman.1
2013-04-14 08:40:50 -07:00
David Dollar
405a85bc86 Merge pull request #351 from GreenplumChorus/master
Remove posix-spawn dependency for JRuby
2013-04-14 08:40:10 -07:00
David Dollar
bff554d554 Merge pull request #347 from bkeepers/dotenv
Replace Foreman::Env with dotenv
2013-04-14 08:37:31 -07:00
David Dollar
eaed989c75 Merge pull request #329 from kentaro/permit-underscore-in-procfile
Permit hyphen for command name in Procfile
2013-04-14 08:32:24 -07:00
David Dollar
93daebbf1b Merge pull request #323 from dplummer/remove-tmux-from-man
Remove tmux option from man page
2013-04-14 08:31:40 -07:00
David Dollar
17a8a316b8 Merge pull request #292 from andrewsmedina/master
circus support
2013-04-14 08:30:17 -07:00
David Dollar
9e1d590734 Merge pull request #266 from messick/master
Use runlevels in upstart config so app starts after boot in Ubuntu 12.03
2013-04-14 08:28:08 -07:00
Austin
6611d818b1 third time is the charm. :) 2013-04-11 09:14:01 -07:00
Austin
ad4d59ae14 fixes #315 2013-04-11 08:49:36 -07:00
Austin
434f30fe42 added support for directories with single quotes. fixes #315 2013-04-11 08:44:16 -07:00
David Dollar
1cb1c8812a Merge pull request #352 from john-griffin/master
Restore ability to trap crtl+c
2013-04-11 07:59:52 -07:00
John Griffin
6786f4df39 Revert "Ensure foreman is the process group leader"
This reverts commit 44726e377e.

Conflicts:

	lib/foreman/engine.rb
2013-04-09 17:05:16 +01:00
Andrew Brown & Corey Downing
0be08a0651 remove posix-spawn dependency as it does not work in jruby 1.7.3 2013-04-04 17:27:31 -07:00
Brandon Keepers
3abe10e5ab Replace Foreman::Env with dotenv 2013-03-22 10:38:58 -04:00
Barry Allard
66ab0f08e7 [foreman-runner] fix sourcing as . is rarely in PATH 2013-03-16 00:04:57 -07:00
Leonardo Mosquera
95a1d49e9d Set FOREMAN_PROCESS_NAME env var for spawned procs
This way, processes can identify themselves to metrics logging systems.
2013-03-14 21:34:39 -03:00
David Dollar
7be4375168 changelog 2013-03-08 14:53:56 -05:00
David Dollar
2ebb33e049 0.62.0 2013-03-08 14:52:50 -05:00
David Dollar
9fe7ddb8bd Merge pull request #334 from ged/reentrant_signal_handlers
Add deferred signal-handling (fixes #332).
2013-03-08 11:41:12 -08:00
David Dollar
f954a42ecb Merge pull request #335 from ged/20_encoding_fix
Fix spec encoding problem under Ruby 2.0.0.
2013-03-08 11:40:50 -08:00
Michael Granger
169188376b Try to allow children to shut down gracefully
Since signals will no longer be handled once foreman goes into
`terminate_gracefully`, default signal handlers are restored so as
not to cause it to get stuck in an unTERMable state.

This necessitates not using the process group for signalling
except as a last resort, as foreman itself will receive the signals
it sends. This splits `killall` into two methods, one which
signals only processes foreman itself has started, and one which
signals all processes in the process group to try to clean up
more aggressively, and then reworks `terminate_gracefully` to use
them.
2013-03-04 14:05:53 -08:00
Michael Granger
5ab08c608b Add deferred signal-handling (fixes #332).
This uses a thread-local queue and a self-pipe so the code in the
trap blocks is properly re-entrant.

For details, see:

  * http://cr.yp.to/docs/selfpipe.html
  * http://blog.rubybestpractices.com/posts/ewong/016-Implementing-Signal-Handlers.html
2013-03-04 13:28:37 -08:00
Michael Granger
0b5c1f919e Fix spec encoding problem under Ruby 2.0.0. 2013-03-04 13:03:13 -08:00
David Dollar
c94aa13b06 add ruby 2.0 to travis 2013-03-04 13:11:05 -05:00
Kentaro Kuribayashi
8d1e3a849f Fixed specs to pass. 2013-02-21 11:52:37 +09:00
Kentaro Kuribayashi
5ef8bbdbe3 Permit underscore for command name in Procfile. 2013-02-21 11:30:59 +09:00
David Dollar
4e84b92536 Merge pull request #327 from patheticpat/master
Fixed a typo in cli options description
2013-02-14 06:23:57 -08:00
Michael Kaiser
6215f8b3db Fixed a typo in cli options description 2013-02-14 14:45:44 +01:00
Patrick Ellis
8cf5896c3e Update man/foreman.1
fix man example typo: Procfile flag is `-f`, not `-p`
2013-02-05 16:29:38 -08:00
Donald Plummer
5f8032cac8 Remove tmux option from man page
Not well supported at this time. See issue #242
2013-01-30 09:29:16 -08:00
Andy Morris
9f3b903085 Prevent upstart export from deleting similarly named upstart files 2013-01-25 00:24:04 -06:00
Per Andersson
69d278d864 Add MIT license text 2013-01-22 10:10:41 +01:00
David Dollar
ba9167152c handled by mingw now 2013-01-14 08:21:40 -05:00
David Dollar
f31ff18191 update docs 2013-01-14 08:19:40 -05:00
David Dollar
c42110e438 update changelog 2013-01-14 08:19:31 -05:00
David Dollar
26bb0ed54e 0.61.0 2013-01-14 08:18:05 -05:00
David Dollar
44002953f6 Merge pull request #277 from pcasaretto/add-timeout-switch
Add timeout switch to CLI - fixes #178
2013-01-14 05:12:12 -08:00
David Dollar
bb2c3a2d04 Merge pull request #264 from asanghi/master
shared_path is set in documentation leading to early evaluation
2013-01-14 05:10:47 -08:00
David Dollar
88cdaacc67 Merge pull request #258 from mclazarus/master
Don't quote shell escaped values.
2013-01-14 05:10:36 -08:00
David Dollar
faf7b3c40f Merge pull request #279 from evanj/master
Fix --directory option: its actually --root
2013-01-14 05:10:11 -08:00
David Dollar
21dd610eaf Merge pull request #280 from BIAINC/windows/host-support
Strip Windows Line Endings
2013-01-14 05:10:01 -08:00
David Dollar
fe65c7510d Merge pull request #288 from crohr/remove-rubygems-requirement
Remove explicit requirement on rubygems.
2013-01-14 05:08:27 -08:00
David Dollar
cd2c255296 Merge pull request #291 from liveh2o/master
JRuby 1.9 doesn't require posix/spawn
2013-01-14 05:08:10 -08:00
David Dollar
92c1909217 Merge pull request #300 from mattv/output-blank-lines
Don't ignore blank lines in the output
2013-01-14 05:07:31 -08:00
David Dollar
af57bf3d52 Merge pull request #302 from ctrochalakis/process_group_fix
Ensure foreman is the process group leader
2013-01-14 05:07:21 -08:00
David Dollar
443994d3b5 Merge pull request #303 from fxposter/master
Fix for high CPU load. Fixes #260 and #299.
2013-01-14 05:06:44 -08:00
David Dollar
2faa3fb6ff Merge pull request #313 from sonots/fix_color
Fix color bug: not intense_cyan but bright_cyan
2013-01-14 05:02:52 -08:00
nseo
0d53f6bd6c fix more 2013-01-13 22:10:12 +09:00
nseo
1d2bcdbc56 fix color 2013-01-12 01:10:25 +09:00
Pavel Forkert
aceea1472a Fix #299 and #260
Some processes close their output channels and IO.select keeps
returning them as "readable", while IO#gets returns nil on them, thus
spending a lot of CPU looping through the same reader continuously
2013-01-07 02:30:56 +02:00
Christos Trochalakis
44726e377e Ensure foreman is the process group leader
Foreman should be the process leader before killing processes using
his process group id.

Before that foreman was broken when it was not spawned from a shell.
2012-12-27 15:47:02 +02:00
Matt Venables
61eca5a1d8 Don't ignore blank lines in the output
This fixes the stdout code to ensure that empty lines are outputted.
Many times, these blank lines are intentional, so foreman should not
suppress them.

This fixes #286
2012-12-21 11:12:52 -05:00
David Dollar
553ac7f81f Merge pull request #295 from petedmarsh/master
Add license to gemspec
2012-12-03 05:56:14 -08:00
petedmarsh
6790cf02a9 Add license to gemspec 2012-12-03 13:52:27 +00:00
Andrews Medina
9325f2ca6d add circus support 2012-11-29 00:15:01 -02:00
Adam Hutchison
7ad41da592 Since JRuby 1.9 doesn't require posix/spawn, only follow that path if JRuby is loaded and running in 1.8 mode. 2012-11-27 22:07:50 -07:00
Cyril Rohr
8ee7b7afdf Remove explicit requirement on rubygems.
It's better to not force the use of a package manager. Better to add it
to the global RUBYOPT if needed. Also, this fixes a dependency issue
when using the .deb package (rubygems1.9.1 is not required, and should
not be).
2012-11-12 21:10:10 +01:00
Aditya Sanghi
2620b90808 Dont use shared_path variable before multistage has a chance at it 2012-11-02 14:04:27 +05:30
Paul Morton
89bdc3ab8e Strip Windows Line Endings 2012-10-18 09:28:39 -07:00
Evan Jones
60a11eb981 Fix man page: --directory is actually --root. 2012-10-18 11:48:50 -04:00
Paulo Luis Franchini Casaretto
61c222deb8 Add timeout switch to CLI 2012-10-17 16:05:10 -03:00
David Dollar
8fe86e98c8 update docs 2012-10-08 10:47:40 -04:00
David Dollar
5c1ffdb7dc changelog 2012-10-08 10:47:21 -04:00
David Dollar
8b49256175 0.60.2 2012-10-08 10:47:00 -04:00
David Dollar
37d777f224 update docs 2012-10-08 10:46:34 -04:00
David Dollar
73fc059634 Merge pull request #273 from silviorelli/master
Fix for fix on issue #260
2012-10-08 07:46:16 -07:00
Silvio Relli
f7b7029cc0 Fix for nil value on io select loop, fixes #260 2012-10-08 16:40:37 +02:00
David Dollar
3a101ec7dd update changelog 2012-10-08 10:31:10 -04:00
David Dollar
6c931ea15e 0.60.1 2012-10-08 10:30:25 -04:00
David Dollar
d173570d98 ree is timing out 2012-10-08 10:29:37 -04:00
David Dollar
0cd41fee7f Merge pull request #272 from silviorelli/master
High cpu load patch by @mauricio - fixes #260
2012-10-08 07:29:31 -07:00
Silvio Relli
9da4e38209 Cleaner fix for high cpu load issue, fixes #260 2012-10-08 16:05:20 +02:00
Silvio Relli
5d9dfd294e Patch for high cpu load issue 2012-10-08 11:52:39 +02:00
David Dollar
8998e9a47c update docs 2012-09-25 12:39:17 -05:00
David Dollar
8238a86942 update changelog 2012-09-25 12:38:45 -05:00
David Dollar
1153fb0f0c 0.60.0 2012-09-25 12:38:01 -05:00
David Dollar
f69c755d9a Merge pull request #262 from dpiddy/foreman-run-from-procfile
foreman run can run things from the Procfile like heroku run.
2012-09-25 10:37:25 -07:00
Nick Messick
9a91e5df44 use "start|stop\ on runlevel [x]" for upstart config 2012-09-24 18:18:05 -07:00
David Dollar
64f0749d16 0.59.0 2012-09-15 11:49:46 +09:00
David Dollar
6b77ca1e46 Merge pull request #246 from jeremyevans/patch-1
Don't have foreman-runner depend on bash
2012-09-14 19:48:03 -07:00
Kevin McAllister
8fbee31a2d Remove expectation of double quotes around environment variables from
test comparisons
2012-09-14 21:33:57 -04:00
Kevin McAllister
7b4eabf0c5 Remove explicit wrapping of Shellwords.escape in double quotes
According to http://www.ruby-doc.org/stdlib-1.9.3/libdoc/shellwords/rdoc/Shellwords.html#method-c-shellescape

"Note that a resulted string should be used unquoted and is not
intended for use in double quotes nor in single quotes."
2012-09-14 21:33:57 -04:00
Jeremy Evans
6a44dd3fd3 Use /bin/sh instead of bash for foreman-runner
/bin/sh should be installed by default on all unix machines, bash
is not necessarily installed on all machines.
2012-09-14 17:35:54 -07:00
Dan Peterson
e99f3173ef foreman run can run things from the Procfile like heroku run. 2012-09-14 10:43:03 -03:00
David Dollar
6c04dab649 0.58.0 2012-09-14 18:10:05 +09:00
David Dollar
96e26e7412 dont set HOME 2012-09-14 17:44:32 +09:00
David Dollar
a2c03cc402 Merge pull request #217 from danielfarrell/capistrano
Add capistrano export support
2012-09-14 01:42:34 -07:00
David Dollar
803115c0c4 Merge pull request #226 from alachaum/master
CLI: fix directory option
2012-09-14 01:41:36 -07:00
David Dollar
a4343187ad Merge pull request #239 from indrekj/master
Use path and env variables in the inittab export
2012-09-14 01:41:18 -07:00
David Dollar
98af1f0943 Merge pull request #261 from martinisoft/launchd_environment
Launchd environment
2012-09-14 01:34:35 -07:00
David Dollar
af2d4762a8 Merge pull request #250 from szimek/249_multiline_strings
Handle multiline strings in .env file
2012-09-14 01:34:10 -07:00
Aaron Kalin
1d06124457 Add StandardOutPath to launchd export 2012-09-11 15:04:15 -05:00
Aaron Kalin
698e6ae092 Add command argument string splitting
launchd uses exec() under the hood when you specify ProgramArguments
or Program so you must break apart program arguments into separate
strings in an array or the command will fail
2012-09-10 19:10:47 -05:00
Aaron Kalin
c617ddb3b2 Cleanup launchd exporter
Also suppresses the deprecation warning for the launchd exporter when
using port instead of engine.port_for
2012-09-10 18:21:51 -05:00
Aaron Kalin
f6d4badcd2 Enable trim_mode via '-' in ERB templates
This adds whitespace supression optionally, similar to the default
behavior in Rails ActionView ERB templates.

Just add - to a %> like so: -%> and it will remove trailing whitespace
and newlines. This can also be done to the beginning tag for removing
newlines from the beginning of the tag line.
2012-09-10 18:18:56 -05:00
Aaron Kalin
f29bf49a35 Add support for setting environment variables 2012-09-05 18:27:10 -05:00
David Dollar
e06d36b27c dont test 1.8.7 or rbx 2012-08-24 12:49:35 -04:00
David Dollar
85e62dfbeb Merge pull request #254 from omarkhan/master
foreman run should exit with the same code as its command
2012-08-24 09:14:45 -07:00
Omar Khan
574e852710 foreman run should exit with the same code as its command 2012-08-24 16:50:05 +01:00
David Dollar
68f098c1d2 update docs 2012-08-21 10:59:21 -04:00
David Dollar
e8bdafdfc1 changelog 2012-08-21 10:58:58 -04:00
David Dollar
4abd3ebedb 0.57.0 2012-08-21 10:58:11 -04:00
David Dollar
f765436dde fix errant space 2012-08-21 10:58:00 -04:00
David Dollar
b5c513b4b5 Merge pull request #247 from asanghi/master
start on boot worked with network-interface but not network
2012-08-21 07:56:30 -07:00
Aditya Sanghi
ee761ff098 Another fix for PR #229 2012-08-21 20:16:28 +05:30
David Dollar
27c22deb6c update changelog 2012-08-19 12:05:37 -04:00
David Dollar
681a9f7e61 0.56.0 2012-08-19 12:05:07 -04:00
David Dollar
8335a2b1ba read .profile, not .profile.d 2012-08-16 12:57:22 -04:00
David Dollar
407425ca78 update changelog 2012-08-14 17:08:14 -04:00
David Dollar
6ca505b4cd 0.55.0 2012-08-14 17:07:52 -04:00
David Dollar
612eae5e21 use a forked process to exec a run with environment 2012-08-14 17:07:39 -04:00
David Dollar
497b5ea1eb update changelog 2012-08-14 17:03:28 -04:00
David Dollar
cd384e0d59 0.54.0 2012-08-14 17:00:19 -04:00
David Dollar
8921cac35b use Foreman::Process to extract command running 2012-08-14 17:00:01 -04:00
Szymon Nowak
7d6de5b2a7 Handle multiline strings in .env file 2012-08-14 11:09:21 +02:00
Aditya Sanghi
cc4306492e run on reboot works with network-interface not network 2012-08-12 14:17:39 +05:30
David Dollar
6042783e82 Merge pull request #245 from brntbeer/env_bash_fix
changed to check env for bash
2012-08-08 14:09:19 -07:00
brntbeer
b7b3a9f898 changed to check env for bash 2012-08-08 13:53:36 -07:00
Indrek Juhkam
8b204db2f0 Use path and env variables in the inittab export 2012-07-31 21:44:31 +03:00
David Dollar
15643dcb3f changelog 2012-07-24 11:18:42 -04:00
David Dollar
5f0f2f5378 0.53.0 2012-07-24 11:18:20 -04:00
David Dollar
c1b57b59cf put app root in $HOME 2012-07-24 11:18:05 -04:00
David Dollar
21d53818f2 add changelog 2012-07-24 11:11:04 -04:00
David Dollar
359d6f1c34 0.52.0 2012-07-24 11:10:16 -04:00
David Dollar
584f251e4a wrap command in a runner that sources .profile.d scripts 2012-07-24 11:09:54 -04:00
David Dollar
7d9c2b2ac4 fix upstart export specs 2012-07-24 10:50:34 -04:00
David Dollar
fba4d9beff Merge pull request #229 from danielfarrell/autostart-upstart
Make upstart export start/stop with network
2012-07-18 13:55:01 -07:00
Daniel Farrell
0bde5fdab5 Make upstart export start/stop with network 2012-07-18 16:50:49 -04:00
Arnaud Lachaume
ebe160d425 fixed the directory option 2012-07-14 11:48:24 +10:00
David Dollar
1beab80c1f update docs 2012-07-11 18:33:13 -04:00
David Dollar
7b270f9f4a changelog 2012-07-11 18:33:10 -04:00
David Dollar
03e5342067 0.51.0 2012-07-11 18:32:51 -04:00
David Dollar
b1d57426fb dont try to colorize windows 2012-07-11 18:30:51 -04:00
David Dollar
06e5c52f35 add mswin32 dist 2012-07-11 18:30:38 -04:00
David Dollar
64ca839c0b update docs 2012-07-11 16:17:13 -04:00
David Dollar
80aebda023 update changelog 2012-07-11 16:17:10 -04:00
David Dollar
5dee2281a2 fix up release tasks 2012-07-11 16:16:29 -04:00
David Dollar
efb6d2f11d 0.50.0 2012-07-11 16:07:52 -04:00
David Dollar
ac528d3b50 update docs 2012-07-11 16:07:03 -04:00
David Dollar
cefd4e351e handle windows 2012-07-11 16:02:07 -04:00
David Dollar
9849f4558a 0.49.0 2012-07-11 15:05:47 -04:00
David Dollar
4b53b42be1 1.8 compatibility 2012-07-11 15:05:28 -04:00
David Dollar
219acaf690 use one pgroup for all of foreman and kill that since ruby 1.8 sucks at pgroups 2012-07-11 15:05:20 -04:00
David Dollar
f8118d7b40 better debugging 2012-07-11 15:04:30 -04:00
David Dollar
7fdade277c 0.48.0 2012-07-10 15:06:22 -04:00
David Dollar
1a0943c495 allow old exporter format to work, but with deprecation warning 2012-07-10 15:06:22 -04:00
David Dollar
4a732abd77 remove debugging code 2012-07-10 15:06:22 -04:00
David Dollar
3e71fea777 Merge pull request #219 from MarkDBlackwell/patch-1
Avoid crash by verifying the existence of SIGHUP before accessing it.
2012-07-09 06:30:14 -07:00
Mark D. Blackwell
ae7aeabb63 Avoid crash by verifying the existence of SIGHUP before accessing it. 2012-07-08 16:21:27 -03:00
Daniel Farrell
0a61b1a62f Add capistrano export support 2012-06-22 15:49:19 -04:00
David Dollar
9901b9f924 0.48.0.pre3 2012-06-18 00:11:43 -04:00
David Dollar
4c2d569810 0.48.0.pre2 2012-06-18 00:11:23 -04:00
David Dollar
26f9c8186d allow color to be forced on 2012-06-18 00:11:23 -04:00
David Dollar
24ed8946f3 terminate gracefully if stdout goes away 2012-06-18 00:11:23 -04:00
David Dollar
83b2a9cc50 always flush output 2012-06-18 00:11:22 -04:00
David Dollar
a0228b9fa0 Merge pull request #212 from morgoth/added-version-command
added command for displaying foreman version
2012-06-12 09:37:07 -07:00
Wojciech Wnętrzak
b1a2a4a0cd added command for displaying foreman version 2012-06-12 17:16:35 +02:00
David Dollar
7774b7f150 Merge pull request #211 from morgoth/fixed-yaml-usage
fixed using YAML
2012-06-11 12:57:24 -07:00
Wojciech Wnętrzak
b01355a093 fixed using YAML 2012-06-11 21:42:59 +02:00
David Dollar
8b18143281 test on more things, but don't fail 2012-06-11 13:34:08 -04:00
David Dollar
e06886ed57 changelog 2012-06-11 12:34:16 -04:00
David Dollar
d6a00d7262 0.48.0.pre1 2012-06-11 12:27:20 -04:00
David Dollar
d9d1346640 foreman doesn't work on ruby 1.8, may try to fix later 2012-06-11 10:31:07 -04:00
David Dollar
0df1a4d784 use bash 2012-06-10 23:12:44 -04:00
David Dollar
51a704939e massive refactoring for programmatic control and stability 2012-06-10 22:58:09 -04:00
David Dollar
f41cc552c7 Merge pull request #164 from hsume2/master
Add support for running procfile in tmux session
2012-06-07 22:29:36 -07:00
Henry Hsu
065bbf1cd8 Only run tmux specs if tmux is installed 2012-06-07 22:14:47 -07:00
Henry Hsu
b628ddc608 Do not assume BUNDLE_GEMFILE 2012-06-07 22:12:52 -07:00
Henry Hsu
f745b16217 Add support for starting procfile in tmux session 2012-06-07 22:12:52 -07:00
David Dollar
a87a882e60 0.47.0 2012-06-07 22:49:51 -04:00
David Dollar
6ba9252d0f Merge pull request #165 from elf-pavlik/master
list of ports to other languages added to README
2012-06-07 19:45:11 -07:00
David Dollar
efd9e2119b Merge pull request #173 from Viximo/feature/escape-env
Quote and escape environment variables in upstart templates
2012-06-07 19:43:20 -07:00
David Dollar
bf9bdbf118 Merge pull request #194 from maxpow4h/master
Added launchd to exporters
2012-06-07 19:41:23 -07:00
David Dollar
929a138e54 Merge pull request #195 from aneeth/patch-1
Updated data/export/bluepill/master.pill.erb
2012-06-07 19:41:09 -07:00
David Dollar
d6514b4f33 Merge pull request #201 from sos4nt/patch-1
Terminate gracefully upon SIGHUP
2012-06-07 19:40:38 -07:00
David Dollar
8696a36833 Merge pull request #208 from dbrock/master
Fix multi-word argument handling in `foreman run`.
2012-06-06 07:48:44 -07:00
Daniel Brockman
3ea5de42aa Fix multi-word argument handling in foreman run. 2012-06-06 15:36:59 +02:00
David Dollar
4a13122082 Merge pull request #199 from atog/master
set port from .env if specified
2012-05-16 15:06:56 -07:00
Koen Van der Auwera
9b2987c3f0 make 'PORT=5000 foreman start' work 2012-05-16 23:59:09 +02:00
Stefan Schüßler
6274f99225 Terminate gracefully upon SIGHUP
Tmux sends SIGHUP when a session is killed which can result in orphaned processes. Adding a SIGHUP handler terminates the processes as expected.
2012-05-14 13:04:46 +03:00
Koen Van der Auwera
0b0324fed9 set port from .env if specified 2012-05-11 14:17:03 +02:00
Aneeth
32db70b778 Updated data/export/bluepill/master.pill.erb to read in the environment variables from the foreman .env file and reflect it in the pill file 2012-05-04 15:53:16 +08:00
Maxwell Swadling
3db1ad6fbc Added launchd exporter 2012-05-04 15:21:16 +10:00
David Dollar
003b466a17 Merge pull request #193 from pat2man/patch-1
Add stop_grace_time to bluepill config
2012-05-03 12:16:23 -07:00
Patrick Tescher
2a896a0fb5 Add stop_grace_time to bluepill config. Fixes this error message:
Config Error: Stop_grace_time should be greater than the sum of stop_signals delays!

Changed bluepill spec example files to include stop_grace_time
2012-05-03 12:11:24 -07:00
David Dollar
6a8c81a38b Revert "Merge pull request #192 from pat2man/patch-1"
This reverts commit a83dab363e, reversing
changes made to e0fe5baf1b.
2012-05-03 14:53:31 -04:00
David Dollar
a83dab363e Merge pull request #192 from pat2man/patch-1
Add stop_grace_time to bluepill config
2012-05-03 11:52:02 -07:00
Patrick Tescher
53e0f4ecf9 Add stop_grace_time to bluepill config. Fixes this error message:
Config Error: Stop_grace_time should be greater than the sum of stop_signals delays!
2012-05-03 11:50:29 -07:00
David Dollar
e0fe5baf1b update docs 2012-05-02 13:21:02 -04:00
David Dollar
1aa1f15b8f update changelog 2012-05-02 13:20:54 -04:00
David Dollar
fa46a605bb 0.46.0 2012-05-02 13:19:11 -04:00
David Dollar
3077857ab7 Merge pull request #191 from ged/profile_load_write_append
Add Procfile load/write/append API
2012-05-02 10:03:31 -07:00
David Dollar
08aa8f9d5d Merge pull request #190 from ged/remove_obsolete_spec
Remove spec obsoleted by 91a8704
2012-05-02 10:01:30 -07:00
Michael Granger
771489dec9 Remove spec obsoleted by 91a8704 2012-05-02 09:37:32 -07:00
Michael Granger
053ae8f0be Add Profile load/write/append API 2012-05-02 09:30:57 -07:00
David Dollar
af58af1c60 Merge pull request #187 from bkaney/master
Guard against missing Procfile in engine.rb
2012-04-29 21:00:58 -07:00
David Dollar
9075d93370 update docs 2012-04-26 17:50:03 -04:00
David Dollar
0c2e9df722 changelog 2012-04-26 17:50:00 -04:00
David Dollar
047f1ff5c4 0.45.0 2012-04-26 17:49:35 -04:00
David Dollar
26b54a62c5 Merge pull request #188 from technomancy/upstart-log-dir
Create and chown log dir in upstart export.
2012-04-24 17:50:42 -07:00
Phil Hagelberg
7b85ad7c1a Create and chown log dir in upstart export. 2012-04-24 17:24:53 -07:00
Brian Kaney
91a87049db Guard against missing Procfile in engine.rb
Should be able to 'foreman run <task>' without a Procfile.  This seems true to the orig. intent as the cli does not directly check with 'run' (as it  does for 'start').
2012-04-24 15:51:14 -04:00
David Dollar
f46408e8be remove parka from dist files 2012-04-23 16:00:27 -04:00
David Dollar
93f04e42ac update docs 2012-04-23 15:57:06 -04:00
David Dollar
97c4582acc changelog 2012-04-23 15:57:00 -04:00
David Dollar
5689d75a87 0.44.0 2012-04-23 15:55:56 -04:00
David Dollar
1325b6750e make var output order repeatable 2012-04-23 15:53:59 -04:00
David Dollar
638005403f clean up file/directory interaction, add some tests 2012-04-23 15:50:23 -04:00
David Dollar
55f274532f Merge pull request #183 from technomancy/app_root
Look for .env and app_root in the same dir as the Procfile.
2012-04-23 12:20:10 -07:00
Phil Hagelberg
66f76c2036 Look for .env and app_root in the same dir as the Procfile. 2012-04-20 20:59:37 -07:00
David Dollar
865cabb525 update docs 2012-04-20 19:33:14 -04:00
David Dollar
7c3c4bc58f add more options to the start docs 2012-04-20 19:33:11 -04:00
David Dollar
2dbe8c733b update docs 2012-04-20 17:42:47 -04:00
David Dollar
84352b82cc changelog 2012-04-20 17:42:33 -04:00
David Dollar
383c1f87af 0.43.0 2012-04-20 17:41:08 -04:00
David Dollar
ce3003b026 factor out wrap_environment 2012-04-20 17:40:53 -04:00
David Dollar
e06f4b2f9e Merge pull request #182 from leahpar/master
Supervisord Export fix for environments containing commas
2012-04-20 14:36:26 -07:00
Raphael Randschau
b721fd894e per default wrap every environment value in quotes 2012-04-20 21:15:43 +02:00
Raphael Randschau
b8ea6fd4b3 fix typo in spec description 2012-04-20 09:14:12 +02:00
Raphael Randschau
dbda63263b fix supervisord export for environments containing commas 2012-04-20 09:11:25 +02:00
David Dollar
93cdc31be0 update docs 2012-04-18 13:04:45 -04:00
David Dollar
4dfbe46690 update changelog 2012-04-18 13:03:58 -04:00
David Dollar
0033f9caeb 0.42.0 2012-04-18 13:02:51 -04:00
David Dollar
74839800a9 Merge pull request #181 from technomancy/master
Public read_environment
2012-04-18 10:01:38 -07:00
Phil Hagelberg
b75337e21e Move read_environment to a public class method. 2012-04-18 09:56:17 -07:00
David Dollar
d94f941189 update gemfile.lock 2012-04-18 12:49:33 -04:00
David Dollar
0b34f067cb Revert "Merge pull request #176 from rtyler/feature/156-foreman-stop-command"
Rewriting using a better Process API

This reverts commit dbe51832b0, reversing
changes made to 69216b4c5e.
2012-04-18 12:48:51 -04:00
David Dollar
dbe51832b0 Merge pull request #176 from rtyler/feature/156-foreman-stop-command
Adding #stop method to Foreman::Engine
2012-04-11 10:29:54 -07:00
R. Tyler Croy
3a2a53be95 Remove ALL_PROCESSES and default the name arguments to #start/#stop to nil 2012-04-11 10:25:58 -07:00
R. Tyler Croy
b2bf95479e Refactor #stop to reference ALL_PROCESSES for a bit clearer readability 2012-04-10 20:35:59 -07:00
R. Tyler Croy
48f764e347 Refactor #spawn_processes into #start(name)
When passed nil (aka ALL_PROCESSES) #start will start all processes in the
Procfile as per existing behavior
2012-04-10 20:35:59 -07:00
R. Tyler Croy
de62d0655e Re-name the main Foreman::Engine method to #run to avoid a name collision with #start(name) 2012-04-10 20:35:59 -07:00
R. Tyler Croy
38aecff886 When executing #stop(nil), all processes should be sent the signal 2012-04-10 20:35:58 -07:00
R. Tyler Croy
e4a3215257 Re-implement #terminate_gracefully with #stop(name) 2012-04-10 20:19:23 -07:00
R. Tyler Croy
c705b5fbef Add #stop method on Foreman::Engine for stopping certain named processes
This will make embedding foreman "nicer" since the embedder can then stop
a specific process (e.g. turning off a service for an integration fail-over test)
2012-04-10 20:10:51 -07:00
Matt Griffin
2fcb64959b Quote and escape environment variables in upstart templates 2012-04-03 16:36:18 -04:00
David Dollar
69216b4c5e Merge pull request #171 from technomancy/0.41.0-deparkaed
Drop parka dependency to make things easier on the build slaves.
2012-03-26 15:05:35 -07:00
Phil Hagelberg
5d2930745a Drop parka dependency to make things easier on the build slaves. 2012-03-26 15:01:04 -07:00
elf Pavlik
a5465bf55e added list of ports to other languages to README 2012-03-18 09:35:51 +01:00
David Dollar
8fc3d1ef24 Merge pull request #161 from leahpar/master
Supervisord Support (Update & Bugfix)
2012-03-16 18:47:15 -07:00
David Dollar
d33e4fb0ed update changelog 2012-03-16 16:36:16 -04:00
David Dollar
39b48b566f 0.41.0 2012-03-16 16:34:32 -04:00
David Dollar
2f982ff9c7 replace term-ansicolor with built-in colorization 2012-03-16 16:34:14 -04:00
Raphael Randschau
1217ef1b56 fix typo 2012-03-05 15:51:10 +01:00
Raphael Randschau
c9943d70ec add group support for supervisord 2012-03-05 15:48:40 +01:00
Raphael Randschau
08dca57eb4 fix enviroment export 2012-02-25 17:24:27 +01:00
David Dollar
bb3377407a Merge pull request #160 from leahpar/master
supervisord support
2012-02-24 11:49:55 -08:00
Raphael Randschau
084b9493d1 revert bundle update 2012-02-24 20:31:07 +01:00
Raphael Randschau
279a251c78 fix whitespaces 2012-02-24 20:29:16 +01:00
Raphael Randschau
a49ef286e8 all specs are passing 2012-02-24 20:18:38 +01:00
Raphael Randschau
0b3da59947 passing first export 2012-02-24 19:52:18 +01:00
David Dollar
1f725dd68a let github show authors 2012-02-24 11:34:34 -05:00
David Dollar
2272f76479 authors 2012-02-24 11:33:33 -05:00
David Dollar
cf4762071c changelog 2012-02-24 11:33:18 -05:00
David Dollar
d86b0bed1f 0.40.0 2012-02-24 11:32:00 -05:00
David Dollar
12f825204b support various quoting styles in .env 2012-02-24 11:31:21 -05:00
David Dollar
62c9d1db45 remove load_env! as it's made unnecessary by foreman run 2012-02-24 11:31:21 -05:00
Raphael Randschau
1127551369 rename supervisord base template to app
add supervisord stub
2012-02-23 17:24:08 +01:00
Raphael Randschau
8cb58b8517 require supervisord export 2012-02-23 17:11:19 +01:00
Raphael Randschau
d03e931b67 add supervisord export engine 2012-02-23 17:10:57 +01:00
Raphael Randschau
9ba2b32b36 add supervisord base template 2012-02-23 17:02:59 +01:00
David Dollar
cf5689a77a Merge pull request #155 from rtyler/bug/152-check-exception-no-procfile
Provide a useful error if `foreman check` fails to find a Procfile
2012-02-21 17:40:20 -08:00
R. Tyler Croy
c23dbb79af Provide a useful error if foreman check fails to find a Procfile
Fixes #152
2012-02-21 17:27:15 -08:00
David Dollar
7e55d8d3e2 update docs 2012-02-21 11:58:57 -05:00
David Dollar
d4f23d45a4 add run to docs 2012-02-21 11:58:52 -05:00
David Dollar
93fa1645e7 update docs 2012-02-21 11:50:11 -05:00
David Dollar
7bdada4a10 update docs 2012-02-21 11:49:57 -05:00
David Dollar
2b47d24ab7 Travis doesn't support cext. Revert "try java again"
This reverts commit 24695348fb.
2012-02-09 19:16:14 -05:00
David Dollar
24695348fb try java again 2012-02-09 19:12:11 -05:00
David Dollar
38b6482af5 changelog 2012-02-07 11:19:12 -05:00
David Dollar
501bc138c5 authors 2012-02-07 11:18:06 -05:00
David Dollar
df8c05cd6c 0.39.0 2012-02-07 11:17:59 -05:00
David Dollar
edcc4f3567 move to foreman-runner 2012-02-07 11:17:44 -05:00
David Dollar
3759dbb463 fix tgz release 2012-02-07 11:16:07 -05:00
David Dollar
b673931c05 Double Revert "Revert "bundle update hpricot""
This reverts commit 137e43b040.
2012-02-04 19:47:48 -05:00
David Dollar
137e43b040 Revert "bundle update hpricot"
This reverts commit 16d4b84a5d.
2012-02-04 19:25:01 -05:00
David Dollar
79211d9bbf Merge pull request #147 from jfirebaugh/rubinius
bundle update hpricot
2012-02-04 14:05:06 -08:00
John Firebaugh
16d4b84a5d bundle update hpricot
0.8.2 doesn't compile on Rubinius; 0.8.6 does.
2012-02-04 13:46:48 -08:00
David Dollar
5ea4537046 remove version from inner pkg 2012-02-02 21:31:58 -05:00
David Dollar
d51433ff82 fix foreman pkg builder 2012-02-02 21:08:01 -05:00
David Dollar
54ab74d305 fix release tasks 2012-02-02 17:32:52 -05:00
David Dollar
e8b8f34f41 changelog 2012-02-02 17:27:56 -05:00
David Dollar
7535f1d3d8 0.38.0 2012-02-02 17:26:42 -05:00
David Dollar
3af0dfb4ae bring back single process starting 2012-02-02 17:26:25 -05:00
David Dollar
78547b8175 foreman + jruby + travis = nope 2012-02-01 13:24:43 -05:00
David Dollar
976fbc0bb0 stub out proctitle/termtitle 2012-02-01 10:19:33 -05:00
David Dollar
28a9aa774f try ci on jruby 2012-02-01 10:06:25 -05:00
David Dollar
51f5ff3842 latest jruby fixed non-excutable to raise EACCESS 2012-01-31 18:01:53 -05:00
David Dollar
e1e18f62bb remove jruby until we figure out what's going on with travis 2012-01-30 11:42:41 -05:00
David Dollar
0a09117328 set JRUBY_OPTS for travis 2012-01-29 23:02:48 -05:00
David Dollar
c3df12746f ignore .rbenv-version 2012-01-29 23:02:40 -05:00
David Dollar
2ec6a23fb3 force to binary encoding if supported 2012-01-29 23:02:20 -05:00
David Dollar
0d6b784de1 disable test in jruby 2012-01-29 23:02:10 -05:00
David Dollar
2dcd2c03db wait for process termination instead of sleeping 2012-01-29 23:02:03 -05:00
David Dollar
9d6d0bbb7d add bundler setup to rakefile 2012-01-29 22:45:50 -05:00
David Dollar
89c1314abe try jruby with travis 2012-01-29 22:37:07 -05:00
David Dollar
f33211d100 clean up gems 2012-01-29 21:35:51 -05:00
David Dollar
4317079bf3 tweak changelog 2012-01-29 21:29:11 -05:00
David Dollar
c745c282c9 update docs 2012-01-29 21:28:49 -05:00
David Dollar
7dca45db57 0.37.2 2012-01-29 21:28:30 -05:00
David Dollar
de3c47fe21 handle directories with spaces in runner 2012-01-29 21:28:11 -05:00
David Dollar
a8a255db4a update docs 2012-01-29 14:06:09 -05:00
David Dollar
307d63b631 changelog 2012-01-29 14:05:59 -05:00
David Dollar
de54f6a5a5 0.37.1 2012-01-29 14:03:45 -05:00
David Dollar
0dff116340 use a 1.8-compatbiel method for IO.pipe 2012-01-29 14:01:13 -05:00
David Dollar
e053dc8434 cleanup 2012-01-29 13:58:16 -05:00
David Dollar
20f6ba1563 use binary pipes 2012-01-29 13:56:57 -05:00
David Dollar
557a08ea77 add utf8 test to engine 2012-01-29 13:56:51 -05:00
David Dollar
8eccc819d6 remove nonfunctional utf8 test 2012-01-29 13:56:42 -05:00
David Dollar
347d4a0184 factor out poll_readers 2012-01-29 13:56:35 -05:00
David Dollar
0147f5d284 set up example procfile with utf8 item 2012-01-29 13:56:22 -05:00
David Dollar
1da034ce66 try to add failing test for utf8 2012-01-29 10:03:31 -05:00
David Dollar
5fc7552572 refactor resource_file 2012-01-29 10:03:14 -05:00
David Dollar
083efe3ae9 sleep longer after loading scripts 2012-01-29 10:03:03 -05:00
David Dollar
9d2382a2d2 remove autotest 2012-01-29 02:06:01 -05:00
David Dollar
f731daae1a update docs 2012-01-29 01:55:44 -05:00
David Dollar
b7f0e3f573 typo 2012-01-29 01:55:14 -05:00
David Dollar
e7c523dab7 cleanup 2012-01-29 01:52:30 -05:00
David Dollar
9f4f15a29c update docs 2012-01-29 01:51:52 -05:00
David Dollar
b2dc89c50e fix up authors 2012-01-29 01:51:00 -05:00
David Dollar
389bf05800 cleanup 2012-01-29 01:47:05 -05:00
David Dollar
a632a62efd update docs 2012-01-29 01:45:57 -05:00
David Dollar
2edf6e1c68 pedantry 2012-01-29 01:41:21 -05:00
David Dollar
56158e881b add changelog 2012-01-29 01:40:36 -05:00
David Dollar
6b5ed495d5 fix up packaging after moving tasks 2012-01-29 01:38:21 -05:00
David Dollar
9652c285f1 fix up changelog task 2012-01-29 01:32:20 -05:00
David Dollar
ba171cc10d curate changelog 2012-01-29 01:26:21 -05:00
David Dollar
ebb191adba better changelog task 2012-01-29 01:26:14 -05:00
David Dollar
8c3ef1a7af 0.37.0 2012-01-29 01:13:27 -05:00
David Dollar
cf69c31ae3 raw changelog 2012-01-29 01:13:02 -05:00
David Dollar
e16a35da4b fix changelog script 2012-01-29 01:12:56 -05:00
David Dollar
2490dd2a5b put an entire line of output inside a single mutex so we don't cross the streams 2012-01-29 00:09:44 -05:00
David Dollar
2705520496 fix race condition with process termination 2012-01-29 00:09:28 -05:00
David Dollar
522fee5e9e pedantry 2012-01-29 00:08:58 -05:00
David Dollar
c462473a25 fix simplecov 2012-01-28 18:53:43 -05:00
David Dollar
224fe94dc2 add more tests 2012-01-28 18:48:26 -05:00
David Dollar
7cb73f5c36 rearrange tasks, add coverage 2012-01-28 18:48:13 -05:00
David Dollar
1537ddbcc9 tweak simplecov entry 2012-01-28 16:27:02 -05:00
David Dollar
b04c81dd06 add simplecov 2012-01-28 16:26:12 -05:00
David Dollar
ae22d34967 Merge pull request #144 from technomancy/debian
Drop the S3 publishing rake tasks.
2012-01-26 17:02:42 -08:00
Phil Hagelberg
9a359efbf7 Drop the S3 publishing rake tasks. 2012-01-26 17:00:07 -08:00
David Dollar
9632227d29 0.37.0.pre5 2012-01-24 08:34:48 -08:00
David Dollar
288d118ca4 Merge pull request #142 from apollo13/master
Fix bash string comparision
2012-01-24 08:29:38 -08:00
David Dollar
576455b8d7 rename readme 2012-01-24 08:14:50 -08:00
David Dollar
a04032454a 0.37.0.pre4 2012-01-24 08:03:07 -08:00
David Dollar
29c94c785d Merge pull request #130 from clowder/foreman
---

Because `Your::Runit::Env != My::Runit::Env` I thought that it would be nice to be able to include your own export format as a another gem along side Foreman; similar to MultiJson’s engines but using constantize because we are specifying formats via the command line.

Cheers,
Chris
2012-01-24 08:00:12 -08:00
Chris Lowder
a0a2dd9454 Don't expose the options hash. 2012-01-24 15:10:25 +00:00
Chris Lowder
1b701cddf3 Simplify subclassing by adding all arguments to the initializer. Also, clean up the method signatures for existing exporters. 2012-01-24 15:10:18 +00:00
Chris Lowder
4080b3f1f2 Add ZenTest as a development dependency, there is an autotest folder but the gem is missing. 2012-01-24 14:37:57 +00:00
Chris Lowder
84c49ae2b8 Attempt to require the custom export class. 2012-01-24 14:36:38 +00:00
Chris Lowder
d54b46806c Catching more than we need to. 2012-01-24 14:36:38 +00:00
Chris Lowder
bc1c5e4c74 Extract commonality into the base class, make life easy for our plugin writers. 2012-01-24 14:36:38 +00:00
Chris Lowder
ed4a32518f Removing the hard coding of export formats allowing the user to 'plug-in' their own export format. 2012-01-24 14:31:45 +00:00
Florian Apolloner
e1d3955d3c Fix the test for an empty string in bin/runner 2012-01-24 09:51:16 +01:00
David Dollar
c7167e1c83 Update README.markdown 2012-01-23 14:20:45 -08:00
David Dollar
db93432118 Revert "try on more rubies"
This reverts commit 5eada6b7cc.
2012-01-23 00:45:25 -05:00
David Dollar
5eada6b7cc try on more rubies 2012-01-23 00:43:25 -05:00
David Dollar
5e46b1f43a fix webhook url 2012-01-23 00:41:10 -05:00
David Dollar
ef723f5831 Update README.markdown 2012-01-23 00:37:58 -05:00
David Dollar
52e24ef64b Update README.markdown 2012-01-23 00:31:57 -05:00
David Dollar
0b1daf1927 ensure we have non-nil data, fixes #111 2012-01-22 22:01:03 -05:00
David Dollar
e0d84a56d7 make sure error method exists. fixes #104 2012-01-22 21:55:55 -05:00
David Dollar
7ee2edcc60 Merge pull request #136 from fnichol/foreman
---

Simple enough, but this helps when re-exporting runit services. Cheers, and thanks!

Conflicts:
	spec/foreman/export/runit_spec.rb
2012-01-22 21:46:57 -05:00
David Dollar
d428ac5356 remove other from install instructions 2012-01-22 21:42:51 -05:00
David Dollar
5351b49fee cleanup 2012-01-22 21:41:10 -05:00
David Dollar
6ebe76d8c1 Revert "tweak authors"
This reverts commit 7c43c672c9
2012-01-22 21:39:24 -05:00
David Dollar
7c43c672c9 tweak authors 2012-01-22 21:39:09 -05:00
David Dollar
41d9050ae3 fix authors 2012-01-22 21:38:17 -05:00
David Dollar
09e9cefa3a readme tweaks 2012-01-22 21:36:38 -05:00
David Dollar
85efe5c1ba readme tweaks 2012-01-22 21:36:24 -05:00
David Dollar
c21634c04e fix up authors 2012-01-22 21:34:23 -05:00
David Dollar
6d4f3476f1 Update README.markdown 2012-01-22 21:32:33 -05:00
David Dollar
724812d6e3 fix up specs 2012-01-22 21:17:54 -05:00
David Dollar
ce6ec4848d we're not chdiring any more 2012-01-22 21:17:38 -05:00
David Dollar
a37a097f73 use strings rather than symbols to better emulate the real thing 2012-01-22 21:17:18 -05:00
David Dollar
f28725bdac Merge pull request #139 from brainopia/foreman
---

If you remember Ive wanted to implement a prototype of process-tree monitor (and therefore restore support for complex commands), but decided to start with specing current behavior and marking desired behavior as pending specs.

This pull-request is a first pass. Its mainly Foreman::Process specs and a bit of refactoring. Specs for Foreman::Engine will be coming next (theyll cover a wide ground of possible uses). Afterwards I will replace machinery a bit to remove pending specs and ensure the rest is still passing on all available to me platforms.

If you have any notices, Ill gladly hear them.
2012-01-22 20:58:51 -05:00
David Dollar
51eee011ce 0.37.0.pre3 2012-01-22 20:40:50 -05:00
David Dollar
6c8c848f54 normalize platform names 2012-01-22 20:33:14 -05:00
David Dollar
f60c4cb767 windows support 2012-01-22 20:33:14 -05:00
David Dollar
4ad49cb058 add windows support 2012-01-22 20:33:13 -05:00
David Dollar
e993af2b20 dont need pty 2012-01-22 20:33:13 -05:00
David Dollar
d7000bccfa fix specs 2012-01-22 19:14:24 -05:00
David Dollar
2995a605b4 0.37.0.pre2 2012-01-22 19:05:42 -05:00
David Dollar
ef280e802d 0.37.0.pre1 2012-01-22 19:04:25 -05:00
David Dollar
8a9001842c remove unnecessary stdout/stderr flattening 2012-01-22 19:01:36 -05:00
David Dollar
8a8d31eb43 use PLATFORM=jruby instead of JRUBY=true 2012-01-22 18:55:47 -05:00
brainopia
ade0005a92 Add specs for Foreman::Process#run 2012-01-22 22:07:53 +04:00
brainopia
241b91a0d5 Implement Foreman::Process#kill,detach,alive?,dead? 2012-01-22 22:07:01 +04:00
brainopia
047f106d48 - Use explicit fakefs tag in specs
- Clean up trailing whitespace
2012-01-22 15:00:43 +04:00
brainopia
df043e60d8 Simplify Foreman::Process#with_environment 2012-01-22 08:57:09 +04:00
brainopia
158c184f8c Add specs for options of Foreman::Process#run 2012-01-22 08:55:45 +04:00
brainopia
2ed1fe8d44 Add specs for initialization of Foreman::Process 2012-01-22 08:54:42 +04:00
Fletcher Nichol
e76f3533dc runit creates a full path to export directory. 2012-01-18 20:13:08 -07:00
118 changed files with 3232 additions and 1077 deletions

4
.gitignore vendored
View File

@@ -1,8 +1,10 @@
/.bundle
/.rbenv-version
/.yardoc
/coverage
/example/log/*
/man/*.html
/man/*.markdown
/pkg
/tags
/vendor
/vendor

View File

@@ -1,9 +1,10 @@
script: bundle exec rake spec
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
matrix:
allow_failures:
- rvm: jruby
- rvm: rbx
- rvm: ree
notifications:
email: false
@@ -11,4 +12,11 @@ notifications:
on_success: always
on_failure: always
urls:
- https://dx-helper.herokuapp.com/travis
- http://dx-helper.herokuapp.com/travis
rvm:
- 1.8.7
- 1.9.2
- 1.9.3
- 2.0.0
- jruby

View File

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

354
Changelog.md Normal file
View File

@@ -0,0 +1,354 @@
## 0.63.0 (2013-04-15)
* Revert "Ensure foreman is the process group leader" [John Griffin]
* remove posix-spawn dependency as it does not work in jruby 1.7.3 [Andrew Brown & Corey Downing]
* Replace Foreman::Env with dotenv [Brandon Keepers]
* [foreman-runner] fix sourcing as . is rarely in PATH [Barry Allard]
* Fixed specs to pass. [Kentaro Kuribayashi]
* Permit underscore for command name in Procfile. [Kentaro Kuribayashi]
* Update man/foreman.1 [Patrick Ellis]
* Remove tmux option from man page [Donald Plummer]
* Prevent upstart export from deleting similarly named upstart files [Andy Morris]
* Add MIT license text [Per Andersson]
* use "start|stop\ on runlevel [x]" for upstart config [Nick Messick]
## 0.62.0 (2013-03-08)
* Merge pull request #334 from ged/reentrant_signal_handlers [David Dollar]
* Merge pull request #335 from ged/20_encoding_fix [David Dollar]
* Try to allow children to shut down gracefully [Michael Granger]
* Add deferred signal-handling (fixes #332). [Michael Granger]
* Fix spec encoding problem under Ruby 2.0.0. [Michael Granger]
* add ruby 2.0 to travis [David Dollar]
* Merge pull request #327 from patheticpat/master [David Dollar]
* Fixed a typo in cli options description [Michael Kaiser]
* handled by mingw now [David Dollar]
## 0.61.0 (2013-01-14)
* Fix bug in color definitons [nseo]
* Fix for high CPU load when processes close output [Pavel Forkert]
* Ensure foreman is the process group leader [Christos Trochalakis]
* Don't ignore blank lines in the output [Matt Venables]
* Add license to gemspec [petedmarsh]
* Since JRuby 1.9 doesn't require posix/spawn, only follow that path if JRuby is loaded and running in 1.8 mode. [Adam Hutchison]
* Remove explicit requirement on rubygems. [Cyril Rohr]
* Dont use shared_path variable before multistage has a chance at it [Aditya Sanghi]
* Strip Windows Line Endings [Paul Morton]
* Fix man page: --directory is actually --root. [Evan Jones]
* Add timeout switch to CLI [Paulo Luis Franchini Casaretto]
* Remove expectation of double quotes around environment variables from test comparisons [Kevin McAllister]
* Remove explicit wrapping of Shellwords.escape in double quotes [Kevin McAllister]
## 0.60.2 (2012-10-08)
* Fix for nil value on io select loop, fixes #260 [Silvio Relli]
## 0.60.1 (2012-10-08)
* sleep on select() to avoid spinning the cpu [Silvio Relli]
## 0.60.0 (2012-09-25)
* foreman run can run things from the Procfile like heroku run. [Dan Peterson]
## 0.59.0 (2012-09-15)
* Use /bin/sh instead of bash for foreman-runner [Jeremy Evans]
## 0.58.0 (2012-09-14)
* dont set HOME [David Dollar]
* Add StandardOutPath to launchd export [Aaron Kalin]
* Add command argument string splitting [Aaron Kalin]
* Cleanup launchd exporter [Aaron Kalin]
* Enable trim_mode via '-' in ERB templates [Aaron Kalin]
* Add support for setting environment variables [Aaron Kalin]
* foreman run should exit with the same code as its command [Omar Khan]
* Handle multiline strings in .env file [Szymon Nowak]
* Use path and env variables in the inittab export [Indrek Juhkam]
* fixed the directory option [Arnaud Lachaume]
* Add capistrano export support [Daniel Farrell]
## 0.57.0 (2012-08-21)
* fix startup checks for upstart exporter [Aditya Sanghi]
## 0.56.0 (2012-08-19)
* read .profile, not .profile.d [David Dollar]
## 0.55.0 (2012-08-14)
* use a forked process to exec a run with environment [David Dollar]
## 0.54.0 (2012-08-14)
* use Foreman::Process to extract command running [David Dollar]
* changed to check env for bash [brntbeer]
## 0.53.0 (2012-07-24)
* put app root in $HOME [David Dollar]
## 0.52.0 (2012-07-24)
* wrap command in a runner that sources .profile.d scripts [David Dollar]
* fix upstart export specs [David Dollar]
* Make upstart export start/stop with network [Daniel Farrell]
## 0.51.0 (2012-07-11)
* dont try to colorize windows [David Dollar]
## 0.50.0 (2012-07-11)
* handle windows [David Dollar]
## 0.49.0 (2012-07-11)
* 1.8 compatibility [David Dollar]
* use one pgroup for all of foreman and kill that since ruby 1.8 sucks at pgroups [David Dollar]
* better debugging [David Dollar]
## 0.48.0 (2012-07-10)
* allow old exporter format to work, but with deprecation warning [David Dollar]
* remove debugging code [David Dollar]
* Merge pull request #219 from MarkDBlackwell/patch-1 [David Dollar]
* Avoid crash by verifying the existence of SIGHUP before accessing it. [Mark D. Blackwell]
* allow color to be forced on [David Dollar]
* terminate gracefully if stdout goes away [David Dollar]
* always flush output [David Dollar]
* Merge pull request #212 from morgoth/added-version-command [David Dollar]
* added command for displaying foreman version [Wojciech Wnętrzak]
* Merge pull request #211 from morgoth/fixed-yaml-usage [David Dollar]
* fixed using YAML [Wojciech Wnętrzak]
* test on more things, but don't fail [David Dollar]
* changelog [David Dollar]
* 0.48.0.pre1 [David Dollar]
* foreman doesn't work on ruby 1.8, may try to fix later [David Dollar]
* use bash [David Dollar]
* massive refactoring for programmatic control and stability [David Dollar]
* Merge pull request #164 from hsume2/master [David Dollar]
* Only run tmux specs if tmux is installed [Henry Hsu]
* Do not assume BUNDLE_GEMFILE [Henry Hsu]
* Add support for starting procfile in tmux session [Henry Hsu]
## 0.47.0 (2012-06-07)
* Fix multi-word argument handling in `foreman run`. [Daniel Brockman]
* Make 'PORT=5000 foreman start' work [Koen Van der Auwera]
* Terminate gracefully upon SIGHUP [Stefan Schüßler]
* Set port from .env if specified [Koen Van der Auwera]
* Updated bluepill exporter to use environment variables from .env [Aneeth]
* Added launchd exporter [Maxwell Swadling]
* Quote and escape environment variables in upstart templates [Matt Griffin]
* Added list of ports to other languages to README [elf Pavlik]
## 0.46.0 (2012-05-02)
* Add Profile load/write/append API [Michael Granger]
* Guard against missing Procfile in engine.rb [Brian Kaney]
## 0.45.0 (2012-04-26)
* create and chown log dir in upstart export. [Phil Hagelberg]
* remove parka from dist files [David Dollar]
## 0.44.0 (2012-04-23)
* make var output order repeatable in supervisord export [David Dollar]
* make --procfile and --app-root influence each other in a more intuitive way [David Dollar]
* Look for .env and app_root in the same dir as the Procfile. [Phil Hagelberg]
## 0.43.0 (2012-04-20)
* wrap supervisord env vars in quotes [Raphael Randschau]
## 0.42.0 (2012-04-18)
* Move read_environment to a public class method. [Phil Hagelberg]
* Drop parka dependency [Phil Hagelberg]
* add group support for supervisord [Raphael Randschau]
* fix enviroment export [Raphael Randschau]
## 0.41.0 (2012-03-16)
* replace term-ansicolor with built-in colorization [David Dollar]
* supervisord export template [Raphael Randschau]
## 0.40.0 (2012-02-24)
* support various quoting styles in .env [David Dollar]
* remove load_env! as it's made unnecessary by foreman run [David Dollar]
* Provide a useful error if `foreman check` fails to find a Procfile [R. Tyler Croy]
* update docs [David Dollar]
## 0.39.0 (2012-02-07)
* rename bin/runner to bin/foreman-runner [David Dollar]
* fix tgz release [David Dollar]
* bundle update hpricot [John Firebaugh]
* touch up .pkg release tasks [David Dollar]
## 0.38.0 (2012-02-02)
* bring back single process starting [David Dollar]
* more attempts at getting ci working with jruby [David Dollar]
* ignore .rbenv-version [David Dollar]
* force to binary encoding if supported [David Dollar]
## 0.37.2 (2012-01-29)
* handle directories with spaces in runner [David Dollar]
* update docs [David Dollar]
## 0.37.1 (2012-01-29)
* use binary pipes to better handle UTF-8 data [David Dollar]
* set up example procfile with UTF-8 item [David Dollar]
* remove autotest [David Dollar]
* fix up authors generation [David Dollar]
* fix up packaging after moving tasks [David Dollar]
* fix up changelog tasks [David Dollar]
## 0.37.0 (2012-01-29)
* put an entire line of output inside a single mutex so we don't cross the streams [David Dollar]
* fix race condition with process termination [David Dollar]
* allow external custom exporters [Chris Lowder]
* fix the test for an empty string in bin/runner [Florian Apolloner]
* ensure we have non-nil data, fixes #111 [David Dollar]
* make sure error method exists, fixes #104 [David Dollar]
* clean up chdir usage [David Dollar]
* normalize platform names [David Dollar]
* add windows support [David Dollar]
* add jruby support [David Dollar]
* pass basedir along to the runner script [David Dollar]
* harden runner script [David Dollar]
* add many missing specs [brainopia]
* clean up fakefs usage in specs [brainopia]
* runit creates a full path to export directory. [Fletcher Nichol]
## 0.36.1 (2012-01-18)
* 0.36.1 [David Dollar]
* bump term-ansicolor in gemspec [David Dollar]
## 0.36.0 (2012-01-17)
* 0.36.0 [David Dollar]
* sync the writer stream [David Dollar]
* capture stderr as well [David Dollar]
## 0.35.0 (2012-01-16)
* update rake [David Dollar]
* 0.35.0 [David Dollar]
* Merge pull request #132 from Viximo/feature/concurrency [David Dollar]
* Fix export specs [Matt Griffin]
* Merge branch 'master' of https://github.com/michaeldwan/foreman into feature/concurrency [Matt Griffin]
* default process concurrency is 0 when concurrency options specified, otherwise default concurrency is 1 [Michael Dwan]
## 0.34.1 (2012-01-16)
* 0.34.1 [David Dollar]
* fix missing start desc [David Dollar]
## 0.34.0 (2012-01-16)
* 0.34.0 [David Dollar]
* update man page [David Dollar]
* update docs for -d [David Dollar]
* Merge pull request #101 from ndbroadbent/foreman [David Dollar]
* Wrap around to the first colour when all the colours are used [Craig R Webster]
* run specs in random order [David Dollar]
* update rspec [David Dollar]
* pedantry [David Dollar]
* Set executable bit on runit run scripts. [Matthijs Langenberg]
* Merge pull request #114 from gburt/master [David Dollar]
* add more colors [Gabriel Burt]
* Added option to specify app_root, if executing a Procfile from a shared location [Nathan Broadbent]
## 0.33.1 (2012-01-16)
* 0.33.1 [David Dollar]
* Merge pull request #129 from fnichol/resolve-home-template [David Dollar]
* Expand template path under user's home directory. [Fletcher Nichol]
## 0.33.0 (2012-01-15)
* 0.33.0 [David Dollar]
* Revert "Merge pull request #125 from brainopia/master" [David Dollar]
## 0.32.0 (2012-01-12)
* 0.32.0 [David Dollar]
* Merge pull request #125 from brainopia/master [David Dollar]
* Merge pull request #121 from Viximo/feature/run [David Dollar]
* Return some whitespace that was accidentally removed [Matt Griffin]
* Steal the run method back from Thor so that it can be used in place for exec for running commands in the foreman environment. [Matt Griffin]
* Remove old cruft [brainopia]
* In case someone wants to use bin/runner directly [brainopia]
* Fix for double fork [brainopia]
* Use ruby exec which works with escaped cmd and replaces shell [brainopia]
* Fix foreman to work with cmds containing pipes and redirects [brainopia]
* Add "exec" action to allow execution of arbitrary commands with the app's environment. [Matt Griffin]
* tweak readme [David Dollar]
## 0.31.0 (2012-01-04)
* 0.31.0 [David Dollar]
* make fork more robust [David Dollar]
* remove unnecessary debug [David Dollar]
* add more information when shutting down [David Dollar]
* Merge pull request #110 from lstoll/master [David Dollar]
* Use different port ranges for each process type [Lincoln Stoll]
## 0.30.1 (2011-12-23)
* 0.30.1 [David Dollar]
* require thread for mutex [David Dollar]
## 0.30.0 (2011-12-22)
* 0.30.0 [David Dollar]
* compatibility with ruby 1.8 [David Dollar]
## 0.29.0 (2011-12-22)
* 0.29.0 [David Dollar]
* 0.28.0.pre2 [David Dollar]
* fix pipe error [David Dollar]
* 0.28.0.pre1 [David Dollar]
* Merge branch 'fork' [David Dollar]
* wip [David Dollar]
* wip [David Dollar]
* wip [David Dollar]
* wip [David Dollar]
* wip [David Dollar]
## 0.27.0 (2011-12-05)
* 0.27.0 [David Dollar]
* add changelog [David Dollar]
* Merge pull request #103 from csquared/load_env_from_irb [David Dollar]
* refactor load_env to apply_environment [Chris Continanza]
* rename load! to load_env! [Chris Continanza]
* use ./.env as default [Chris Continanza]
* load contents from env file [Chris Continanza]
* refactor engine to expose env methods [Chris Continanza]
* disable email notifications [David Dollar]
* add travis config [David Dollar]
## 0.26.1 2011-12-05
* Merge pull request #103 from csquared/load_env_from_irb [David Dollar]
* refactor load_env to apply_environment [Chris Continanza]
* rename load! to load_env! [Chris Continanza]
* use ./.env as default [Chris Continanza]
* load contents from env file [Chris Continanza]
* refactor engine to expose env methods [Chris Continanza]
* disable email notifications [David Dollar]
* add travis config [David Dollar]

14
Gemfile
View File

@@ -2,18 +2,22 @@ source "http://rubygems.org"
gemspec
platform :jruby do
platform :mingw do
gem "win32console", "~> 1.3.0"
end
platform :jruby, :ruby_18 do
gem "posix-spawn", "~> 0.3.6"
end
group :development do
gem 'parka'
gem 'aws-s3'
gem 'rake'
gem 'ronn'
gem 'fakefs', '~> 0.3.2'
gem 'rcov', '~> 0.9.8'
gem 'rr', '~> 1.0.2'
gem 'rspec', '~> 2.0'
gem 'aws-s3'
gem "rubyzip"
gem "simplecov", :require => false
gem 'timecop'
gem 'yard'
end

View File

@@ -1,8 +1,8 @@
PATH
remote: .
specs:
foreman (0.36.1)
term-ansicolor (~> 1.0.7)
foreman (0.63.0)
dotenv (>= 0.7)
thor (>= 0.13.6)
GEM
@@ -13,24 +13,17 @@ GEM
mime-types
xml-simple
builder (3.0.0)
crack (0.1.8)
diff-lcs (1.1.3)
dotenv (0.7.0)
fakefs (0.3.2)
hpricot (0.8.2)
hpricot (0.8.2-java)
hpricot (0.8.6)
hpricot (0.8.6-java)
mime-types (1.16)
multi_json (1.0.4)
mustache (0.11.2)
parka (0.6.2)
crack
rest-client
thor
posix-spawn (0.3.6)
rake (0.9.2.2)
rcov (0.9.8)
rcov (0.9.8-java)
rdiscount (1.6.5)
rest-client (1.6.1)
mime-types (>= 1.16)
ronn (0.7.3)
hpricot (>= 0.8.2)
mustache (>= 0.7.0)
@@ -44,24 +37,31 @@ GEM
rspec-expectations (2.8.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.8.0)
rubyzip (0.9.4)
term-ansicolor (1.0.7)
thor (0.14.6)
simplecov (0.5.4)
multi_json (~> 1.0.3)
simplecov-html (~> 0.5.3)
simplecov-html (0.5.3)
thor (0.16.0)
timecop (0.3.5)
win32console (1.3.0-x86-mingw32)
xml-simple (1.0.15)
yard (0.8.2)
PLATFORMS
java
ruby
x86-mingw32
DEPENDENCIES
aws-s3
fakefs (~> 0.3.2)
foreman!
parka
posix-spawn (~> 0.3.6)
rake
rcov (~> 0.9.8)
ronn
rr (~> 1.0.2)
rspec (~> 2.0)
rubyzip
simplecov
timecop
win32console (~> 1.3.0)
yard

19
LICENSE Normal file
View File

@@ -0,0 +1,19 @@
Copyright (c) 2012 David Dollar
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -1,50 +0,0 @@
# Foreman
## Installation
* Rubygems
gem install foreman
* OSX
http://assets.foreman.io/foreman/foreman.pkg
* Standalone Tarball
http://assets.foreman.io/foreman/foreman.tgz
## Description
http://blog.daviddollar.org/2011/05/06/introducing-foreman.html
## Manual
* [man page](http://ddollar.github.com/foreman)
* [wiki](http://github.com/ddollar/foreman/wiki)
## Authorship
Created by David Dollar
Patches contributed by:
* Adam Wiggins
* Dan Peterson
* Hunter Nield
* Jay Zeschin
* Keith Rarick
* Khaja Minhajuddin
* Matt Haynes
* Michael van Rooijen
* Mike Javorski
* Nathan L Smith
* Nick Zadrozny
* Ricardo Chimal, Jr
* Thom May
* clifff
* Greg Reinacker
## License
MIT

49
README.md Normal file
View File

@@ -0,0 +1,49 @@
# 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
* [forego](https://github.com/ddollar/forego) - Go
## Authors
#### Created and maintained by
David Dollar
#### Patches contributed by
[Contributor List](https://github.com/ddollar/foreman/contributors)
## License
Foreman is licensed under the MIT license.
See LICENSE for the full license text.

185
Rakefile
View File

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

View File

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

View File

@@ -1,6 +1,6 @@
#!/bin/sh
#
#/ Usage: runner [-d <dir>] <command>
#/ Usage: foreman-runner [-d <dir>] [-p] <command> [<args>...]
#/
#/ Run a command with exec, optionally changing directory first
@@ -16,9 +16,12 @@ usage() {
exit
}
while getopts ":hd:" OPT; do
read_profile=""
while getopts ":hd:p" OPT; do
case $OPT in
d) cd $OPTARG ;;
d) cd "$OPTARG" ;;
p) read_profile="1" ;;
h) usage ;;
\?) error "invalid option: -$OPTARG" ;;
:) error "option -$OPTARG requires an argument" ;;
@@ -27,10 +30,12 @@ done
shift $((OPTIND-1))
command=$1
[ -z "$1" ] && usage
if [ "$1" == "" ]; then
usage
if [ "$read_profile" = "1" ]; then
if [ -f .profile ]; then
. ./.profile
fi
fi
exec $1 2>&1
exec "$@"

8
bin/taskman Executable file
View 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

View File

@@ -0,0 +1,2 @@
#!/bin/sh
export FOO=bar

View File

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

14
data/example/spawnee Executable file
View 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
View File

@@ -0,0 +1,7 @@
#!/bin/sh
./spawnee A &
./spawnee B &
./spawnee C &
wait

11
data/example/utf8 Executable file
View File

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

View File

@@ -3,24 +3,25 @@ Bluepill.application("<%= app %>", :foreground => false, :log_file => "/var/log/
app.uid = "<%= user %>"
app.gid = "<%= user %>"
<% engine.procfile.entries.each do |process| %>
<% 1.upto(concurrency[process.name]) do |num| %>
<% port = engine.port_for(process, num, options[:port]) %>
app.process("<%= process.name %>-<%=num%>") do |process|
process.start_command = "<%= process.command.gsub("$PORT", port.to_s) %>"
<% engine.each_process do |name, process| %>
<% 1.upto(engine.formation[name]) do |num| %>
<% port = engine.port_for(process, num) %>
app.process("<%= name %>-<%= num %>") do |process|
process.start_command = "<%= process.command %>"
process.working_dir = "<%= engine.directory %>"
process.working_dir = "<%= engine.root %>"
process.daemonize = true
process.environment = {"PORT" => "<%= port %>"}
process.environment = <%= engine.env.merge("PORT" => port.to_s).inspect %>
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
process.stop_grace_time = 45.seconds
process.stdout = process.stderr = "<%= log_root %>/<%= app %>-<%= process.name %>-<%=num%>.log"
process.stdout = process.stderr = "<%= log %>/<%= app %>-<%= name %>-<%= num %>.log"
process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}"
children.stop_command "kill {{PID}}"
end
process.group = "<%= app %>-<%= process.name %>"
process.group = "<%= app %>-<%= name %>"
end
<% end %>
<% end %>

View File

@@ -0,0 +1,14 @@
pre-start script
bash << "EOF"
mkdir -p <%= log %>
chown -R <%= user %> <%= log %>
mkdir -p <%= run %>
chown -R <%= user %> <%= run %>
EOF
end script
start on runlevel [2345]
stop on runlevel [016]

View File

@@ -0,0 +1,8 @@
start on starting <%= app %>-<%= name.gsub('_', '-') %>
stop on stopping <%= app %>-<%= name.gsub('_', '-') %>
respawn
env PORT=<%= port %><% engine.env.each_pair do |var, env| %>
env <%= var.upcase %>=<%= env %><% end %>
exec start-stop-daemon --start --chuid <%= user %> --chdir <%= engine.root %> --make-pidfile --pidfile <%= run %>/<%= app %>-<%= name %>-<%= num %>.pid --exec <%= executable %><%= arguments %> >> <%= log %>/<%= app %>-<%= name %>-<%= num %>.log 2>&1

View File

@@ -0,0 +1,2 @@
start on starting <%= app %>
stop on stopping <%= app %>

View 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>

View 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"

View File

@@ -1,7 +0,0 @@
#!/bin/sh
set -e
LOG=<%= log_root %>/<%= process.name %>-<%= num %>
test -d "$LOG" || mkdir -p m2750 "$LOG" && chown <%= user %> "$LOG"
exec chpst -u <%= user %> svlogd "$LOG"

View File

@@ -1,3 +1,3 @@
#!/bin/sh
cd <%= engine.directory %>
exec chpst -u <%= user %> -e <%= process_env_directory %> <%= process.command %>
cd <%= engine.root %>
exec chpst -u <%= user %> -e <%= File.join(location, "#{process_directory}/env") %> <%= process.command %>

View 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(',') %>

View File

@@ -0,0 +1 @@
[Unit]

View File

@@ -0,0 +1,18 @@
[Unit]
StopWhenUnneeded=true
[Service]
User=<%= user %>
WorkingDirectory=<%= engine.root %>
Environment=PORT=<%= port %><% engine.env.each_pair do |var,env| %>
Environment=<%= var.upcase %>=<%= env %><% end %>
ExecStart=/bin/bash -lc '<%= process.command %>'
Restart=always
StandardInput=null
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=%n
KillMode=process
[Install]
WantedBy=<%= app %>-<%= name %>.target

View File

@@ -0,0 +1,5 @@
[Unit]
StopWhenUnneeded=true
[Install]
WantedBy=<%= app %>.target

View File

@@ -1,8 +1,12 @@
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 runlevel [2345]
stop on runlevel [016]

View File

@@ -1,5 +1,5 @@
start on starting <%= app %>-<%= process.name %>
stop on stopping <%= app %>-<%= process.name %>
start on starting <%= app %>-<%= name %>
stop on stopping <%= app %>-<%= name %>
respawn
exec su - <%= user %> -c 'cd <%= engine.directory %>; export PORT=<%= port %>;<% engine.environment.each_pair do |var,env| %> export <%= var.upcase %>=<%= env %>; <% end %> <%= process.command %> >> <%= log_root %>/<%=process.name%>-<%=num%>.log 2>&1'
exec su - <%= user %> -c 'cd <%= engine.root %>; export PORT=<%= port %>;<% engine.env.each_pair do |var,env| %> export <%= var.upcase %>=<%= shell_quote(env) %>; <% end %> <%= process.command %> >> <%= log %>/<%=name%>-<%=num%>.log 2>&1'

17
dist/deb.rake vendored
View File

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

2
dist/gem.rake vendored
View File

@@ -10,5 +10,5 @@ task "gem:clean" do
end
task "gem:release" => "gem:build" do |t|
sh "parka push -f #{pkg("foreman-#{version}.gem")}"
sh "gem push #{pkg("foreman-#{version}.gem")} || echo 'error'"
end

6
dist/jruby.rake vendored
View File

@@ -1,5 +1,7 @@
file pkg("foreman-#{version}-jruby.gem") => distribution_files do |t|
sh "env JRUBY=true gem build foreman.gemspec"
Bundler.with_clean_env do
sh "env PLATFORM=java gem build foreman.gemspec"
end
sh "mv foreman-#{version}-java.gem #{t.name}"
end
@@ -10,5 +12,5 @@ task "jruby:clean" do
end
task "jruby:release" => "jruby:build" do |t|
sh "parka push -f #{pkg("foreman-#{version}-jruby.gem")}"
sh "gem push #{pkg("foreman-#{version}-jruby.gem")} || echo 'error'"
end

16
dist/mingw32.rake vendored Normal file
View 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

15
dist/pkg.rake vendored
View File

@@ -13,7 +13,7 @@ file pkg("foreman-#{version}.pkg") => distribution_files do |t|
mkdir_p "pkg"
mkdir_p "pkg/Resources"
mkdir_p "pkg/foreman-#{version}.pkg"
mkdir_p "pkg/foreman.pkg"
dist = File.read(resource("pkg/Distribution.erb"))
dist = ERB.new(dist).result(binding)
@@ -21,20 +21,21 @@ file pkg("foreman-#{version}.pkg") => distribution_files do |t|
dist = File.read(resource("pkg/PackageInfo.erb"))
dist = ERB.new(dist).result(binding)
File.open("pkg/foreman-#{version}.pkg/PackageInfo", "w") { |f| f.puts dist }
File.open("pkg/foreman.pkg/PackageInfo", "w") { |f| f.puts dist }
mkdir_p "pkg/foreman-#{version}.pkg/Scripts"
cp resource("pkg/postinstall"), "pkg/foreman-#{version}.pkg/Scripts/postinstall"
chmod 0755, "pkg/foreman-#{version}.pkg/Scripts/postinstall"
mkdir_p "pkg/foreman.pkg/Scripts"
cp resource("pkg/postinstall"), "pkg/foreman.pkg/Scripts/postinstall"
chmod 0755, "pkg/foreman.pkg/Scripts/postinstall"
sh %{ mkbom -s foreman pkg/foreman-#{version}.pkg/Bom }
sh %{ mkbom -s foreman pkg/foreman.pkg/Bom }
Dir.chdir("foreman") do
sh %{ pax -wz -x cpio . > ../pkg/foreman-#{version}.pkg/Payload }
sh %{ pax -wz -x cpio . > ../pkg/foreman.pkg/Payload }
end
sh %{ pkgutil --flatten pkg foreman-#{version}.pkg }
FileUtils.mkdir_p(File.dirname(t.name))
cp_r "foreman-#{version}.pkg", t.name
end
end

View File

@@ -10,14 +10,14 @@
]]></script>
<choices-outline>
<line choice="git"/>
<line choice="foreman-<%= version %>"/>
<line choice="foreman"/>
</choices-outline>
<choice id="git" title="git" start_selected="false" start_enabled="false" selected="needs_git()" enabled="needs_git()">
<pkg-ref id="git.pkg" />
</choice>
<choice id="foreman-<%= version %>" title="foreman">
<choice id="foreman" title="foreman">
<pkg-ref id="io.foreman.installer"/>
</choice>
<pkg-ref id="io.foreman.installer" installKBytes="<%= kbytes %>" version="<%= version %>" auth="Root">#foreman-<%= version %>.pkg</pkg-ref>
<pkg-ref id="io.foreman.installer" installKBytes="<%= kbytes %>" version="<%= version %>" auth="Root">#foreman.pkg</pkg-ref>
</installer-script>

View File

@@ -4,4 +4,3 @@
<postinstall file="./postinstall"/>
</scripts>
</pkg-info>

4
dist/tgz.rake vendored
View File

@@ -3,8 +3,8 @@ file pkg("foreman-#{version}.tgz") => distribution_files do |t|
mkchdir("foreman") do
assemble_distribution
assemble_gems
rm_rf "bin"
assemble resource("tgz/foreman"), "foreman", 0755
rm_f "bin/foreman"
assemble resource("tgz/foreman"), "bin/foreman", 0755
end
sh "tar czvf #{t.name} foreman"

View File

@@ -3,6 +3,7 @@ require "foreman/version"
Gem::Specification.new do |gem|
gem.name = "foreman"
gem.license = "MIT"
gem.version = Foreman::VERSION
gem.author = "David Dollar"
@@ -16,11 +17,15 @@ Gem::Specification.new do |gem|
gem.files = Dir["**/*"].select { |d| d =~ %r{^(README|bin/|data/|ext/|lib/|spec/|test/)} }
gem.files << "man/foreman.1"
gem.add_dependency 'term-ansicolor', '~> 1.0.7'
gem.add_dependency 'thor', '>= 0.13.6'
gem.add_dependency 'thor', '>= 0.13.6'
gem.add_dependency 'dotenv', '>= 0.7'
if ENV["JRUBY"]
gem.add_dependency "posix-spawn", "~> 0.3.6"
if ENV["PLATFORM"] == "java"
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

View File

@@ -4,15 +4,20 @@ module Foreman
class AppDoesNotExist < Exception; end
# load contents of env_file into ENV
def self.load_env!(env_file = './.env')
require 'foreman/engine'
Foreman::Engine.load_env!(env_file)
def self.runner
File.expand_path("../../bin/foreman-runner", __FILE__)
end
def self.runner
File.expand_path("../../bin/runner", __FILE__)
def self.jruby_18?
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java" and ruby_18?
end
def self.ruby_18?
defined?(RUBY_VERSION) and RUBY_VERSION =~ /^1\.8\.\d+/
end
def self.windows?
defined?(RUBY_PLATFORM) and RUBY_PLATFORM =~ /(win|w)32$/
end
end

View File

@@ -1,30 +1,44 @@
require "foreman"
require "foreman/helpers"
require "foreman/engine"
require "foreman/engine/cli"
require "foreman/export"
require "thor"
require "foreman/version"
require "shellwords"
require "yaml"
require "thor"
class Foreman::CLI < Thor
desc "start", "Start the application"
include Foreman::Helpers
map ["-v", "--version"] => :version
class_option :procfile, :type => :string, :aliases => "-f", :desc => "Default: Procfile"
class_option :app_root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"
class_option :root, :type => :string, :aliases => "-d", :desc => "Default: Procfile directory"
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
method_option :port, :type => :numeric, :aliases => "-p"
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
desc "start [PROCESS]", "Start the application (or a specific PROCESS)"
method_option :color, :type => :boolean, :aliases => "-c", :desc => "Force color to be enabled"
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
method_option :formation, :type => :string, :aliases => "-m", :banner => '"alpha=5,bar=3"'
method_option :port, :type => :numeric, :aliases => "-p"
method_option :timeout, :type => :numeric, :aliases => "-t", :desc => "Specify the amount of time (in seconds) processes have to shutdown gracefully before receiving a SIGKILL, defaults to 5."
class << self
# Hackery. Take the run method away from Thor so that we can redefine it.
def is_thor_reserved_word?(word, type)
return false if word == 'run'
return false if word == "run"
super
end
end
def start
def start(process=nil)
require_posix_spawn_for_ruby_18!
check_procfile!
load_environment!
engine.load_procfile(procfile)
engine.options[:formation] = "#{process}=1" if process
engine.start
end
@@ -32,26 +46,19 @@ class Foreman::CLI < Thor
method_option :app, :type => :string, :aliases => "-a"
method_option :log, :type => :string, :aliases => "-l"
method_option :run, :type => :string, :aliases => "-r", :desc => "Specify the pid file directory, defaults to /var/run/<application>"
method_option :env, :type => :string, :aliases => "-e", :desc => "Specify an environment file to load, defaults to .env"
method_option :port, :type => :numeric, :aliases => "-p"
method_option :user, :type => :string, :aliases => "-u"
method_option :template, :type => :string, :aliases => "-t"
method_option :concurrency, :type => :string, :aliases => "-c",
:banner => '"alpha=5,bar=3"'
method_option :concurrency, :type => :string, :aliases => "-c", :banner => '"alpha=5,bar=3"'
def export(format, location=nil)
check_procfile!
formatter = case format
when "inittab" then Foreman::Export::Inittab
when "upstart" then Foreman::Export::Upstart
when "bluepill" then Foreman::Export::Bluepill
when "runit" then Foreman::Export::Runit
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
@@ -59,54 +66,100 @@ class Foreman::CLI < Thor
desc "check", "Validate your application's Procfile"
def check
error "no processes defined" unless engine.procfile.entries.length > 0
display "valid procfile detected (#{engine.procfile.process_names.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", "Run a command using your application's environment"
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)
engine.apply_environment!
begin
exec args.join(" ")
rescue Errno::EACCES
error "not executable: #{args.first}"
rescue Errno::ENOENT
error "command not found: #{args.first}"
load_environment!
if File.exist?(procfile)
engine.load_procfile(procfile)
end
pid = fork do
begin
engine.env.each { |k,v| ENV[k] = v }
if args.size == 1 && process = engine.process(args.first)
process.exec(:env => engine.env)
else
exec args.shelljoin
end
rescue Errno::EACCES
error "not executable: #{args.first}"
rescue Errno::ENOENT
error "command not found: #{args.first}"
end
end
Process.wait(pid)
exit $?.exitstatus
end
desc "version", "Display Foreman gem version"
def version
puts Foreman::VERSION
end
no_tasks do
def engine
@engine ||= begin
engine_class = Foreman::Engine::CLI
engine = engine_class.new(options)
engine
end
end
end
private ######################################################################
def check_procfile!
error("#{procfile} does not exist.") unless File.exist?(procfile)
end
def engine
@engine ||= Foreman::Engine.new(procfile, options)
end
def procfile
options[:procfile] || "Procfile"
end
def display(message)
puts message
end
def error(message)
puts "ERROR: #{message}"
exit 1
end
def procfile_exists?(procfile)
File.exist?(procfile)
def check_procfile!
error("#{procfile} does not exist.") unless File.exist?(procfile)
end
def load_environment!
if options[:env]
options[:env].split(",").each do |file|
engine.load_env file
end
else
default_env = File.join(engine.root, ".env")
engine.load_env default_env if File.exists?(default_env)
end
end
def require_posix_spawn_for_ruby_18!
begin
Kernel.require 'posix/spawn' # Use Kernel explicitly so we can mock the require call in the spec
rescue LoadError
error "foreman requires gem `posix-spawn` on Ruby #{RUBY_VERSION}. Please `gem install posix-spawn`."
end if Foreman.ruby_18?
end
def procfile
case
when options[:procfile] then options[:procfile]
when options[:root] then File.expand_path(File.join(options[:root], "Procfile"))
else "Procfile"
end
end
def options
original_options = super
return original_options unless File.exists?(".foreman")
defaults = YAML::load_file(".foreman") || {}
defaults = ::YAML::load_file(".foreman") || {}
Thor::CoreExt::HashWithIndifferentAccess.new(defaults.merge(original_options))
end
end

View File

@@ -1,101 +1,398 @@
require "foreman"
require "foreman/process"
require "foreman/procfile"
require "foreman/utils"
require "pty"
require "dotenv"
require "tempfile"
require "timeout"
require "term/ansicolor"
require "fileutils"
require "thread"
class Foreman::Engine
attr_reader :procfile
attr_reader :directory
# The signals that the engine cares about.
#
HANDLED_SIGNALS = [ :TERM, :INT, :HUP ]
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, blue,
intense_cyan, intense_yellow, intense_green, intense_magenta,
intense_red, intense_blue ]
@options[:formation] ||= (options[:concurrency] || "all=1")
@options[:timeout] ||= 5
def initialize(procfile, options={})
@procfile = Foreman::Procfile.new(procfile)
@directory = options[:app_root] || File.expand_path(File.dirname(procfile))
@options = options
@environment = read_environment_files(options[:env])
@output_mutex = Mutex.new
end
def self.load_env!(env_file)
@environment = read_environment_files(env_file)
apply_environment!
@env = {}
@mutex = Mutex.new
@names = {}
@processes = []
@running = {}
@readers = {}
# Self-pipe for deferred signal-handling (ala djb: http://cr.yp.to/docs/selfpipe.html)
reader, writer = create_pipe
reader.close_on_exec = true if reader.respond_to?(:close_on_exec)
writer.close_on_exec = true if writer.respond_to?(:close_on_exec)
@selfpipe = { :reader => reader, :writer => writer }
# Set up a global signal queue
# http://blog.rubybestpractices.com/posts/ewong/016-Implementing-Signal-Handlers.html
Thread.main[:signal_queue] = []
end
# Start the processes registered to this +Engine+
#
def start
proctitle "ruby: foreman master"
termtitle "#{File.basename(@directory)} - foreman"
trap("TERM") { puts "SIGTERM received"; terminate_gracefully }
trap("INT") { puts "SIGINT received"; terminate_gracefully }
assign_colors
register_signal_handlers
startup
spawn_processes
watch_for_output
watch_for_termination
sleep 0.1
watch_for_termination { terminate_gracefully }
shutdown
end
def port_for(process, num, base_port=nil)
base_port ||= 5000
offset = procfile.process_names.index(process.name) * 100
base_port.to_i + offset + num - 1
end
private ######################################################################
def spawn_processes
concurrency = Foreman::Utils.parse_concurrency(@options[:concurrency])
procfile.entries.each do |entry|
reader, writer = IO.pipe
entry.spawn(concurrency[entry.name], writer, @directory, @environment, port_for(entry, 1, base_port)).each do |process|
running_processes[process.pid] = process
readers[process] = reader
# Set up deferred signal handlers
#
def register_signal_handlers
HANDLED_SIGNALS.each do |sig|
if ::Signal.list.include? sig.to_s
trap(sig) { Thread.main[:signal_queue] << sig ; notice_signal }
end
end
end
def base_port
options[:port] || 5000
end
def kill_all(signal="SIGTERM")
running_processes.each do |pid, process|
info "sending #{signal} to pid #{pid}"
Process.kill(signal, pid) rescue Errno::ESRCH
# Unregister deferred signal handlers
#
def restore_default_signal_handlers
HANDLED_SIGNALS.each do |sig|
trap(sig, :DEFAULT) if ::Signal.list.include? sig.to_s
end
end
def terminate_gracefully
info "sending SIGTERM to all processes"
kill_all "SIGTERM"
Timeout.timeout(5) { Process.waitall }
rescue Timeout::Error
info "sending SIGKILL to all processes"
kill_all "SIGKILL"
# Wake the main thread up via the selfpipe when there's a signal
#
def notice_signal
@selfpipe[:writer].write_nonblock( '.' )
rescue Errno::EAGAIN
# Ignore writes that would block
rescue Errno::EINT
# Retry if another signal arrived while writing
retry
end
# Invoke the real handler for signal +sig+. This shouldn't be called directly
# by signal handlers, as it might invoke code which isn't re-entrant.
#
# @param [Symbol] sig the name of the signal to be handled
#
def handle_signal(sig)
case sig
when :TERM
handle_term_signal
when :INT
handle_interrupt
when :HUP
handle_hangup
else
system "unhandled signal #{sig}"
end
end
# Handle a TERM signal
#
def handle_term_signal
puts "SIGTERM received"
terminate_gracefully
end
# Handle an INT signal
#
def handle_interrupt
puts "SIGINT received"
terminate_gracefully
end
# Handle a HUP signal
#
def handle_hangup
puts "SIGHUP received"
terminate_gracefully
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)
@env.update Dotenv::Environment.new(filename)
end
# Send a signal to all processes started by this +Engine+
#
# @param [String] signal The signal to send to each process
#
def kill_children(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
end
else
begin
Process.kill signal, *@running.keys unless @running.empty?
rescue Errno::ESRCH, Errno::EPERM
end
end
end
# Send a signal to the whole process group.
#
# @param [String] signal The signal to send
#
def killall(signal="SIGTERM")
if Foreman.windows?
kill_children(signal)
else
begin
Process.kill "-#{signal}", Process.pid
rescue Errno::ESRCH, Errno::EPERM
end
end
end
# Get the process formation
#
# @returns [Fixnum] The formation count for the specified process
#
def formation
@formation ||= parse_formation(options[:formation])
end
# 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
# Get the root directory for this +Engine+
#
# @returns [String] The root directory
#
def root
File.expand_path(options[:root] || Dir.pwd)
end
# 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
# 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
# deprecated
def environment
env
end
private
### Engine API ######################################################
def startup
raise TypeError, "must use a subclass of Foreman::Engine"
end
def output(name, data)
raise TypeError, "must use a subclass of Foreman::Engine"
end
def shutdown
raise TypeError, "must use a subclass of Foreman::Engine"
end
## Helpers ##########################################################
def create_pipe
IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
end
def name_for(pid)
process, index = @running[pid]
name_for_index(process, index)
end
def name_for_index(process, index)
[ @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,
"PS" => name_for_index(process, n)
})
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
rs, ws = IO.select(readers.values, [], [], 1)
(rs || []).each do |r|
ps, message = r.gets.split(",", 2)
color = colors[ps.split(".").first]
info message, ps, color
io = IO.select([@selfpipe[:reader]] + @readers.values, nil, nil, 30)
begin
@selfpipe[:reader].read_nonblock(11)
rescue Errno::EAGAIN, Errno::EINTR => err
# ignore
end
# Look for any signals that arrived and handle them
while sig = Thread.main[:signal_queue].shift
self.handle_signal(sig)
end
(io.nil? ? [] : io.first).each do |reader|
next if reader == @selfpipe[:reader]
if reader.eof?
@readers.delete_if { |key, value| value == reader }
else
data = reader.gets
output_with_mutex name_for(@readers.invert[reader]), data
end
end
end
rescue Exception => ex
@@ -107,118 +404,30 @@ private ######################################################################
def watch_for_termination
pid, status = Process.wait2
process = running_processes.delete(pid)
info "process terminated", process.name
terminate_gracefully
kill_all
output_with_mutex name_for(pid), termination_message_for(status)
@running.delete(pid)
yield if block_given?
pid
rescue Errno::ECHILD
end
def info(message, name="system", color=Term::ANSIColor.white)
print color
print "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
print Term::ANSIColor.reset
print message.chomp
puts ""
end
def print(message=nil)
@output_mutex.synchronize do
$stdout.print message
def terminate_gracefully
return if @terminating
restore_default_signal_handlers
@terminating = true
if Foreman.windows?
system "sending SIGKILL to all processes"
kill_children "SIGKILL"
else
system "sending SIGTERM to all processes"
kill_children "SIGTERM"
end
end
def puts(message=nil)
@output_mutex.synchronize do
$stdout.puts message
Timeout.timeout(options[:timeout]) do
watch_for_termination while @running.length > 0
end
rescue Timeout::Error
system "sending SIGKILL to all processes"
kill_children "SIGKILL"
end
def error(message)
puts "ERROR: #{message}"
exit 1
end
def longest_process_name
@longest_process_name ||= begin
longest = procfile.process_names.map { |name| name.length }.sort.last
longest = 6 if longest < 6 # system
longest
end
end
def pad_process_name(name="system")
name.to_s.ljust(longest_process_name + 3) # add 3 for process number padding
end
def proctitle(title)
$0 = title
end
def termtitle(title)
printf("\033]0;#{title}\007")
end
def running_processes
@running_processes ||= {}
end
def readers
@readers ||= {}
end
def colors
@colors ||= {}
end
def assign_colors
procfile.entries.each do |entry|
colors[entry.name] = next_color
end
end
def process_by_reader(reader)
readers.invert[reader]
end
def next_color
@current_color ||= -1
@current_color += 1
@current_color = 0 if COLORS.length < @current_color
COLORS[@current_color]
end
module Env
attr_reader :environment
def read_environment_files(filenames)
environment = {}
(filenames || "").split(",").map(&:strip).each do |filename|
error "No such file: #{filename}" unless File.exists?(filename)
environment.merge!(read_environment(filename))
end
environment.merge!(read_environment(".env")) unless filenames
environment
end
def read_environment(filename)
return {} unless File.exists?(filename)
File.read(filename).split("\n").inject({}) do |hash, line|
if line =~ /\A([A-Za-z_0-9]+)=(.*)\z/
hash[$1] = $2
end
hash
end
end
def apply_environment!
@environment.each { |k,v| ENV[k] = v }
end
end
include Env
extend Env
end

104
lib/foreman/engine/cli.rb Normal file
View File

@@ -0,0 +1,104 @@
require "foreman/engine"
class Foreman::Engine::CLI < Foreman::Engine
module Color
ANSI = {
:reset => 0,
:black => 30,
:red => 31,
:green => 32,
:yellow => 33,
:blue => 34,
:magenta => 35,
:cyan => 36,
:white => 37,
:bright_black => 30,
:bright_red => 31,
:bright_green => 32,
:bright_yellow => 33,
:bright_blue => 34,
:bright_magenta => 35,
:bright_cyan => 36,
:bright_white => 37,
}
def self.enable(io, force=false)
io.extend(self)
@@color_force = force
end
def color?
return true if @@color_force
return false if Foreman.windows?
return false unless self.respond_to?(:isatty)
self.isatty && ENV["TERM"]
end
def color(name)
return "" unless color?
return "" unless ansi = ANSI[name.to_sym]
"\e[#{ansi}m"
end
end
FOREMAN_COLORS = %w( cyan yellow green magenta red blue bright_cyan bright_yellow
bright_green bright_magenta bright_red bright_blue )
def startup
@colors = map_colors
proctitle "foreman: master" unless Foreman.windows?
Color.enable($stdout, options[:color])
end
def output(name, data)
data.to_s.lines.map(&:chomp).each do |message|
output = ""
output += $stdout.color(@colors[name.split(".").first].to_sym)
output += "#{Time.now.strftime("%H:%M:%S")} #{pad_process_name(name)} | "
output += $stdout.color(:reset)
output += message
$stdout.puts output
$stdout.flush
end
rescue Errno::EPIPE
terminate_gracefully
end
def shutdown
end
private
def name_padding
@name_padding ||= begin
index_padding = @names.values.map { |n| formation[n] }.max.to_s.length + 1
name_padding = @names.values.map { |n| n.length + index_padding }.sort.last
[ 6, name_padding ].max
end
end
def pad_process_name(name)
name.ljust(name_padding, " ")
end
def map_colors
colors = Hash.new("white")
@names.values.each_with_index do |name, index|
colors[name] = FOREMAN_COLORS[index % FOREMAN_COLORS.length]
end
colors["system"] = "bright_white"
colors
end
def proctitle(title)
$0 = title
end
def termtitle(title)
printf("\033]0;#{title}\007") unless Foreman.windows?
end
end

View File

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

View File

@@ -1,20 +1,87 @@
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.mkdir_p(run) rescue error("Could not create: #{run}")
FileUtils.chown(user, nil, log) rescue error("Could not chown #{log} to #{user}")
FileUtils.chown(user, nil, run) rescue error("Could not chown #{run} to #{user}")
end
def app
options[:app] || "app"
end
def log
options[:log] || "/var/log/#{app}"
end
def run
options[:run] || "/var/run/#{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,8 +89,19 @@ private ######################################################################
def say(message)
puts "[foreman export] %s" % message
end
def clean(filename)
return unless File.exists?(filename)
say "cleaning up: #{filename}"
FileUtils.rm(filename)
end
def export_template(exporter, file, template_root)
def shell_quote(value)
Shellwords.escape(value)
end
# deprecated
def old_export_template(exporter, file, template_root)
if template_root && File.exist?(file_path = File.join(template_root, file))
File.read(file_path)
elsif File.exist?(file_path = File.expand_path(File.join("~/.foreman/templates", file)))
@@ -33,9 +111,39 @@ private ######################################################################
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

View File

@@ -3,26 +3,10 @@ require "foreman/export"
class Foreman::Export::Bluepill < Foreman::Export::Base
def export(location, options={})
error("Must specify a location") unless location
FileUtils.mkdir_p location
app = options[:app] || File.basename(engine.directory)
user = options[:user] || app
log_root = options[:log] || "/var/log/#{app}"
template_root = options[:template]
Dir["#{location}/#{app}.pill"].each do |file|
say "cleaning up: #{file}"
FileUtils.rm(file)
end
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
master_template = export_template("bluepill", "master.pill.erb", template_root)
master_config = ERB.new(master_template).result(binding)
write_file "#{location}/#{app}.pill", master_config
def export
super
clean "#{location}/#{app}.pill"
write_template "bluepill/master.pill.erb", "#{app}.pill", binding
end
end

View File

@@ -0,0 +1,28 @@
require "erb"
require "foreman/export"
class Foreman::Export::Daemon < Foreman::Export::Base
def export
super
(Dir["#{location}/#{app}-*.conf"] << "#{location}/#{app}.conf").each do |file|
clean file
end
write_template "daemon/master.conf.erb", "#{app}.conf", binding
engine.each_process do |name, process|
next if engine.formation[name] < 1
write_template "daemon/process_master.conf.erb", "#{app}-#{name}.conf", binding
1.upto(engine.formation[name]) do |num|
port = engine.port_for(process, num)
arguments = process.command.split(" ")
executable = arguments.slice!(0)
arguments = arguments.size > 0 ? " -- #{arguments.join(' ')}" : ""
write_template "daemon/process.conf.erb", "#{app}-#{name}-#{num}.conf", binding
end
end
end
end

View File

@@ -2,36 +2,40 @@ require "foreman/export"
class Foreman::Export::Inittab < Foreman::Export::Base
def export(fname=nil, options={})
app = options[:app] || File.basename(engine.directory)
user = options[:user] || app
log_root = options[:log] || "/var/log/#{app}"
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
def export
error("Must specify a location") unless location
inittab = []
inittab << "# ----- foreman #{app} processes -----"
engine.procfile.entries.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

View 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

View File

@@ -2,60 +2,33 @@ 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(location, options={})
error("Must specify a location") unless location
def export
super
app = options[:app] || File.basename(engine.directory)
user = options[:user] || app
log_root = options[:log] || "/var/log/#{app}"
template_root = options[:template]
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
run_template = export_template('runit', 'run.erb', template_root)
log_run_template = export_template('runit', 'log_run.erb', template_root)
engine.procfile.entries.each do |process|
1.upto(concurrency[process.name]) do |num|
process_directory = "#{location}/#{app}-#{process.name}-#{num}"
process_env_directory = "#{process_directory}/env"
process_log_directory = "#{process_directory}/log"
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_env_directory
create_directory process_log_directory
create_directory "#{process_directory}/env"
create_directory "#{process_directory}/log"
run = ERB.new(run_template).result(binding)
write_file "#{process_directory}/run", run
FileUtils.chmod 0755, "#{process_directory}/run"
write_template "runit/run.erb", "#{process_directory}/run", binding
chmod 0755, "#{process_directory}/run"
port = engine.port_for(process, num, options[:port])
environment_variables = {'PORT' => port}.
merge(engine.environment).
merge(inline_variables(process.command))
environment_variables.each_pair do |var, env|
write_file "#{process_env_directory}/#{var.upcase}", env
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
log_run = ERB.new(log_run_template).result(binding)
write_file "#{process_log_directory}/run", log_run
FileUtils.chmod 0755, "#{process_log_directory}/run"
write_template "runit/log/run.erb", "#{process_directory}/log/run", binding
chmod 0755, "#{process_directory}/log/run"
end
end
end
private
def create_directory(location)
say "creating: #{location}"
FileUtils.mkdir(location)
end
def inline_variables(command)
variable_name_regex =
Hash[*command.scan(ENV_VARIABLE_REGEX).flatten]
end
end

View 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

View File

@@ -0,0 +1,25 @@
require "erb"
require "foreman/export"
class Foreman::Export::Systemd < Foreman::Export::Base
def export
super
Dir["#{location}/#{app}*.target"].concat(Dir["#{location}/#{app}*.service"]).each do |file|
clean file
end
write_template "systemd/master.target.erb", "#{app}.target", binding
engine.each_process do |name, process|
next if engine.formation[name] < 1
write_template "systemd/process_master.target.erb", "#{app}-#{name}.target", binding
1.upto(engine.formation[name]) do |num|
port = engine.port_for(process, num)
write_template "systemd/process.service.erb", "#{app}-#{name}-#{num}.service", binding
end
end
end
end

View File

@@ -3,41 +3,23 @@ require "foreman/export"
class Foreman::Export::Upstart < Foreman::Export::Base
def export(location, options={})
error("Must specify a location") unless location
def export
super
FileUtils.mkdir_p location
app = options[:app] || File.basename(engine.directory)
user = options[:user] || app
log_root = options[:log] || "/var/log/#{app}"
template_root = options[:template]
Dir["#{location}/#{app}*.conf"].each do |file|
say "cleaning up: #{file}"
FileUtils.rm(file)
(Dir["#{location}/#{app}-*.conf"] << "#{location}/#{app}.conf").each do |file|
clean file
end
concurrency = Foreman::Utils.parse_concurrency(options[:concurrency])
write_template "upstart/master.conf.erb", "#{app}.conf", binding
master_template = export_template("upstart", "master.conf.erb", template_root)
master_config = ERB.new(master_template).result(binding)
write_file "#{location}/#{app}.conf", master_config
engine.each_process do |name, process|
next if engine.formation[name] < 1
write_template "upstart/process_master.conf.erb", "#{app}-#{name}.conf", binding
process_template = export_template("upstart", "process.conf.erb", template_root)
engine.procfile.entries.each do |process|
next if (conc = concurrency[process.name]) < 1
process_master_template = export_template("upstart", "process_master.conf.erb", template_root)
process_master_config = ERB.new(process_master_template).result(binding)
write_file "#{location}/#{app}-#{process.name}.conf", process_master_config
1.upto(concurrency[process.name]) do |num|
port = engine.port_for(process, num, options[:port])
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
View 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

View File

@@ -1,81 +1,129 @@
require "foreman"
require "rubygems"
require "shellwords"
class Foreman::Process
attr_reader :entry
attr_reader :num
attr_reader :pid
attr_reader :port
attr_reader :command
attr_reader :env
def initialize(entry, num, port)
@entry = entry
@num = num
@port = port
# 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
def run(pipe, basedir, environment)
with_environment(environment.merge("PORT" => port.to_s)) do
run_process basedir, entry.command, pipe
# 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
runner = "#{Foreman.runner}".shellescape
if Foreman.windows?
Dir.chdir(cwd) do
Process.spawn env, expanded_command(env), :out => output, :err => output
end
elsif Foreman.jruby_18? || Foreman.ruby_18?
require "posix/spawn"
wrapped_command = "#{runner} -d '#{cwd.shellescape}' -p -- #{expanded_command(env)}"
POSIX::Spawn.spawn(*spawn_args(env, wrapped_command.shellsplit, {:out => output, :err => output}))
else
wrapped_command = "#{runner} -d '#{cwd.shellescape}' -p -- #{command}"
Process.spawn env, wrapped_command, :out => output, :err => output
end
end
def name
"%s.%s" % [ entry.name, num ]
# 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
private
def jruby?
defined?(RUBY_PLATFORM) and RUBY_PLATFORM == "java"
end
def fork_with_io(command, basedir)
reader, writer = IO.pipe
command = replace_command_env(command)
pid = if jruby?
require "posix/spawn"
POSIX::Spawn.spawn(Foreman.runner, "-d", basedir, command, {
:out => writer, :err => writer
})
else
fork do
trap("INT", "IGNORE")
writer.sync = true
$stdout.reopen writer
$stderr.reopen writer
reader.close
exec Foreman.runner, "-d", basedir, command
end
end
[ reader, pid ]
end
def run_process(basedir, command, pipe)
io, @pid = fork_with_io(command, basedir)
output pipe, "started with pid %d" % @pid
Thread.new do
until io.eof?
output pipe, io.gets
end
end
end
def output(pipe, message)
pipe.puts "%s,%s" % [ name, message ]
end
def replace_command_env(command)
command.gsub(/\$(\w+)/) { |e| ENV[e[1..-1]] }
end
def with_environment(environment)
old_env = ENV.each_pair.inject({}) { |h,(k,v)| h.update(k => v) }
environment.each { |k,v| ENV[k] = v }
ret = yield
ENV.clear
old_env.each { |k,v| ENV[k] = v}
ret
def spawn_args(env, argv, options)
args = []
args << env
args += argv
args << options
args
end
end

View File

@@ -1,36 +1,90 @@
require "foreman"
require "foreman/procfile_entry"
# A valid Procfile entry is captured by this regex.
# Reads and writes Procfiles
#
# A valid Procfile entry is captured by this regex:
#
# /^([A-Za-z0-9_]+):\s*(.+)$/
#
# All other lines are ignored.
#
# /^([A-Za-z0-9_]+):\s*(.+)$/
#
# $1 = name
# $2 = command
#
class Foreman::Procfile
attr_reader :entries
def initialize(filename)
@entries = parse_procfile(filename)
# 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 { |entry| entry.name == name }
@entries.detect { |n,c| name == n }.last
end
def process_names
entries.map(&:name)
# 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_procfile(filename)
File.read(filename).split("\n").map do |line|
if line =~ /^([A-Za-z0-9_]+):\s*(.+)$/
Foreman::ProcfileEntry.new($1, $2)
def parse(filename)
File.read(filename).gsub("\r\n","\n").split("\n").map do |line|
if line =~ /^([A-Za-z0-9_-]+):\s*(.+)$/
[$1, $2]
end
end.compact
end

View File

@@ -1,22 +0,0 @@
require "foreman"
class Foreman::ProcfileEntry
attr_reader :name
attr_reader :command
attr_accessor :color
def initialize(name, command)
@name = name
@command = command
end
def spawn(num, pipe, basedir, environment, base_port)
(1..num).to_a.map do |n|
process = Foreman::Process.new(self, n, base_port + (n-1))
process.run(pipe, basedir, environment)
process
end
end
end

View File

@@ -1,18 +0,0 @@
require "foreman"
class Foreman::Utils
def self.parse_concurrency(concurrency)
begin
pairs = concurrency.to_s.gsub(/\s/, "").split(",")
default = concurrency.nil? ? 1 : 0
pairs.inject(Hash.new(default)) do |hash, pair|
process, amount = pair.split("=")
hash.update(process => amount.to_i)
end
end
end
end

View File

@@ -1,5 +1,5 @@
module Foreman
VERSION = "0.36.1"
VERSION = "0.63.0"
end

View File

@@ -1,7 +1,7 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "FOREMAN" "1" "January 2012" "Foreman 0.33.1" "Foreman Manual"
.TH "FOREMAN" "1" "May 2013" "Foreman 0.63.0" "Foreman Manual"
.
.SH "NAME"
\fBforeman\fR \- manage Procfile\-based applications
@@ -10,10 +10,13 @@
\fBforeman start [process]\fR
.
.br
\fBforeman run <command>\fR
.
.br
\fBforeman export <format> [location]\fR
.
.SH "DESCRIPTION"
\fBForeman\fR is a manager for Procfile\-based applications\. Its aim is to abstract away the details of the Procfile format, and allow you to either run your application directly or export it to some other process management format\.
Foreman is a manager for Procfile\-based applications\. Its aim is to abstract away the details of the Procfile format, and allow you to either run your application directly or export it to some other process management format\.
.
.SH "RUNNING"
\fBforeman start\fR is used to run your application directly from the command line\.
@@ -32,9 +35,20 @@ The following options control how the application is run:
Specify the number of each process type to run\. The value passed in should be in the format \fBprocess=num,process=num\fR
.
.TP
\fB\-e\fR, \fB\-\-env\fR
Specify one or more \.env files to load
.
.TP
\fB\-f\fR, \fB\-\-procfile\fR
Specify an alternate Procfile to load, implies \fB\-d\fR at the Procfile root\.
.
.TP
\fB\-p\fR, \fB\-\-port\fR
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
.
.P
\fBforeman run\fR is used to run one\-off commands using the same environment as your defined processes\.
.
.SH "EXPORTING"
\fBforeman export\fR is used to export your application to another process management format\.
.
@@ -61,14 +75,18 @@ Specify the directory to place process logs in\.
Specify which port to use as the base for this application\. Should be a multiple of 1000\.
.
.TP
\fB\-t\fR, \fB\-\-template\fR
Specify an alternate template to use for creating export files\. See \fIhttps://github\.com/ddollar/foreman/tree/master/data/export\fR for examples\.
.
.TP
\fB\-u\fR, \fB\-\-user\fR
Specify the user the application should be run as\. Defaults to the app name
.
.SH "OPTIONS"
.SH "GLOBAL OPTIONS"
These options control all modes of foreman\'s operation\.
.
.TP
\fB\-d\fR, \fB\-\-directory\fR
\fB\-d\fR, \fB\-\-root\fR
Specify an alternate application root\. This defaults to the directory containing the Procfile\.
.
.TP
@@ -89,9 +107,18 @@ bluepill
inittab
.
.IP "\(bu" 4
launchd
.
.IP "\(bu" 4
runit
.
.IP "\(bu" 4
supervisord
.
.IP "\(bu" 4
systemd
.
.IP "\(bu" 4
upstart
.
.IP "" 0
@@ -112,6 +139,18 @@ EX02:4:respawn:/bin/su \- example \-c \'PORT=5100 bundle exec rake jobs:work >>
.
.IP "" 0
.
.SH "SYSTEMD EXPORT"
Will create a series of systemd scripts in the location you specify\. Scripts will be structured to make the following commands valid:
.
.P
\fBsystemctl start appname\.target\fR
.
.P
\fBsystemctl stop appname\-processname\.target\fR
.
.P
\fBsystemctl restart appname\-processname\-3\.service\fR
.
.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:
.
@@ -139,7 +178,7 @@ job: bundle exec rake jobs:work
.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:
A process name may contain letters, numbers and the underscore character\. You can validate your Procfile format using the \fBcheck\fR command:
.
.IP "" 4
.
@@ -212,7 +251,7 @@ Run one process type from the application defined in a specific Procfile:
.
.nf
$ foreman start alpha \-p ~/myapp/Procfile
$ foreman start alpha \-f ~/myapp/Procfile
.
.fi
.

View File

@@ -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,19 @@ 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.
`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,15 +68,19 @@ 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`:
* `-d`, `--root`:
Specify an alternate application root. This defaults to the directory
containing the Procfile.
@@ -87,8 +101,14 @@ foreman currently supports the following output formats:
* inittab
* launchd
* runit
* supervisord
* systemd
* upstart
## INITTAB EXPORT
@@ -100,6 +120,17 @@ Will export a chunk of inittab-compatible configuration:
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 -----
## SYSTEMD EXPORT
Will create a series of systemd scripts in the location you specify. Scripts
will be structured to make the following commands valid:
`systemctl start appname.target`
`systemctl stop appname-processname.target`
`systemctl restart appname-processname-3.service`
## UPSTART EXPORT
Will create a series of upstart scripts in the location you specify. Scripts
@@ -119,7 +150,7 @@ to run it.
web: bundle exec thin start
job: bundle exec rake jobs:work
A process name may contain letters, numbers amd the underscore character.
A process name may contain letters, numbers and the underscore character.
You can validate your Procfile format using the `check` command:
$ foreman check
@@ -154,7 +185,7 @@ Export the application in upstart format:
Run one process type from the application defined in a specific Procfile:
$ foreman start alpha -p ~/myapp/Procfile
$ foreman start alpha -f ~/myapp/Procfile
## COPYRIGHT

View File

@@ -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,129 +27,85 @@ 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 "options" do
it "respects --env" do
write_procfile
write_env("envfile")
mock.instance_of(Foreman::Export::Upstart).export("/upstart", { "env" => "envfile" })
foreman %{ export upstart /upstart --env envfile }
end
end
describe "with a non-existent Procfile" do
it "prints an error" do
mock_error(subject, "Procfile does not exist.") do
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 "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
it "runs successfully" do
dont_allow(subject).error
mock.instance_of(Foreman::Export::Upstart).export("/tmp/foo", {})
subject.export("upstart", "/tmp/foo")
it "sets PS variable with the process name" do
without_fakefs do
output = foreman("start -f #{resource_path("Procfile")}")
output.should =~ /ps.1 \| PS env var is ps.1/
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, foo_bar, foo-bar)\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
describe "with a valid Procfile" do
before { write_procfile }
it "can run a command" do
forked_foreman("run echo 1").should == "1\n"
end
describe "and a command" do
let(:command) { ["ls", "-l"] }
before(:each) do
stub(subject).exec
end
it "should load the environment file" do
write_env
preserving_env do
subject.run *command
ENV["FOO"].should == "bar"
end
ENV["FOO"].should be_nil
end
it "should runute the command as a string" do
mock(subject).exec(command.join(" "))
subject.run *command
end
end
describe "and a non-existent command" do
let(:command) { "iuhtngrglhulhdfg" }
it "should print an error" do
mock_error(subject, "command not found: #{command}") do
subject.run command
end
end
end
describe "and a non-executable command" do
let(:command) { __FILE__ }
it "should print an error" do
mock_error(subject, "not executable: #{command}") do
subject.run command
end
end
end
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
describe "when posix-spawn is not present on ruby 1.8" do
it "should fail with an error" do
mock(Kernel).require('posix/spawn') { raise LoadError }
output = foreman("start -f #{resource_path("Procfile")}")
output.should == "ERROR: foreman requires gem `posix-spawn` on Ruby #{RUBY_VERSION}. Please `gem install posix-spawn`.\n"
end
end if running_ruby_18?
end

View File

@@ -1,86 +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.procfile["alpha"].command.should == "./alpha"
subject.procfile["bravo"].command.should == "./bravo"
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.instance_of(Foreman::Process).run_process("./alpha", is_a(IO))
mock.instance_of(Foreman::Process).run_process("./bravo", is_a(IO))
mock(subject.process("alpha")).run(anything)
mock(subject.process("bravo")).run(anything)
mock(subject).watch_for_output
mock(subject).watch_for_termination
subject.start
end
it "handles concurrency" do
write_procfile
engine = Foreman::Engine.new("Procfile",:concurrency => "alpha=2")
mock.instance_of(Foreman::Process).run_process("./alpha", is_a(IO)).twice
mock.instance_of(Foreman::Process).run_process("./bravo", is_a(IO)).never
mock(engine).watch_for_output
mock(engine).watch_for_termination
engine.start
subject.options[:formation] = "alpha=2"
mock(subject.process("alpha")).run(anything).twice
mock(subject.process("bravo")).run(anything).never
mock(subject).watch_for_output
mock(subject).watch_for_termination
subject.start
end
end
describe "directories" do
it "has the directory default relative to the Procfile" do
write_procfile "/some/app/Procfile"
engine = Foreman::Engine.new.load_procfile("/some/app/Procfile")
engine.root.should == "/some/app"
end
end
describe "environment" do
before(:each) do
write_procfile
stub(Process).fork
end
it "should read if specified" do
it "should read env files" do
File.open("/tmp/env", "w") { |f| f.puts("FOO=baz") }
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
stub(engine).info
mock(engine).spawn_processes
mock(engine).watch_for_termination
engine.environment.should == {"FOO"=>"baz"}
engine.start
subject.load_env("/tmp/env")
subject.env["FOO"].should == "baz"
end
it "should read more than one if specified" do
File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
File.open("/tmp/env2", "w") { |f| f.puts("BAZ=qux") }
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env1,/tmp/env2")
stub(engine).info
mock(engine).spawn_processes
mock(engine).watch_for_termination
engine.environment.should == { "FOO"=>"bar", "BAZ"=>"qux" }
engine.start
subject.load_env "/tmp/env1"
subject.load_env "/tmp/env2"
subject.env["FOO"].should == "bar"
subject.env["BAZ"].should == "qux"
end
it "should handle quoted values" do
File.open("/tmp/env", "w") do |f|
f.puts 'FOO=bar'
f.puts 'BAZ="qux"'
f.puts "FRED='barney'"
f.puts 'OTHER="escaped\"quote"'
end
subject.load_env "/tmp/env"
subject.env["FOO"].should == "bar"
subject.env["BAZ"].should == "qux"
subject.env["FRED"].should == "barney"
subject.env["OTHER"].should == 'escaped"quote'
end
it "should handle multiline strings" do
File.open("/tmp/env", "w") do |f|
f.puts 'FOO="bar\nbaz"'
end
subject.load_env "/tmp/env"
subject.env["FOO"].should == "bar\nbaz"
end
it "should fail if specified and doesnt exist" do
mock.instance_of(Foreman::Engine).error("No such file: /tmp/env")
engine = Foreman::Engine.new("Procfile", :env => "/tmp/env")
lambda { subject.load_env "/tmp/env" }.should raise_error(Errno::ENOENT)
end
it "should read .env if none specified" do
File.open(".env", "w") { |f| f.puts("FOO=qoo") }
engine = Foreman::Engine.new("Procfile")
mock(engine).spawn_processes
mock(engine).watch_for_termination
engine.environment.should == {"FOO"=>"qoo"}
engine.start
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

View 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

View File

@@ -3,22 +3,35 @@ require "foreman/engine"
require "foreman/export/bluepill"
require "tmpdir"
describe Foreman::Export::Bluepill do
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
let(:engine) { Foreman::Engine.new(procfile) }
let(:bluepill) { Foreman::Export::Bluepill.new(engine) }
describe Foreman::Export::Bluepill, :fakefs do
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
let(:formation) { nil }
let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(procfile) }
let(:options) { Hash.new }
let(:bluepill) { Foreman::Export::Bluepill.new("/tmp/init", engine, options) }
before(:each) { load_export_templates_into_fakefs("bluepill") }
before(:each) { stub(bluepill).say }
it "exports to the filesystem" do
bluepill.export("/tmp/init")
bluepill.export
normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app.pill"))
end
it "exports to the filesystem with concurrency" do
bluepill.export("/tmp/init", :concurrency => "alpha=2")
normalize_space(File.read("/tmp/init/app.pill")).should == normalize_space(example_export_file("bluepill/app-concurrency.pill"))
it "cleans up if exporting into an existing dir" do
mock(FileUtils).rm("/tmp/init/app.pill")
bluepill.export
bluepill.export
end
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

View File

@@ -0,0 +1,97 @@
require "spec_helper"
require "foreman/engine"
require "foreman/export/daemon"
require "tmpdir"
describe Foreman::Export::Daemon, :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(:daemon) { Foreman::Export::Daemon.new("/tmp/init", engine, options) }
before(:each) { load_export_templates_into_fakefs("daemon") }
before(:each) { stub(daemon).say }
it "exports to the filesystem" do
daemon.export
File.read("/tmp/init/app.conf").should == example_export_file("daemon/app.conf")
File.read("/tmp/init/app-alpha.conf").should == example_export_file("daemon/app-alpha.conf")
File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("daemon/app-alpha-1.conf")
File.read("/tmp/init/app-bravo.conf").should == example_export_file("daemon/app-bravo.conf")
File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("daemon/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")
mock(FileUtils).rm("/tmp/init/app-foo-bar.conf")
mock(FileUtils).rm("/tmp/init/app-foo-bar-1.conf")
mock(FileUtils).rm("/tmp/init/app-foo_bar.conf")
mock(FileUtils).rm("/tmp/init/app-foo_bar-1.conf")
daemon.export
daemon.export
end
it "does not delete exported files for similarly named applications" do
FileUtils.mkdir_p "/tmp/init"
["app2", "app2-alpha", "app2-alpha-1"].each do |name|
path = "/tmp/init/#{name}.conf"
FileUtils.touch(path)
dont_allow(FileUtils).rm(path)
end
daemon.export
end
context "with a formation" do
let(:formation) { "alpha=2" }
it "exports to the filesystem with concurrency" do
daemon.export
File.read("/tmp/init/app.conf").should == example_export_file("daemon/app.conf")
File.read("/tmp/init/app-alpha.conf").should == example_export_file("daemon/app-alpha.conf")
File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("daemon/app-alpha-1.conf")
File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("daemon/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
daemon.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/daemon")
File.open(File.expand_path("~/.foreman/templates/daemon/master.conf.erb"), "w") do |file|
file.puts "default_alternate_template"
end
end
it "can export with alternate template files" do
daemon.export
File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
end
end
end

View File

@@ -0,0 +1,40 @@
require "spec_helper"
require "foreman/engine"
require "foreman/export/inittab"
require "tmpdir"
describe Foreman::Export::Inittab, :fakefs do
let(: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

View 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

View File

@@ -3,34 +3,34 @@ require "foreman/engine"
require "foreman/export/runit"
require "tmpdir"
describe Foreman::Export::Runit do
describe Foreman::Export::Runit, :fakefs do
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile", 'bar=baz') }
let(:engine) { Foreman::Engine.new(procfile) }
let(:runit) { Foreman::Export::Runit.new(engine) }
let(: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
FileUtils.mkdir_p('/tmp/init')
runit.export('/tmp/init', :concurrency => "alpha=2,bravo=1")
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')
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-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-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
end
it "creates a full path to the export directory" do
expect { runit.export }.to_not raise_error(Errno::ENOENT)
end
end

View 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

View File

@@ -0,0 +1,91 @@
require "spec_helper"
require "foreman/engine"
require "foreman/export/systemd"
require "tmpdir"
describe Foreman::Export::Systemd, :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(:systemd) { Foreman::Export::Systemd.new("/tmp/init", engine, options) }
before(:each) { load_export_templates_into_fakefs("systemd") }
before(:each) { stub(systemd).say }
it "exports to the filesystem" do
systemd.export
File.read("/tmp/init/app.target").should == example_export_file("systemd/app.target")
File.read("/tmp/init/app-alpha.target").should == example_export_file("systemd/app-alpha.target")
File.read("/tmp/init/app-alpha-1.service").should == example_export_file("systemd/app-alpha-1.service")
File.read("/tmp/init/app-bravo.target").should == example_export_file("systemd/app-bravo.target")
File.read("/tmp/init/app-bravo-1.service").should == example_export_file("systemd/app-bravo-1.service")
end
it "cleans up if exporting into an existing dir" do
mock(FileUtils).rm("/tmp/init/app.target")
mock(FileUtils).rm("/tmp/init/app-alpha.target")
mock(FileUtils).rm("/tmp/init/app-alpha-1.service")
mock(FileUtils).rm("/tmp/init/app-bravo.target")
mock(FileUtils).rm("/tmp/init/app-bravo-1.service")
mock(FileUtils).rm("/tmp/init/app-foo-bar.target")
mock(FileUtils).rm("/tmp/init/app-foo-bar-1.service")
mock(FileUtils).rm("/tmp/init/app-foo_bar.target")
mock(FileUtils).rm("/tmp/init/app-foo_bar-1.service")
systemd.export
systemd.export
end
it "includes environment variables" do
engine.env['KEY'] = 'some "value"'
systemd.export
File.read("/tmp/init/app-alpha-1.service").should =~ /KEY=some "value"$/
end
context "with a formation" do
let(:formation) { "alpha=2" }
it "exports to the filesystem with concurrency" do
systemd.export
File.read("/tmp/init/app.target").should == example_export_file("systemd/app.target")
File.read("/tmp/init/app-alpha.target").should == example_export_file("systemd/app-alpha.target")
File.read("/tmp/init/app-alpha-1.service").should == example_export_file("systemd/app-alpha-1.service")
File.read("/tmp/init/app-alpha-2.service").should == example_export_file("systemd/app-alpha-2.service")
File.exists?("/tmp/init/app-bravo-1.service").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.target.erb", "w") { |f| f.puts "alternate_template" }
end
it "can export with alternate template files" do
systemd.export
File.read("/tmp/init/app.target").should == "alternate_template\n"
end
end
context "with alternate templates from home dir" do
before do
FileUtils.mkdir_p File.expand_path("~/.foreman/templates/systemd")
File.open(File.expand_path("~/.foreman/templates/systemd/master.target.erb"), "w") do |file|
file.puts "default_alternate_template"
end
end
it "can export with alternate template files" do
systemd.export
File.read("/tmp/init/app.target").should == "default_alternate_template\n"
end
end
end

View File

@@ -3,16 +3,18 @@ require "foreman/engine"
require "foreman/export/upstart"
require "tmpdir"
describe Foreman::Export::Upstart do
let(:procfile) { FileUtils.mkdir_p("/tmp/app"); write_procfile("/tmp/app/Procfile") }
let(:engine) { Foreman::Engine.new(procfile) }
let(:upstart) { Foreman::Export::Upstart.new(engine) }
describe Foreman::Export::Upstart, :fakefs do
let(:procfile) { write_procfile("/tmp/app/Procfile") }
let(:formation) { nil }
let(:engine) { Foreman::Engine.new(:formation => formation).load_procfile(procfile) }
let(:options) { Hash.new }
let(:upstart) { Foreman::Export::Upstart.new("/tmp/init", engine, options) }
before(:each) { load_export_templates_into_fakefs("upstart") }
before(:each) { stub(upstart).say }
it "exports to the filesystem" do
upstart.export("/tmp/init")
upstart.export
File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
@@ -21,48 +23,80 @@ describe Foreman::Export::Upstart do
File.read("/tmp/init/app-bravo-1.conf").should == example_export_file("upstart/app-bravo-1.conf")
end
it "exports to the filesystem with concurrency" do
upstart.export("/tmp/init", :concurrency => "alpha=2")
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")
mock(FileUtils).rm("/tmp/init/app-foo-bar.conf")
mock(FileUtils).rm("/tmp/init/app-foo-bar-1.conf")
mock(FileUtils).rm("/tmp/init/app-foo_bar.conf")
mock(FileUtils).rm("/tmp/init/app-foo_bar-1.conf")
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
upstart.export
upstart.export
end
it "does not delete exported files for similarly named applications" do
FileUtils.mkdir_p "/tmp/init"
["app2", "app2-alpha", "app2-alpha-1"].each do |name|
path = "/tmp/init/#{name}.conf"
FileUtils.touch(path)
dont_allow(FileUtils).rm(path)
end
upstart.export
end
it "quotes and escapes environment variables" do
engine.env['KEY'] = 'd"\|d'
upstart.export
"foobarfoo".should include "bar"
File.read("/tmp/init/app-alpha-1.conf").should =~ /KEY=d\\"\\\\\\\|d/
end
context "with a formation" do
let(:formation) { "alpha=2" }
it "exports to the filesystem with concurrency" do
upstart.export
File.read("/tmp/init/app.conf").should == example_export_file("upstart/app.conf")
File.read("/tmp/init/app-alpha.conf").should == example_export_file("upstart/app-alpha.conf")
File.read("/tmp/init/app-alpha-1.conf").should == example_export_file("upstart/app-alpha-1.conf")
File.read("/tmp/init/app-alpha-2.conf").should == example_export_file("upstart/app-alpha-2.conf")
File.exists?("/tmp/init/app-bravo-1.conf").should == false
end
end
context "with alternate templates" do
let(:template_root) { "/tmp/alternate" }
let(:template) { "/tmp/alternate" }
let(:options) { { :app => "app", :template => template } }
before do
FileUtils.mkdir_p template_root
File.open("#{template_root}/master.conf.erb", "w") { |f| f.puts "alternate_template" }
FileUtils.mkdir_p template
File.open("#{template}/master.conf.erb", "w") { |f| f.puts "alternate_template" }
end
it "can export with alternate template files" do
upstart.export("/tmp/init", :template => template_root)
upstart.export
File.read("/tmp/init/app.conf").should == "alternate_template\n"
end
end
context "with alternate templates from home dir" do
let(:default_template_root) {File.expand_path("#{ENV['HOME']}/.foreman/templates")}
before do
ENV['_FOREMAN_SPEC_HOME'] = ENV['HOME']
ENV['HOME'] = "/home/appuser"
FileUtils.mkdir_p default_template_root
File.open("#{default_template_root}/master.conf.erb", "w") { |f| f.puts "default_alternate_template" }
end
after do
ENV['HOME'] = ENV.delete('_FOREMAN_SPEC_HOME')
FileUtils.mkdir_p File.expand_path("~/.foreman/templates/upstart")
File.open(File.expand_path("~/.foreman/templates/upstart/master.conf.erb"), "w") do |file|
file.puts "default_alternate_template"
end
end
it "can export with alternate template files" do
upstart.export("/tmp/init")
upstart.export
File.read("/tmp/init/app.conf").should == "default_alternate_template\n"
end
end

View File

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

View File

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

View File

@@ -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 == (Foreman.ruby_18? ? "\xFF\x03\n" : "\xFF\x03\n".force_encoding('binary'))
end
end
end

View File

@@ -0,0 +1,43 @@
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"
procfile["foo-bar"].should == "./foo-bar"
procfile["foo_bar"].should == "./foo_bar"
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

View File

@@ -8,26 +8,9 @@ describe Foreman do
it { should be_a String }
end
describe "::load_env!(env_file)" do
before do
FakeFS.activate!
end
after do
FakeFS.deactivate!
ENV['FOO'] = nil
end
it "should load env_file into ENV" do
File.open("/tmp/env1", "w") { |f| f.puts("FOO=bar") }
Foreman.load_env!("/tmp/env1")
ENV['FOO'].should == 'bar'
end
it "should assume env_file in ./.env" do
File.open("./.env", "w") { |f| f.puts("FOO=bar") }
Foreman.load_env!
ENV['FOO'].should == 'bar'
describe "runner" do
it "should exist" do
File.exists?(Foreman.runner).should == true
end
end
end

View File

@@ -3,16 +3,16 @@ 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
end

5
spec/resources/Procfile Normal file
View File

@@ -0,0 +1,5 @@
echo: bin/echo echoing
env: bin/env FOO
test: bin/test
utf8: bin/utf8
ps: bin/echo PS env var is $PS

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

@@ -0,0 +1,2 @@
#!/bin/sh
echo $*

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

@@ -0,0 +1,2 @@
#!/bin/bash
echo ${!1}

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

@@ -0,0 +1,2 @@
#!/bin/sh
echo "testing"

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

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

View File

@@ -11,13 +11,14 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
process.working_dir = "/tmp/app"
process.daemonize = true
process.environment = {"PORT" => "5000"}
process.environment = {"PORT"=>"5000"}
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
process.stop_grace_time = 45.seconds
process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}"
children.stop_command "kill {{PID}}"
end
process.group = "app-alpha"
@@ -29,13 +30,14 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
process.working_dir = "/tmp/app"
process.daemonize = true
process.environment = {"PORT" => "5001"}
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 -QUIT {{PID}}"
children.stop_command "kill {{PID}}"
end
process.group = "app-alpha"

View File

@@ -11,13 +11,14 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
process.working_dir = "/tmp/app"
process.daemonize = true
process.environment = {"PORT" => "5000"}
process.environment = {"PORT"=>"5000"}
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
process.stop_grace_time = 45.seconds
process.stdout = process.stderr = "/var/log/app/app-alpha-1.log"
process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}"
children.stop_command "kill {{PID}}"
end
process.group = "app-alpha"
@@ -28,17 +29,53 @@ Bluepill.application("app", :foreground => false, :log_file => "/var/log/bluepil
process.working_dir = "/tmp/app"
process.daemonize = true
process.environment = {"PORT" => "5100"}
process.environment = {"PORT"=>"5100"}
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
process.stop_grace_time = 45.seconds
process.stdout = process.stderr = "/var/log/app/app-bravo-1.log"
process.monitor_children do |children|
children.stop_command "kill -QUIT {{PID}}"
children.stop_command "kill {{PID}}"
end
process.group = "app-bravo"
end
app.process("foo_bar-1") do |process|
process.start_command = "./foo_bar"
process.working_dir = "/tmp/app"
process.daemonize = true
process.environment = {"PORT"=>"5200"}
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
process.stop_grace_time = 45.seconds
process.stdout = process.stderr = "/var/log/app/app-foo_bar-1.log"
process.monitor_children do |children|
children.stop_command "kill {{PID}}"
end
process.group = "app-foo_bar"
end
app.process("foo-bar-1") do |process|
process.start_command = "./foo-bar"
process.working_dir = "/tmp/app"
process.daemonize = true
process.environment = {"PORT"=>"5300"}
process.stop_signals = [:quit, 30.seconds, :term, 5.seconds, :kill]
process.stop_grace_time = 45.seconds
process.stdout = process.stderr = "/var/log/app/app-foo-bar-1.log"
process.monitor_children do |children|
children.stop_command "kill {{PID}}"
end
process.group = "app-foo-bar"
end
end

View File

@@ -0,0 +1,7 @@
start on starting app-alpha
stop on stopping app-alpha
respawn
env PORT=5000
exec start-stop-daemon --start --chuid app --chdir /tmp/app --make-pidfile --pidfile /var/run/app/app-alpha-1.pid --exec ./alpha >> /var/log/app/app-alpha-1.log 2>&1

View File

@@ -0,0 +1,7 @@
start on starting app-alpha
stop on stopping app-alpha
respawn
env PORT=5001
exec start-stop-daemon --start --chuid app --chdir /tmp/app --make-pidfile --pidfile /var/run/app/app-alpha-2.pid --exec ./alpha >> /var/log/app/app-alpha-2.log 2>&1

View File

@@ -0,0 +1,2 @@
start on starting app
stop on stopping app

View File

@@ -0,0 +1,7 @@
start on starting app-bravo
stop on stopping app-bravo
respawn
env PORT=5100
exec start-stop-daemon --start --chuid app --chdir /tmp/app --make-pidfile --pidfile /var/run/app/app-bravo-1.pid --exec ./bravo >> /var/log/app/app-bravo-1.log 2>&1

View File

@@ -0,0 +1,2 @@
start on starting app
stop on stopping app

View File

@@ -0,0 +1,14 @@
pre-start script
bash << "EOF"
mkdir -p /var/log/app
chown -R app /var/log/app
mkdir -p /var/run/app
chown -R app /var/run/app
EOF
end script
start on runlevel [2345]
stop on runlevel [016]

View 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 -----

View File

@@ -0,0 +1,6 @@
# ----- 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'
AP03:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5200;./foo_bar >> /var/log/app/foo_bar-1.log 2>&1'
AP04:4:respawn:/bin/su - app -c 'cd /tmp/app;export PORT=5300;./foo-bar >> /var/log/app/foo-bar-1.log 2>&1'
# ----- end foreman app processes -----

View 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>

View 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>

View 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>

Some files were not shown because too many files have changed in this diff Show More