rerun 0.6.2 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
-
|