rerun 0.6.2 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +60 -24
- data/Rakefile +15 -1
- data/bin/rerun +1 -3
- data/lib/rerun/runner.rb +109 -9
- data/rerun.gemspec +1 -1
- metadata +23 -40
data/README.md
CHANGED
@@ -2,29 +2,37 @@
|
|
2
2
|
|
3
3
|
<http://github.com/alexch/rerun>
|
4
4
|
|
5
|
-
|
6
|
-
changes, then it restarts your
|
5
|
+
Rerun launches your program, then watches the filesystem. If a relevant file
|
6
|
+
changes, then it restarts your program.
|
7
7
|
|
8
|
-
|
9
|
-
|
8
|
+
Rerun works for both long-running processes (e.g. apps) and short-running ones
|
9
|
+
(e.g. tests). So it works like shotgun and autotest (and guard and all the
|
10
|
+
rest).
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
available on the `PATH`, it sends notifications to growl in addition to
|
15
|
-
the console. Here's how to install
|
16
|
-
[growlnotify](http://growl.info/extras.php#growlnotify):
|
12
|
+
Rerun's advantage is its simple design. Since it uses standard Unix "SIGINT"
|
13
|
+
and "SIGKILL" signals, you're sure the restarted app is really acting just
|
14
|
+
like it was when you ran it from the command line the first time.
|
17
15
|
|
18
|
-
|
16
|
+
By default only *.{rb,js,css,scss,sass,erb,html,haml,ru} files are watched.
|
17
|
+
Use the `--pattern` option if you want to change this.
|
18
|
+
|
19
|
+
If you're on Mac OS X, and using the built-in ruby, it uses the built-in
|
20
|
+
facilities for monitoring the filesystem, so CPU use is very light.
|
19
21
|
|
20
22
|
Rerun does not work on Windows. Sorry, but you can't do much relaunching
|
21
23
|
without "fork".
|
22
24
|
|
23
25
|
# Installation:
|
24
26
|
|
25
|
-
|
27
|
+
gem install rerun
|
28
|
+
|
29
|
+
"sudo" may be required on older systems.
|
26
30
|
|
27
|
-
|
31
|
+
If you are using RVM you might want to put this in your global gemset so it's available to all your apps. (There really should be a better way to distinguish gems-as-libraries from gems-as-tools.)
|
32
|
+
|
33
|
+
rvm @global do gem install rerun
|
34
|
+
|
35
|
+
# Usage:
|
28
36
|
|
29
37
|
rerun [options] [--] cmd
|
30
38
|
|
@@ -32,23 +40,23 @@ For example, if you're running a Sinatra app whose main file is
|
|
32
40
|
app.rb:
|
33
41
|
|
34
42
|
rerun ruby app.rb
|
35
|
-
|
43
|
+
|
36
44
|
If the first part of the command is a `.rb` filename, then `ruby` is
|
37
45
|
optional, so the above can also be accomplished like this:
|
38
46
|
|
39
47
|
rerun app.rb
|
40
|
-
|
48
|
+
|
41
49
|
Or if you're using Thin to run a Rack app that's configured in config.ru
|
42
50
|
but you want it on port 4000 and in debug mode, and only want to watch
|
43
51
|
the `app` subdirectory:
|
44
52
|
|
45
53
|
rerun --dir app -- thin start --debug --port=4000 -R config.ru
|
46
|
-
|
47
|
-
The `--` is to separate rerun options from cmd options. You can also
|
54
|
+
|
55
|
+
The `--` is to separate rerun options from cmd options. You can also
|
48
56
|
use a quoted string for the command, e.g.
|
49
57
|
|
50
58
|
rerun --dir app "thin start --debug --port=4000 -R config.ru"
|
51
|
-
|
59
|
+
|
52
60
|
Rackup can also be used to launch a Rack server, so let's try that:
|
53
61
|
|
54
62
|
rerun -- rackup --port 4000 config.ru
|
@@ -65,18 +73,42 @@ How about regenerating your HTML files after every change to your [Erector](http
|
|
65
73
|
|
66
74
|
rerun -x erector --to-html my_site.rb
|
67
75
|
|
76
|
+
Use Heroku Cedar? `rerun` is now compatible with `foreman`. Run all your
|
77
|
+
Procfile processes locally and restart them all when necessary.
|
78
|
+
|
79
|
+
rerun foreman start
|
80
|
+
|
68
81
|
# Options:
|
69
82
|
|
70
|
-
|
83
|
+
`--dir` directory to watch (default = ".")
|
71
84
|
|
72
|
-
|
85
|
+
`--pattern` glob to match inside directory. This uses the Ruby Dir glob style -- see <http://www.ruby-doc.org/core/classes/Dir.html#M002322> for details.
|
73
86
|
By default it watches these files: `rb,js,css,scss,sass,erb,html,haml,ru`.
|
74
87
|
|
75
|
-
|
88
|
+
`--clear` (or -c) clear the screen before each run
|
76
89
|
|
77
|
-
|
90
|
+
`--exit` (or -x) expect the program to exit. With this option, rerun checks the return value; without it, rerun checks that the launched process is still running.
|
78
91
|
|
79
|
-
Also --version and --help.
|
92
|
+
Also --version and --help, naturally.
|
93
|
+
|
94
|
+
# Growl Notifications
|
95
|
+
|
96
|
+
If you have `growlnotify` available on the `PATH`, it sends notifications to
|
97
|
+
growl in addition to the console.
|
98
|
+
|
99
|
+
Here's how to install [growlnotify](http://growl.info/extras.php#growlnotify):
|
100
|
+
|
101
|
+
> The Installer package for growlnotify is in the growlnotify folder in the Extras folder on the Growl disk image. Simply open the Installer package and follow the on-screen instructions.
|
102
|
+
|
103
|
+
**NOTE**: Growl recently moved to the App Store. I upgraded, and it still works for me, but I'd love it if someone can confirm that `growlnotify` is still available in a clean App Store install and works as advertised.
|
104
|
+
|
105
|
+
# On-The-Fly Commands
|
106
|
+
|
107
|
+
While the app is (re)running, you can make things happen by pressing keys:
|
108
|
+
|
109
|
+
* **r** restart (as if a file had changed)
|
110
|
+
* **c** clear the screen
|
111
|
+
* **x** exit (just like control-C)
|
80
112
|
|
81
113
|
# To Do:
|
82
114
|
|
@@ -85,7 +117,7 @@ Also --version and --help.
|
|
85
117
|
* Allow arbitrary sets of directories and file types, possibly with "include" and "exclude" sets
|
86
118
|
* ".rerun" file to specify options per project or in $HOME.
|
87
119
|
* Test on Linux.
|
88
|
-
* Merge with Kicker
|
120
|
+
* Merge with Kicker or Watchr or Guard -- maybe by using it as a library and writing a Rerun recipe
|
89
121
|
* On OS X, use a C library using growl's developer API <http://growl.info/developer/>
|
90
122
|
* "Failed" icon
|
91
123
|
* Get Rails icon working
|
@@ -99,6 +131,7 @@ Also --version and --help.
|
|
99
131
|
* The Sinatra FAQ has a discussion at <http://www.sinatrarb.com/faq.html#reloading>
|
100
132
|
* Kicker: <http://github.com/alloy/kicker/>
|
101
133
|
* Watchr: <https://github.com/mynyml/watchr>
|
134
|
+
* Guard: <http://github.com/guard/guard>
|
102
135
|
|
103
136
|
# Why would I use this instead of Shotgun?
|
104
137
|
|
@@ -120,6 +153,9 @@ pages all load other files (CSS, JS, media) and that adds up quickly.
|
|
120
153
|
The developers of shotgun are probably using caching or a front web
|
121
154
|
server so this doesn't affect them too much.
|
122
155
|
|
156
|
+
And hey, does Shotgun reload your Worker processes if you're using Foreman and
|
157
|
+
a Procfile? I'm pretty sure it doesn't.
|
158
|
+
|
123
159
|
YMMV!
|
124
160
|
|
125
161
|
# Why would I use this instead of Rack::Reloader?
|
data/Rakefile
CHANGED
@@ -28,6 +28,16 @@ def package(ext='')
|
|
28
28
|
"pkg/#{$spec.name}-#{$spec.version}" + ext
|
29
29
|
end
|
30
30
|
|
31
|
+
desc 'Exit if git is dirty'
|
32
|
+
task :check_git do
|
33
|
+
state = `git status 2> /dev/null | tail -n1`
|
34
|
+
clean = (state =~ /working directory clean/)
|
35
|
+
unless clean
|
36
|
+
warn "can't do that on an unclean git dir"
|
37
|
+
exit 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
31
41
|
desc 'Build packages'
|
32
42
|
task :package => %w[.gem .tar.gz].map { |e| package(e) }
|
33
43
|
|
@@ -55,6 +65,10 @@ file package('.tar.gz') => %w[pkg/] + $spec.files do |f|
|
|
55
65
|
end
|
56
66
|
|
57
67
|
desc 'Publish gem and tarball to rubyforge'
|
58
|
-
task 'release' => [package('.gem'), package('.tar.gz')] do |t|
|
68
|
+
task 'release' => [:check_git, package('.gem'), package('.tar.gz')] do |t|
|
69
|
+
puts "Releasing #{$spec.version}"
|
59
70
|
sh "gem push #{package('.gem')}"
|
71
|
+
puts "Tagging and pushing"
|
72
|
+
sh "git tag -v#{$spec.version}"
|
73
|
+
sh "git push && git push --tags"
|
60
74
|
end
|
data/bin/rerun
CHANGED
data/lib/rerun/runner.rb
CHANGED
@@ -1,6 +1,15 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'io/wait'
|
3
|
+
|
1
4
|
module Rerun
|
2
5
|
class Runner
|
3
6
|
|
7
|
+
def self.keep_running(cmd, options)
|
8
|
+
runner = new(cmd, options)
|
9
|
+
runner.start
|
10
|
+
runner.join
|
11
|
+
end
|
12
|
+
|
4
13
|
include System
|
5
14
|
|
6
15
|
def initialize(run_command, options = {})
|
@@ -8,6 +17,44 @@ module Rerun
|
|
8
17
|
@run_command = "ruby #{@run_command}" if @run_command.split(' ').first =~ /\.rb$/
|
9
18
|
end
|
10
19
|
|
20
|
+
def start_keypress_thread
|
21
|
+
from = caller.first
|
22
|
+
@keypress_thread = Thread.new do
|
23
|
+
# puts "starting keypress thread #{Thread.current.object_id} from #{from}"
|
24
|
+
while true
|
25
|
+
if c = key_pressed
|
26
|
+
puts "\n#{c.inspect} pressed inside rerun"
|
27
|
+
case c.downcase
|
28
|
+
when 'c'
|
29
|
+
say "clearing screen"
|
30
|
+
clear_screen
|
31
|
+
when 'r'
|
32
|
+
say "'r' pressed - restarting"
|
33
|
+
restart
|
34
|
+
break # the break will stop this thread
|
35
|
+
when 'x'
|
36
|
+
die
|
37
|
+
break # the break will stop this thread, in case the 'die' doesn't
|
38
|
+
else
|
39
|
+
puts [["c", "clear screen"],
|
40
|
+
["r", "restart"],
|
41
|
+
["x", "stop and exit"]
|
42
|
+
].map{|key, description| " #{key} -- #{description}"}.join("\n")
|
43
|
+
puts
|
44
|
+
end
|
45
|
+
end
|
46
|
+
sleep 1 # todo: use select instead of polling somehow?
|
47
|
+
end
|
48
|
+
# puts "keypress thread #{Thread.current.object_id} ending"
|
49
|
+
end
|
50
|
+
@keypress_thread.run
|
51
|
+
end
|
52
|
+
|
53
|
+
def kill_keypress_thread
|
54
|
+
@keypress_thread.kill if @keypress_thread
|
55
|
+
@keypress_thread = nil
|
56
|
+
end
|
57
|
+
|
11
58
|
def restart
|
12
59
|
@restarting = true
|
13
60
|
stop
|
@@ -53,7 +100,8 @@ module Rerun
|
|
53
100
|
notify "restarted", taglines[rand(taglines.size)]
|
54
101
|
end
|
55
102
|
|
56
|
-
|
103
|
+
clear_screen if clear?
|
104
|
+
start_keypress_thread
|
57
105
|
|
58
106
|
@pid = Kernel.fork do
|
59
107
|
begin
|
@@ -66,17 +114,19 @@ module Rerun
|
|
66
114
|
end
|
67
115
|
status_thread = Process.detach(@pid) # so if the child exits, it dies
|
68
116
|
|
69
|
-
Signal.trap("INT") do
|
70
|
-
|
71
|
-
|
117
|
+
Signal.trap("INT") do # INT = control-C -- allows user to stop the top-level rerun process
|
118
|
+
die
|
119
|
+
end
|
120
|
+
|
121
|
+
Signal.trap("TERM") do # TERM is the polite way of terminating a process
|
122
|
+
die
|
72
123
|
end
|
73
124
|
|
74
125
|
begin
|
75
126
|
sleep 2
|
76
127
|
rescue Interrupt => e
|
77
|
-
# in case someone hits control-C immediately
|
78
|
-
|
79
|
-
exit
|
128
|
+
# in case someone hits control-C immediately ("oops!")
|
129
|
+
die
|
80
130
|
end
|
81
131
|
|
82
132
|
if exit?
|
@@ -106,7 +156,11 @@ module Rerun
|
|
106
156
|
watcher.start
|
107
157
|
@watcher = watcher
|
108
158
|
end
|
159
|
+
end
|
109
160
|
|
161
|
+
def die
|
162
|
+
stop # stop the child process if it exists
|
163
|
+
exit 0 # todo: status code param
|
110
164
|
end
|
111
165
|
|
112
166
|
def join
|
@@ -126,8 +180,23 @@ module Rerun
|
|
126
180
|
|
127
181
|
def stop
|
128
182
|
if @pid && (@pid != 0)
|
129
|
-
notify "
|
130
|
-
|
183
|
+
notify "stopping", "All good things must come to an end." unless @restarting
|
184
|
+
begin
|
185
|
+
timeout(2) do
|
186
|
+
# start with a polite SIGTERM
|
187
|
+
signal("TERM") && Process.wait(@pid)
|
188
|
+
end
|
189
|
+
rescue Timeout::Error
|
190
|
+
begin
|
191
|
+
timeout(2) do
|
192
|
+
# escalate to SIGINT aka control-C since some foolish process may be ignoring SIGTERM
|
193
|
+
signal("INT") && Process.wait(@pid)
|
194
|
+
end
|
195
|
+
rescue Timeout::Error
|
196
|
+
# escalate to SIGKILL aka "kill -9" which cannot be ignored
|
197
|
+
signal("KILL") && Process.wait(@pid)
|
198
|
+
end
|
199
|
+
end
|
131
200
|
end
|
132
201
|
rescue => e
|
133
202
|
false
|
@@ -154,5 +223,36 @@ module Rerun
|
|
154
223
|
puts "#{Time.now.strftime("%T")} - #{msg}"
|
155
224
|
end
|
156
225
|
|
226
|
+
# non-blocking stdin reader.
|
227
|
+
# returns a 1-char string if a key was pressed; otherwise nil
|
228
|
+
#
|
229
|
+
def key_pressed
|
230
|
+
begin
|
231
|
+
# this "raw input" nonsense is because unix likes waiting for linefeeds before sending stdin
|
232
|
+
# "stty -echo" would make it not clutter the console, but be sure (ensure) to "stty echo" before exit
|
233
|
+
system("stty raw") # turn raw input on
|
234
|
+
c = nil
|
235
|
+
if $stdin.ready?
|
236
|
+
c = $stdin.getc
|
237
|
+
end
|
238
|
+
c.chr if c
|
239
|
+
ensure
|
240
|
+
system "stty -raw" # turn raw input off
|
241
|
+
end
|
242
|
+
|
243
|
+
# note: according to 'man tty' the proper way restore the settings is
|
244
|
+
# tty_state=`stty -g`
|
245
|
+
# ensure
|
246
|
+
# system 'stty "#{tty_state}'
|
247
|
+
# end
|
248
|
+
# but this way seems fine and less confusing
|
249
|
+
|
250
|
+
end
|
251
|
+
|
252
|
+
def clear_screen
|
253
|
+
# see http://ascii-table.com/ansi-escape-sequences-vt-100.php
|
254
|
+
$stdout.print "\033[H\033[2J"
|
255
|
+
end
|
256
|
+
|
157
257
|
end
|
158
258
|
end
|
data/rerun.gemspec
CHANGED
@@ -3,7 +3,7 @@ $spec = Gem::Specification.new do |s|
|
|
3
3
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
4
4
|
|
5
5
|
s.name = 'rerun'
|
6
|
-
s.version = '0.6.
|
6
|
+
s.version = '0.6.3'
|
7
7
|
|
8
8
|
s.description = "Restarts your app when a file changes"
|
9
9
|
s.summary = "Launches an app, and restarts it whenever the filesystem changes."
|
metadata
CHANGED
@@ -1,33 +1,24 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: rerun
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 6
|
9
|
-
- 2
|
10
|
-
version: 0.6.2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.3
|
5
|
+
prerelease:
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Alex Chaffee
|
14
9
|
autorequire:
|
15
10
|
bindir: bin
|
16
11
|
cert_chain: []
|
17
|
-
|
18
|
-
date: 2011-05-18 00:00:00 -07:00
|
19
|
-
default_executable:
|
12
|
+
date: 2011-11-27 00:00:00.000000000Z
|
20
13
|
dependencies: []
|
21
|
-
|
22
14
|
description: Restarts your app when a file changes
|
23
15
|
email: alex@stinky.com
|
24
|
-
executables:
|
16
|
+
executables:
|
25
17
|
- rerun
|
26
18
|
extensions: []
|
27
|
-
|
28
|
-
extra_rdoc_files:
|
19
|
+
extra_rdoc_files:
|
29
20
|
- README.md
|
30
|
-
files:
|
21
|
+
files:
|
31
22
|
- README.md
|
32
23
|
- LICENSE
|
33
24
|
- Rakefile
|
@@ -41,39 +32,31 @@ files:
|
|
41
32
|
- lib/rerun/runner.rb
|
42
33
|
- lib/rerun/system.rb
|
43
34
|
- lib/rerun/watcher.rb
|
44
|
-
has_rdoc: true
|
45
35
|
homepage: http://github.com/alexch/rerun/
|
46
36
|
licenses: []
|
47
|
-
|
48
37
|
post_install_message:
|
49
38
|
rdoc_options: []
|
50
|
-
|
51
|
-
require_paths:
|
39
|
+
require_paths:
|
52
40
|
- lib
|
53
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
42
|
none: false
|
55
|
-
requirements:
|
56
|
-
- -
|
57
|
-
- !ruby/object:Gem::Version
|
58
|
-
|
59
|
-
segments:
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
segments:
|
60
48
|
- 0
|
61
|
-
|
62
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
hash: 569371154783151759
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
51
|
none: false
|
64
|
-
requirements:
|
65
|
-
- -
|
66
|
-
- !ruby/object:Gem::Version
|
67
|
-
|
68
|
-
segments:
|
69
|
-
- 0
|
70
|
-
version: "0"
|
52
|
+
requirements:
|
53
|
+
- - ! '>='
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
71
56
|
requirements: []
|
72
|
-
|
73
57
|
rubyforge_project: pivotalrb
|
74
|
-
rubygems_version: 1.
|
58
|
+
rubygems_version: 1.8.6
|
75
59
|
signing_key:
|
76
60
|
specification_version: 2
|
77
61
|
summary: Launches an app, and restarts it whenever the filesystem changes.
|
78
62
|
test_files: []
|
79
|
-
|