rerun 0.9.0 → 0.13.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/README.md +176 -29
- data/Rakefile +8 -1
- data/bin/rerun +9 -3
- data/lib/goo.rb +3 -0
- data/lib/rerun.rb +1 -2
- data/lib/rerun/notification.rb +82 -0
- data/lib/rerun/options.rb +96 -42
- data/lib/rerun/runner.rb +155 -84
- data/lib/rerun/system.rb +3 -25
- data/lib/rerun/watcher.rb +34 -13
- data/rerun.gemspec +2 -2
- metadata +10 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 12916c529d417f7435294064c61ab8bc9b3e4811e61ab162725b84dc42730212
|
4
|
+
data.tar.gz: 2d32573a380ae73f845564580eced58064271d3347a09da7c5e13ad72882a8c5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3d9ed3d68053c23b3c9665abc33cde89d4fd5893fa1d9930fbcb93113459450810ced0d3d0cd7e83c2bc4dc9da27446228f1104dc2a46cdb01ef53dc8bc5f48
|
7
|
+
data.tar.gz: 1df642c255cd253271015b111f2b0fa4b9023c0fd697b1bd03405b2fccb9d716658f840a82884dfa7b042395c07664205f6427c08a9b432f8742ce58413792cd
|
data/README.md
CHANGED
@@ -14,14 +14,18 @@ Rerun's advantage is its simple design. Since it uses `exec` and the standard
|
|
14
14
|
Unix `SIGINT` and `SIGKILL` signals, you're sure the restarted app is really
|
15
15
|
acting just like it was when you ran it from the command line the first time.
|
16
16
|
|
17
|
-
By default
|
17
|
+
By default it watches files ending in: `rb,js,coffee,css,scss,sass,erb,html,haml,ru,yml,slim,md,feature,c,h`.
|
18
18
|
Use the `--pattern` option if you want to change this.
|
19
19
|
|
20
20
|
As of version 0.7.0, we use the Listen gem, which tries to use your OS's
|
21
21
|
built-in facilities for monitoring the filesystem, so CPU use is very light.
|
22
22
|
|
23
|
-
Rerun does
|
24
|
-
|
23
|
+
**UPDATE**: Now Rerun *does* work on Windows! Caveats:
|
24
|
+
* not well-tested
|
25
|
+
* you need to press Enter after keypress input
|
26
|
+
* you may need to install the `wdm` gem manually: `gem install wdm`
|
27
|
+
* You may see this persistent `INFO` error message; to remove it, use`--no-notify`:
|
28
|
+
* `INFO: Could not find files for the given pattern(s)`
|
25
29
|
|
26
30
|
# Installation:
|
27
31
|
|
@@ -40,10 +44,39 @@ they're not available. Unfortunately, Rubygems doesn't understand optional
|
|
40
44
|
dependencies very well, so you may have to install extra gems (and/or put them
|
41
45
|
in your Gemfile) to make Rerun work more smoothly on your system.
|
42
46
|
(Learn more at <https://github.com/guard/listen#listen-adapters>.)
|
43
|
-
|
47
|
+
|
48
|
+
On Mac OS X, use
|
44
49
|
|
45
50
|
gem install rb-fsevent
|
46
51
|
|
52
|
+
On Windows, use
|
53
|
+
|
54
|
+
gem install wdm
|
55
|
+
|
56
|
+
On *BSD, use
|
57
|
+
|
58
|
+
gem install rb-kqueue
|
59
|
+
|
60
|
+
## Installation via Gemfile / Bundler
|
61
|
+
|
62
|
+
If you are using rerun inside an existing Ruby application (like a Rails or Sinatra app), you can add it to your Gemfile:
|
63
|
+
|
64
|
+
``` ruby
|
65
|
+
group :development, :test do
|
66
|
+
gem "rerun"
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
Using a Gemfile is also an easy way to use the pre-release branch, which may have bugfixes or features you want:
|
71
|
+
|
72
|
+
``` ruby
|
73
|
+
group :development, :test do
|
74
|
+
gem "rerun", git: "https://github.com/alexch/rerun.git"
|
75
|
+
end
|
76
|
+
```
|
77
|
+
|
78
|
+
When using a Gemfile, install with `bundle install` or `bundle update`, and run using `bundle exec rerun`, to guarantee you are using the rerun version specified in the Gemfile, and not a different version in a system-wide gemset.
|
79
|
+
|
47
80
|
# Usage:
|
48
81
|
|
49
82
|
rerun [options] [--] cmd
|
@@ -113,42 +146,95 @@ Procfile processes locally and restart them all when necessary.
|
|
113
146
|
|
114
147
|
# Options:
|
115
148
|
|
149
|
+
These options can be specified on the command line and/or inside a `.rerun` config file (see below).
|
150
|
+
|
116
151
|
`--dir` directory (or directories) to watch (default = "."). Separate multiple paths with ',' and/or use multiple `-d` options.
|
117
152
|
|
118
153
|
`--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.
|
119
|
-
By default it watches files ending in: `rb,js,css,scss,sass,erb,html,haml,ru`.
|
120
|
-
On top of this, it also ignores dotfiles, `.tmp` files, and some other files and directories (like `.git
|
154
|
+
By default it watches files ending in: `rb,js,coffee,css,scss,sass,erb,html,haml,ru,yml,slim,md,feature,c,h`.
|
155
|
+
On top of this, it also ignores dotfiles, `.tmp` files, and some other files and directories (like `.git` and `log`).
|
156
|
+
Run `rerun --help` to see the actual list.
|
157
|
+
|
158
|
+
`--ignore pattern` file glob to ignore (can be set many times). To ignore a directory, you must append `'/*'` e.g.
|
159
|
+
`--ignore 'coverage/*'`.
|
121
160
|
|
122
|
-
`--
|
161
|
+
`--[no-]ignore-dotfiles` By default, on top of --pattern and --ignore, we ignore any changes to files and dirs starting with a dot. Setting `--no-ignore-dotfiles` allows you to monitor a relevant file like .env, but you may also have to explicitly --ignore more dotfiles and dotdirs.
|
162
|
+
|
163
|
+
`--signal` (or `-s`) use specified signal(s) (instead of the default `TERM,INT,KILL`) to terminate the previous process. You can use a comma-delimited list if you want to try a signal, wait up to 5 seconds for the process to die, then try again with a different signal, and so on.
|
123
164
|
This may be useful for forcing the respective process to terminate as quickly as possible.
|
124
165
|
(`--signal KILL` is the equivalent of `kill -9`)
|
125
166
|
|
126
|
-
`--
|
167
|
+
`--wait sec` (or `-w`) after asking the process to terminate, wait this long (in seconds) before either aborting, or trying the next signal in series. Default: 2 sec
|
168
|
+
|
169
|
+
`--restart` (or `-r`) expect process to restart itself, using signal HUP by default
|
170
|
+
(e.g. `-r -s INT` will send a INT and then resume watching for changes)
|
127
171
|
|
128
172
|
`--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.
|
129
173
|
|
174
|
+
`--clear` (or -c) clear the screen before each run
|
175
|
+
|
130
176
|
`--background` (or -b) disable on-the-fly commands, allowing the process to be backgrounded
|
131
177
|
|
132
|
-
`--
|
178
|
+
`--notify NOTIFIER` use `growl` or `osx` or `notify-send` for notifications (see below)
|
179
|
+
|
180
|
+
`--no-notify` disable notifications
|
133
181
|
|
134
182
|
`--name` set the app name (for display)
|
135
183
|
|
136
|
-
|
184
|
+
`--force-polling` use polling instead of a native filesystem scan (useful for Vagrant)
|
185
|
+
|
186
|
+
`--quiet` silences most messages
|
187
|
+
|
188
|
+
`--verbose` enables even more messages (unless you also specified `--quiet`, which overrides `--verbose`)
|
189
|
+
|
190
|
+
Also `--version` and `--help`, naturally.
|
191
|
+
|
192
|
+
## Config file
|
193
|
+
|
194
|
+
If the current directory contains a file named `.rerun`, it will be parsed with the same rules as command-line arguments. Newlines are the same as any other whitespace, so you can stack options vertically, like this:
|
195
|
+
|
196
|
+
```
|
197
|
+
--quiet
|
198
|
+
--pattern **/*.{rb,js,scss,sass,html,md}
|
199
|
+
```
|
137
200
|
|
138
|
-
|
201
|
+
Options specified on the command line will override those in the config file. You can negate boolean options with `--no-`, so for example, with the above config file, to re-enable logging, you could say:
|
202
|
+
|
203
|
+
```sh
|
204
|
+
rerun --no-quiet rackup
|
205
|
+
```
|
206
|
+
|
207
|
+
If you're not sure what options are being overwritten, use `--verbose` and rerun will show you the final result of the parsing.
|
208
|
+
|
209
|
+
# Notifications
|
139
210
|
|
140
211
|
If you have `growlnotify` available on the `PATH`, it sends notifications to
|
141
|
-
growl in addition to the console.
|
142
|
-
|
212
|
+
growl in addition to the console.
|
213
|
+
|
214
|
+
If you have `terminal-notifier`, it sends notifications to
|
215
|
+
the OS X notification center in addition to the console.
|
216
|
+
|
217
|
+
If you have `notify-send`, it sends notifications to Freedesktop-compatible
|
218
|
+
desktops in addition to the console.
|
219
|
+
|
220
|
+
If you have more than one available notification program, Rerun will pick one, or you can choose between them using `--notify growl`, `--notify osx`, `--notify notify-send`, etc.
|
221
|
+
|
222
|
+
If you have a notifier installed but don't want rerun to use it,
|
223
|
+
set the `--no-notify` option.
|
143
224
|
|
144
225
|
Download [growlnotify here](http://growl.info/downloads.php#generaldownloads)
|
145
226
|
now that Growl has moved to the App Store.
|
146
227
|
|
228
|
+
Install [terminal-notifier](https://github.com/julienXX/terminal-notifier) using `gem install terminal-notifier`. (You may have to put it in your system gemset and/or use `sudo` too.) Using Homebrew to install terminal-notifier is not recommended.
|
229
|
+
|
230
|
+
On Debian/Ubuntu, `notify-send` is availble in the `libnotify-bin` package. On other GNU/Linux systems, it might be in a package with a different name.
|
231
|
+
|
147
232
|
# On-The-Fly Commands
|
148
233
|
|
149
234
|
While the app is (re)running, you can make things happen by pressing keys:
|
150
235
|
|
151
236
|
* **r** -- restart (as if a file had changed)
|
237
|
+
* **f** -- force restart (stop and start)
|
152
238
|
* **c** -- clear the screen
|
153
239
|
* **x** or **q** -- exit (just like control-C)
|
154
240
|
* **p** -- pause/unpause filesystem watching
|
@@ -161,44 +247,66 @@ keys to be trapped, so use the `--background` option.
|
|
161
247
|
The current algorithm for killing the process is:
|
162
248
|
|
163
249
|
* send [SIGTERM](http://en.wikipedia.org/wiki/SIGTERM) (or the value of the `--signal` option)
|
164
|
-
* if that doesn't work after
|
250
|
+
* if that doesn't work after 2 seconds, send SIGINT (aka control-C)
|
165
251
|
* if that doesn't work after 2 more seconds, send SIGKILL (aka kill -9)
|
166
252
|
|
167
253
|
This seems like the most gentle and unixy way of doing things, but it does
|
168
|
-
mean that if your program ignores SIGTERM, it takes an extra
|
254
|
+
mean that if your program ignores SIGTERM, it takes an extra 2 to 4 seconds to
|
169
255
|
restart.
|
170
256
|
|
257
|
+
If you want to use your own series of signals, use the `--signal` option. If you want to change the delay before attempting the next signal, use the `--wait` option.
|
258
|
+
|
259
|
+
# Vagrant and VirtualBox
|
260
|
+
|
261
|
+
If running inside a shared directory using Vagrant and VirtualBox, you must pass the `--force-polling` option. You may also have to pass some extra `--ignore` options too; otherwise each scan can take 10 or more seconds on directories with a large number of files or subdirectories underneath it.
|
262
|
+
|
263
|
+
# Troubleshooting
|
264
|
+
|
265
|
+
## zsh ##
|
266
|
+
|
267
|
+
If you are using `zsh` as your shell, and you are specifying your `--pattern` as `**/*.rb`, you may face this error
|
268
|
+
```
|
269
|
+
Errno::EACCES: Permission denied - <filename>
|
270
|
+
```
|
271
|
+
This is because `**/*.rb` gets expanded into the command by `zsh` instead of passing it through to rerun. The solution is to simply quote ('' or "") the pattern.
|
272
|
+
i.e
|
273
|
+
```
|
274
|
+
rerun -p **/*.rb rake test
|
275
|
+
```
|
276
|
+
becomes
|
277
|
+
```
|
278
|
+
rerun -p "**/*.rb" rake test
|
279
|
+
```
|
280
|
+
|
171
281
|
# To Do:
|
172
282
|
|
173
|
-
|
283
|
+
## Must have for v1.0
|
284
|
+
* Make sure to pass through quoted options correctly to target process [bug]
|
285
|
+
* Optionally do "bundle install" before and "bundle exec" during launch
|
286
|
+
|
287
|
+
## Nice to have
|
288
|
+
* ".rerun" file in $HOME
|
174
289
|
* If the last element of the command is a `.ru` file and there's no other command then use `rackup`
|
175
|
-
* --ignore pattern (currently we're using Listen's default list plus dotfiles)
|
176
|
-
* ".rerun" file to specify options per project or in $HOME.
|
177
290
|
* Figure out an algorithm so "-x" is not needed (if possible) -- maybe by accepting a "--port" option or reading `config.ru`
|
178
291
|
* Specify (or deduce) port to listen for to determine success of a web server launch
|
179
|
-
*
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
* Optionally do "bundle install" before and "bundle exec" during launch
|
292
|
+
* see also [todo.md](todo.md)
|
293
|
+
|
294
|
+
## Wacky Ideas
|
295
|
+
|
184
296
|
* On OS X:
|
185
297
|
* use a C library using growl's developer API <http://growl.info/developer/>
|
186
298
|
* Use growl's AppleScript or SDK instead of relying on growlnotify
|
187
|
-
* Use OS X notifications
|
188
299
|
* "Failed" icon for notifications
|
189
|
-
* On Linux:
|
190
|
-
* Test on Linux.
|
191
|
-
* Use libnotify or notify-send http://www.linuxjournal.com/content/tech-tip-get-notifications-your-scripts-notify-send
|
192
300
|
|
193
301
|
# Other projects that do similar things
|
194
302
|
|
303
|
+
* Guard: <http://github.com/guard/guard>
|
195
304
|
* Restartomatic: <http://github.com/adammck/restartomatic>
|
196
305
|
* Shotgun: <http://github.com/rtomayko/shotgun>
|
197
306
|
* Rack::Reloader middleware: <http://github.com/rack/rack/blob/5ca8f82fb59f0bf0e8fd438e8e91c5acf3d98e44/lib/rack/reloader.rb>
|
198
307
|
* The Sinatra FAQ has a discussion at <http://www.sinatrarb.com/faq.html#reloading>
|
199
308
|
* Kicker: <http://github.com/alloy/kicker/>
|
200
309
|
* Watchr: <https://github.com/mynyml/watchr>
|
201
|
-
* Guard: <http://github.com/guard/guard>
|
202
310
|
* Autotest: <https://github.com/grosser/autotest>
|
203
311
|
|
204
312
|
# Why would I use this instead of Shotgun?
|
@@ -279,14 +387,53 @@ Based upon and/or inspired by:
|
|
279
387
|
* <https://github.com/FND>
|
280
388
|
* Barry Sia <https://github.com/bsia>
|
281
389
|
* Paul Rangel <https://github.com/ismell>
|
390
|
+
* James Edward Gray II <https://github.com/JEG2>
|
391
|
+
* Raul E Rangel <https://github.com/ismell> and Antonio Terceiro <https://github.com/terceiro>
|
392
|
+
* Mike Pastore <https://github.com/mwpastore>
|
393
|
+
* Andy Duncan <https://github.com/ajduncan>
|
394
|
+
* Brent Van Minnen
|
395
|
+
* Matthew O'Riordan <https://github.com/mattheworiordan>
|
396
|
+
* Antonio Terceiro <https://github.com/terceiro>
|
397
|
+
* <https://github.com/mattbrictson>
|
398
|
+
* <https://github.com/krissi>
|
282
399
|
|
283
400
|
# Version History
|
284
401
|
|
285
|
-
*
|
402
|
+
*
|
403
|
+
* --no-ignore-dotfiles option
|
404
|
+
|
405
|
+
* v0.13.0 26 January 2018
|
406
|
+
* bugfix: pause/unpause works again (thanks Barry!)
|
407
|
+
* `.rerun` config file
|
408
|
+
|
409
|
+
* v0.12.0 23 January 2018
|
410
|
+
* smarter `--signal` option, allowing you to specify a series of signals to try in order; also `--wait` to change how long between tries
|
411
|
+
* `--force-polling` option (thanks ajduncan)
|
412
|
+
* `f` key to force stop and start (thanks mwpastore)
|
413
|
+
* add `.c` and `.h` files to default ignore list
|
414
|
+
* support for Windows
|
415
|
+
* use `Kernel.spawn` instead of `fork`
|
416
|
+
* use `wdm` gem for Windows Directory Monitor
|
417
|
+
* support for notifications on GNU/Linux using [notify-send](http://www.linuxjournal.com/content/tech-tip-get-notifications-your-scripts-notify-send) (thanks terceiro)
|
418
|
+
* fix `Gem::LoadError - terminal-notifier is not part of the bundle` [bug](https://github.com/alexch/rerun/issues/108) (thanks mattheworiordan)
|
419
|
+
|
420
|
+
* 0.11.0 7 October 2015
|
421
|
+
* better 'changed' message
|
422
|
+
* `--notify osx` option
|
423
|
+
* `--restart` option (with bugfix by Mike Pastore)
|
424
|
+
* use Listen 3 gem
|
425
|
+
* add `.feature` files to default watchlist (thanks @jmuheim)
|
426
|
+
|
427
|
+
* v0.10.0 4 May 2014
|
428
|
+
* add '.coffee,.slim,.md' to default pattern (thanks @xylinq)
|
429
|
+
* --ignore option
|
430
|
+
|
431
|
+
* v0.9.0 6 March 2014
|
286
432
|
* --dir (or -d) can be specified more than once, for multiple directories (thanks again Barry!)
|
287
433
|
* --name option
|
288
434
|
* press 'p' to pause/unpause filesystem watching (Barry is the man!)
|
289
435
|
* works with Listen 2 (note: needs 2.3 or higher)
|
436
|
+
* cooldown works, thanks to patches to underlying Listen gem
|
290
437
|
* ignore all dotfiles, and add actual list of ignored dirs and files
|
291
438
|
|
292
439
|
* v0.8.2
|
data/Rakefile
CHANGED
@@ -32,7 +32,7 @@ end
|
|
32
32
|
desc 'Exit if git is dirty'
|
33
33
|
task :check_git do
|
34
34
|
state = `git status 2> /dev/null | tail -n1`
|
35
|
-
clean = (state =~ /working directory clean/)
|
35
|
+
clean = (state =~ /working (directory|tree) clean/)
|
36
36
|
unless clean
|
37
37
|
warn "can't do that on an unclean git dir"
|
38
38
|
exit 1
|
@@ -73,3 +73,10 @@ task 'release' => [:check_git, package('.gem'), package('.tar.gz')] do |t|
|
|
73
73
|
sh "git tag v#{$spec.version}"
|
74
74
|
sh "git push && git push --tags"
|
75
75
|
end
|
76
|
+
|
77
|
+
desc 'download github issues and pull requests'
|
78
|
+
task 'github' do
|
79
|
+
%w(issues pulls).each do |type|
|
80
|
+
sh "curl -o #{type}.json https://api.github.com/repos/alexch/rerun/#{type}"
|
81
|
+
end
|
82
|
+
end
|
data/bin/rerun
CHANGED
@@ -7,6 +7,12 @@ $LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
|
|
7
7
|
require 'rerun'
|
8
8
|
require 'optparse'
|
9
9
|
|
10
|
-
options = Rerun::Options.parse
|
11
|
-
|
12
|
-
|
10
|
+
options = Rerun::Options.parse config_file: ".rerun"
|
11
|
+
|
12
|
+
if options and options[:verbose]
|
13
|
+
puts "\nrerun options:\n\t#{options}"
|
14
|
+
end
|
15
|
+
|
16
|
+
exit if options.nil? or options[:cmd].nil? or options[:cmd].empty?
|
17
|
+
|
18
|
+
Rerun::Runner.keep_running(options[:cmd], options)
|
data/lib/goo.rb
ADDED
data/lib/rerun.rb
CHANGED
@@ -4,13 +4,12 @@ $: << here unless $:.include?(here)
|
|
4
4
|
require "listen" # pull in the Listen gem
|
5
5
|
require "rerun/options"
|
6
6
|
require "rerun/system"
|
7
|
+
require "rerun/notification"
|
7
8
|
require "rerun/runner"
|
8
9
|
require "rerun/watcher"
|
9
10
|
require "rerun/glob"
|
10
11
|
|
11
12
|
module Rerun
|
12
13
|
|
13
|
-
DEFAULT_PATTERN = "**/*.{rb,js,css,scss,sass,erb,html,haml,ru}"
|
14
|
-
|
15
14
|
end
|
16
15
|
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# todo: unit tests
|
2
|
+
|
3
|
+
module Rerun
|
4
|
+
class Notification
|
5
|
+
include System
|
6
|
+
|
7
|
+
attr_reader :title, :body, :options
|
8
|
+
|
9
|
+
def initialize(title, body, options = Options::DEFAULTS.dup)
|
10
|
+
@title = title
|
11
|
+
@body = body
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def command
|
16
|
+
# todo: strategy or subclass
|
17
|
+
|
18
|
+
s = nil
|
19
|
+
|
20
|
+
if options[:notify] == true or options[:notify] == "growl"
|
21
|
+
if (cmd = command_named("growlnotify"))
|
22
|
+
# todo: check version of growlnotify and warn if it's too old
|
23
|
+
icon_str = ("--image \"#{icon}\"" if icon)
|
24
|
+
s = "#{cmd} -n \"#{app_name}\" -m \"#{body}\" \"#{app_name} #{title}\" #{icon_str}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
if s.nil? and options[:notify] == true or options[:notify] == "osx"
|
29
|
+
if (cmd = command_named("terminal-notifier"))
|
30
|
+
icon_str = ("-appIcon \"#{icon}\"" if icon)
|
31
|
+
s = "#{cmd} -title \"#{app_name}\" -message \"#{body}\" \"#{app_name} #{title}\" #{icon_str}"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
if s.nil? and options[:notify] == true or options[:notify] == "notify-send"
|
36
|
+
if (cmd = command_named('notify-send'))
|
37
|
+
icon_str = "--icon #{icon}" if icon
|
38
|
+
s = "#{cmd} -t 500 --hint=int:transient:1 #{icon_str} \"#{app_name}: #{title}\" \"#{body}\""
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
s
|
43
|
+
end
|
44
|
+
|
45
|
+
def command_named(name)
|
46
|
+
which_command = windows? ? 'where.exe %{cmd}' : 'which %{cmd} 2> /dev/null'
|
47
|
+
# TODO: remove 'INFO' error message
|
48
|
+
path = `#{which_command % {cmd: name}}`.chomp
|
49
|
+
path.empty? ? nil : path
|
50
|
+
end
|
51
|
+
|
52
|
+
def send(background = true)
|
53
|
+
return unless command
|
54
|
+
with_clean_env do
|
55
|
+
`#{command}#{" &" if background}`
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def app_name
|
60
|
+
options[:name]
|
61
|
+
end
|
62
|
+
|
63
|
+
def icon
|
64
|
+
"#{icon_dir}/rails_red_sml.png" if rails?
|
65
|
+
end
|
66
|
+
|
67
|
+
def icon_dir
|
68
|
+
here = File.expand_path(File.dirname(__FILE__))
|
69
|
+
File.expand_path("#{here}/../../icons")
|
70
|
+
end
|
71
|
+
|
72
|
+
def with_clean_env
|
73
|
+
if defined?(Bundler)
|
74
|
+
Bundler.with_clean_env do
|
75
|
+
yield
|
76
|
+
end
|
77
|
+
else
|
78
|
+
yield
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/rerun/options.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
require 'pathname'
|
3
|
+
require 'rerun/watcher'
|
4
|
+
require 'rerun/system'
|
3
5
|
|
4
6
|
libdir = "#{File.expand_path(File.dirname(File.dirname(__FILE__)))}"
|
5
7
|
|
@@ -7,86 +9,138 @@ $spec = Gem::Specification.load(File.join(libdir, "..", "rerun.gemspec"))
|
|
7
9
|
|
8
10
|
module Rerun
|
9
11
|
class Options
|
10
|
-
|
12
|
+
|
13
|
+
extend Rerun::System
|
14
|
+
|
15
|
+
# If you change the default pattern, please update the README.md file -- the list appears twice therein, which at the time of this comment are lines 17 and 119
|
16
|
+
DEFAULT_PATTERN = "**/*.{rb,js,coffee,css,scss,sass,erb,html,haml,ru,yml,slim,md,feature,c,h}"
|
11
17
|
DEFAULT_DIRS = ["."]
|
12
18
|
|
13
19
|
DEFAULTS = {
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
20
|
+
:background => false,
|
21
|
+
:dir => DEFAULT_DIRS,
|
22
|
+
:force_polling => false,
|
23
|
+
:ignore => [],
|
24
|
+
:ignore_dotfiles => true,
|
25
|
+
:name => Pathname.getwd.basename.to_s.capitalize,
|
26
|
+
:notify => true,
|
27
|
+
:pattern => DEFAULT_PATTERN,
|
28
|
+
:quiet => false,
|
29
|
+
:signal => (windows? ? "TERM,KILL" : "TERM,INT,KILL"),
|
30
|
+
:verbose => false,
|
31
|
+
:wait => 2,
|
18
32
|
}
|
19
33
|
|
20
|
-
def self.parse args
|
21
|
-
options = DEFAULTS.dup
|
22
|
-
opts = OptionParser.new("", 24, ' ') do |opts|
|
23
|
-
opts.banner = "Usage: rerun [options] [--] cmd"
|
34
|
+
def self.parse args: ARGV, config_file: nil
|
24
35
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
36
|
+
default_options = DEFAULTS.dup
|
37
|
+
options = {
|
38
|
+
ignore: []
|
39
|
+
}
|
40
|
+
|
41
|
+
if config_file && File.exist?(config_file)
|
42
|
+
require 'shellwords'
|
43
|
+
config_args = File.read(config_file).shellsplit
|
44
|
+
args = config_args + args
|
45
|
+
end
|
31
46
|
|
32
|
-
|
47
|
+
option_parser = OptionParser.new("", 24, ' ') do |o|
|
48
|
+
o.banner = "Usage: rerun [options] [--] cmd"
|
49
|
+
|
50
|
+
o.separator ""
|
51
|
+
o.separator "Launches an app, and restarts it when the filesystem changes."
|
52
|
+
o.separator "See http://github.com/alexch/rerun for more info."
|
53
|
+
o.separator "Version: #{$spec.version}"
|
54
|
+
o.separator ""
|
55
|
+
o.separator "Options:"
|
56
|
+
|
57
|
+
o.on("-d dir", "--dir dir", "directory to watch, default = \"#{DEFAULT_DIRS}\". Specify multiple paths with ',' or separate '-d dir' option pairs.") do |dir|
|
33
58
|
elements = dir.split(",")
|
34
59
|
options[:dir] = (options[:dir] || []) + elements
|
35
60
|
end
|
36
61
|
|
37
|
-
|
62
|
+
# todo: rename to "--watch"
|
63
|
+
o.on("-p pattern", "--pattern pattern", "file glob to watch, default = \"#{DEFAULTS[:pattern]}\"") do |pattern|
|
38
64
|
options[:pattern] = pattern
|
39
65
|
end
|
40
66
|
|
41
|
-
|
67
|
+
o.on("-i pattern", "--ignore pattern", "file glob(s) to ignore. Can be set many times. To ignore a directory, you must append '/*' e.g. --ignore 'coverage/*' . Globs do not match dotfiles by default.") do |pattern|
|
68
|
+
options[:ignore] += [pattern]
|
69
|
+
end
|
70
|
+
|
71
|
+
o.on("--[no-]ignore-dotfiles", "by default, file globs do not match files that begin with a dot. Setting --no-ignore-dotfiles allows you to monitor a relevant file like .env, but you may also have to explicitly --ignore more dotfiles and dotdirs.") do |value|
|
72
|
+
options[:ignore_dotfiles] = value
|
73
|
+
end
|
74
|
+
|
75
|
+
o.on("-s signal", "--signal signal", "terminate process using this signal. To try several signals in series, use a comma-delimited list. Default: \"#{DEFAULTS[:signal]}\"") do |signal|
|
42
76
|
options[:signal] = signal
|
43
77
|
end
|
44
78
|
|
45
|
-
|
46
|
-
|
79
|
+
o.on("-w sec", "--wait sec", "after asking the process to terminate, wait this long (in seconds) before either aborting, or trying the next signal in series. Default: #{DEFAULTS[:wait]} sec")
|
80
|
+
|
81
|
+
o.on("-r", "--restart", "expect process to restart itself, so just send a signal and continue watching. Sends the HUP signal unless overridden using --signal") do |signal|
|
82
|
+
options[:restart] = true
|
83
|
+
default_options[:signal] = "HUP"
|
47
84
|
end
|
48
85
|
|
49
|
-
|
50
|
-
options[:exit] =
|
86
|
+
o.on("-x", "--exit", "expect the program to exit. With this option, rerun checks the return value; without it, rerun checks that the process is running.") do |value|
|
87
|
+
options[:exit] = value
|
51
88
|
end
|
52
89
|
|
53
|
-
|
54
|
-
options[:
|
90
|
+
o.on("-c", "--clear", "clear screen before each run") do |value|
|
91
|
+
options[:clear] = value
|
55
92
|
end
|
56
93
|
|
57
|
-
|
94
|
+
o.on("-b", "--background", "disable on-the-fly keypress commands, allowing the process to be backgrounded") do |value|
|
95
|
+
options[:background] = value
|
96
|
+
end
|
97
|
+
|
98
|
+
o.on("-n name", "--name name", "name of app used in logs and notifications, default = \"#{DEFAULTS[:name]}\"") do |name|
|
58
99
|
options[:name] = name
|
59
100
|
end
|
60
101
|
|
61
|
-
|
102
|
+
o.on("--[no-]force-polling", "use polling instead of a native filesystem scan (useful for Vagrant)") do |value|
|
103
|
+
options[:force_polling] = value
|
104
|
+
end
|
105
|
+
|
106
|
+
o.on("--no-growl", "don't use growl [OBSOLETE]") do
|
62
107
|
options[:growl] = false
|
108
|
+
$stderr.puts "--no-growl is obsolete; use --no-notify instead"
|
109
|
+
return
|
110
|
+
end
|
111
|
+
|
112
|
+
o.on("--[no-]notify [notifier]", "send messages through a desktop notification application. Supports growl (requires growlnotify), osx (requires terminal-notifier gem), and notify-send on GNU/Linux (notify-send must be installed)") do |notifier|
|
113
|
+
notifier = true if notifier.nil?
|
114
|
+
options[:notify] = notifier
|
63
115
|
end
|
64
116
|
|
65
|
-
|
66
|
-
|
117
|
+
o.on("-q", "--[no-]quiet", "don't output any logs") do |value|
|
118
|
+
options[:quiet] = value
|
119
|
+
end
|
120
|
+
|
121
|
+
o.on("--[no-]verbose", "log extra stuff like PIDs (unless you also specified `--quiet`") do |value|
|
122
|
+
options[:verbose] = value
|
123
|
+
end
|
124
|
+
|
125
|
+
o.on_tail("-h", "--help", "--usage", "show this message and immediately exit") do
|
126
|
+
puts o
|
67
127
|
return
|
68
128
|
end
|
69
129
|
|
70
|
-
|
130
|
+
o.on_tail("--version", "show version and immediately exit") do
|
71
131
|
puts $spec.version
|
72
132
|
return
|
73
133
|
end
|
74
134
|
|
75
|
-
opts.on_tail ""
|
76
|
-
opts.on_tail "On top of --pattern, we ignore any changes to files and dirs starting with a dot, ending with [#{Listen::Silencer::DEFAULT_IGNORED_EXTENSIONS.join(',')}], or named [#{Listen::Silencer::DEFAULT_IGNORED_DIRECTORIES.join(',')}]."
|
77
|
-
|
78
135
|
end
|
79
136
|
|
80
|
-
if args.empty?
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
opts.parse! args
|
85
|
-
options[:cmd] = args.join(" ")
|
86
|
-
options[:dir] ||= DEFAULT_DIRS
|
87
|
-
options
|
88
|
-
end
|
89
|
-
end
|
137
|
+
puts option_parser if args.empty?
|
138
|
+
option_parser.parse! args
|
139
|
+
options = default_options.merge(options)
|
140
|
+
options[:cmd] = args.join(" ").strip # todo: better arg word handling
|
90
141
|
|
142
|
+
options
|
143
|
+
end
|
91
144
|
end
|
145
|
+
|
92
146
|
end
|
data/lib/rerun/runner.rb
CHANGED
@@ -4,19 +4,25 @@ require 'io/wait'
|
|
4
4
|
module Rerun
|
5
5
|
class Runner
|
6
6
|
|
7
|
+
# The watcher instance that wait for changes
|
8
|
+
attr_reader :watcher
|
9
|
+
|
7
10
|
def self.keep_running(cmd, options)
|
8
11
|
runner = new(cmd, options)
|
9
12
|
runner.start
|
10
13
|
runner.join
|
11
14
|
# apparently runner doesn't keep running anymore (as of Listen 2) so we have to sleep forever :-(
|
12
|
-
sleep 10000 while true
|
15
|
+
sleep 10000 while true # :-(
|
13
16
|
end
|
14
17
|
|
15
18
|
include System
|
19
|
+
include ::Timeout
|
16
20
|
|
17
21
|
def initialize(run_command, options = {})
|
18
22
|
@run_command, @options = run_command, options
|
19
23
|
@run_command = "ruby #{@run_command}" if @run_command.split(' ').first =~ /\.rb$/
|
24
|
+
@options[:directory] ||= options.delete(:dir) || '.'
|
25
|
+
@options[:ignore] ||= []
|
20
26
|
end
|
21
27
|
|
22
28
|
def start_keypress_thread
|
@@ -32,22 +38,26 @@ module Rerun
|
|
32
38
|
when 'r'
|
33
39
|
say "Restarting"
|
34
40
|
restart
|
41
|
+
when 'f'
|
42
|
+
say "Stopping and starting"
|
43
|
+
restart(false)
|
35
44
|
when 'p'
|
36
|
-
toggle_pause
|
45
|
+
toggle_pause
|
37
46
|
when 'x', 'q'
|
38
47
|
die
|
39
|
-
break
|
48
|
+
break # the break will stop this thread, in case the 'die' doesn't
|
40
49
|
else
|
41
50
|
puts "\n#{c.inspect} pressed inside rerun"
|
42
51
|
puts [["c", "clear screen"],
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
52
|
+
["r", "restart"],
|
53
|
+
["f", "forced restart (stop and start)"],
|
54
|
+
["p", "toggle pause"],
|
55
|
+
["x or q", "stop and exit"]
|
56
|
+
].map {|key, description| " #{key} -- #{description}"}.join("\n")
|
47
57
|
puts
|
48
58
|
end
|
49
59
|
end
|
50
|
-
sleep 1
|
60
|
+
sleep 1 # todo: use select instead of polling somehow?
|
51
61
|
end
|
52
62
|
end
|
53
63
|
@keypress_thread.run
|
@@ -58,24 +68,24 @@ module Rerun
|
|
58
68
|
@keypress_thread = nil
|
59
69
|
end
|
60
70
|
|
61
|
-
def restart
|
71
|
+
def restart(with_signal = true)
|
62
72
|
@restarting = true
|
63
|
-
|
64
|
-
|
73
|
+
if @options[:restart] && with_signal
|
74
|
+
restart_with_signal(@options[:signal])
|
75
|
+
else
|
76
|
+
stop
|
77
|
+
start
|
78
|
+
end
|
65
79
|
@restarting = false
|
66
80
|
end
|
67
81
|
|
68
|
-
def watcher_running?
|
69
|
-
@watcher && @watcher.running?
|
70
|
-
end
|
71
|
-
|
72
82
|
def toggle_pause
|
73
83
|
unless @pausing
|
74
84
|
say "Pausing. Press 'p' again to resume."
|
75
85
|
@watcher.pause
|
76
86
|
@pausing = true
|
77
87
|
else
|
78
|
-
say "Resuming"
|
88
|
+
say "Resuming."
|
79
89
|
@watcher.unpause
|
80
90
|
@pausing = false
|
81
91
|
end
|
@@ -86,11 +96,7 @@ module Rerun
|
|
86
96
|
end
|
87
97
|
|
88
98
|
def dir
|
89
|
-
@options[:
|
90
|
-
end
|
91
|
-
|
92
|
-
def dirs
|
93
|
-
@options[:dir] || "."
|
99
|
+
@options[:directory]
|
94
100
|
end
|
95
101
|
|
96
102
|
def pattern
|
@@ -101,6 +107,14 @@ module Rerun
|
|
101
107
|
@options[:clear]
|
102
108
|
end
|
103
109
|
|
110
|
+
def quiet?
|
111
|
+
@options[:quiet]
|
112
|
+
end
|
113
|
+
|
114
|
+
def verbose?
|
115
|
+
@options[:verbose]
|
116
|
+
end
|
117
|
+
|
104
118
|
def exit?
|
105
119
|
@options[:exit]
|
106
120
|
end
|
@@ -109,19 +123,19 @@ module Rerun
|
|
109
123
|
@options[:name]
|
110
124
|
end
|
111
125
|
|
112
|
-
def
|
113
|
-
if
|
114
|
-
|
126
|
+
def restart_with_signal(restart_signal)
|
127
|
+
if @pid && (@pid != 0)
|
128
|
+
notify "restarting", "We will be with you shortly."
|
129
|
+
send_signal(restart_signal)
|
115
130
|
end
|
131
|
+
end
|
116
132
|
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
@already_running = true
|
124
|
-
else
|
133
|
+
def force_polling
|
134
|
+
@options[:force_polling]
|
135
|
+
end
|
136
|
+
|
137
|
+
def start
|
138
|
+
if @already_running
|
125
139
|
taglines = [
|
126
140
|
"Here we go again!",
|
127
141
|
"Keep on trucking.",
|
@@ -129,26 +143,33 @@ module Rerun
|
|
129
143
|
"The road goes ever on and on, down from the door where it began.",
|
130
144
|
]
|
131
145
|
notify "restarted", taglines[rand(taglines.size)]
|
146
|
+
else
|
147
|
+
taglines = [
|
148
|
+
"To infinity... and beyond!",
|
149
|
+
"Charge!",
|
150
|
+
]
|
151
|
+
notify "launched", taglines[rand(taglines.size)]
|
152
|
+
@already_running = true
|
132
153
|
end
|
133
154
|
|
134
155
|
clear_screen if clear?
|
135
156
|
start_keypress_thread unless @keypress_thread
|
136
157
|
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
end
|
158
|
+
begin
|
159
|
+
@pid = run @run_command
|
160
|
+
say "Rerun (#{$PID}) running #{app_name} (#{@pid})"
|
161
|
+
rescue => e
|
162
|
+
puts "#{e.class}: #{e.message}"
|
163
|
+
exit
|
144
164
|
end
|
165
|
+
|
145
166
|
status_thread = Process.detach(@pid) # so if the child exits, it dies
|
146
167
|
|
147
168
|
Signal.trap("INT") do # INT = control-C -- allows user to stop the top-level rerun process
|
148
169
|
die
|
149
170
|
end
|
150
171
|
|
151
|
-
Signal.trap("TERM") do
|
172
|
+
Signal.trap("TERM") do # TERM is the polite way of terminating a process
|
152
173
|
die
|
153
174
|
end
|
154
175
|
|
@@ -174,28 +195,47 @@ module Rerun
|
|
174
195
|
end
|
175
196
|
|
176
197
|
unless @watcher
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
message = [:modified, :added, :removed].map do |change|
|
181
|
-
count = changes[change].size
|
182
|
-
if count and count > 0
|
183
|
-
"#{count} #{change}"
|
184
|
-
end
|
185
|
-
end.compact.join(", ")
|
198
|
+
watcher = Watcher.new(@options) do |changes|
|
199
|
+
message = change_message(changes)
|
186
200
|
say "Change detected: #{message}"
|
187
201
|
restart unless @restarting
|
188
202
|
end
|
189
203
|
watcher.start
|
190
204
|
@watcher = watcher
|
191
|
-
|
205
|
+
ignore = @options[:ignore]
|
206
|
+
say "Watching #{dir.join(', ')} for #{pattern}" +
|
207
|
+
(ignore.empty? ? "" : " (ignoring #{ignore.join(',')})") +
|
208
|
+
(watcher.adapter.nil? ? "" : " with #{watcher.adapter_name} adapter")
|
192
209
|
end
|
193
210
|
end
|
194
211
|
|
212
|
+
def run command
|
213
|
+
Kernel.spawn command
|
214
|
+
end
|
215
|
+
|
216
|
+
def change_message(changes)
|
217
|
+
message = [:modified, :added, :removed].map do |change|
|
218
|
+
count = changes[change] ? changes[change].size : 0
|
219
|
+
if count > 0
|
220
|
+
"#{count} #{change}"
|
221
|
+
end
|
222
|
+
end.compact.join(", ")
|
223
|
+
|
224
|
+
changed_files = changes.values.flatten
|
225
|
+
if changed_files.count > 0
|
226
|
+
message += ": "
|
227
|
+
message += changes.values.flatten[0..3].map {|path| path.split('/').last}.join(', ')
|
228
|
+
if changed_files.count > 3
|
229
|
+
message += ", ..."
|
230
|
+
end
|
231
|
+
end
|
232
|
+
message
|
233
|
+
end
|
234
|
+
|
195
235
|
def die
|
196
236
|
#stop_keypress_thread # don't do this since we're probably *in* the keypress thread
|
197
237
|
stop # stop the child process if it exists
|
198
|
-
exit 0
|
238
|
+
exit 0 # todo: status code param
|
199
239
|
end
|
200
240
|
|
201
241
|
def join
|
@@ -203,40 +243,63 @@ module Rerun
|
|
203
243
|
end
|
204
244
|
|
205
245
|
def running?
|
206
|
-
|
246
|
+
send_signal(0)
|
247
|
+
end
|
248
|
+
|
249
|
+
# Send the signal to process @pid and wait for it to die.
|
250
|
+
# @returns true if the process dies
|
251
|
+
# @returns false if either sending the signal fails or the process fails to die
|
252
|
+
def signal_and_wait(signal)
|
253
|
+
|
254
|
+
signal_sent = if windows?
|
255
|
+
force_kill = (signal == 'KILL')
|
256
|
+
system("taskkill /T #{'/F' if force_kill} /PID #{@pid}")
|
257
|
+
else
|
258
|
+
send_signal(signal)
|
259
|
+
end
|
260
|
+
|
261
|
+
if signal_sent
|
262
|
+
# the signal was successfully sent, so wait for the process to die
|
263
|
+
begin
|
264
|
+
timeout(@options[:wait]) do
|
265
|
+
Process.wait(@pid)
|
266
|
+
end
|
267
|
+
process_status = $?
|
268
|
+
say "Process ended: #{process_status}" if verbose?
|
269
|
+
true
|
270
|
+
rescue Timeout::Error
|
271
|
+
false
|
272
|
+
end
|
273
|
+
else
|
274
|
+
false
|
275
|
+
end
|
207
276
|
end
|
208
277
|
|
209
|
-
|
210
|
-
|
278
|
+
# Send the signal to process @pid.
|
279
|
+
# @returns true if the signal is sent
|
280
|
+
# @returns false if sending the signal fails
|
281
|
+
# If sending the signal fails, the exception will be swallowed
|
282
|
+
# (and logged if verbose is true) and this method will return false.
|
283
|
+
#
|
284
|
+
def send_signal(signal)
|
285
|
+
say "Sending signal #{signal} to #{@pid}" unless signal == 0 if verbose?
|
211
286
|
Process.kill(signal, @pid)
|
212
287
|
true
|
213
|
-
rescue
|
288
|
+
rescue => e
|
289
|
+
say "Signal #{signal} failed: #{e.class}: #{e.message}" if verbose?
|
214
290
|
false
|
215
291
|
end
|
216
292
|
|
217
293
|
# todo: test escalation
|
218
294
|
def stop
|
219
|
-
default_signal = @options[:signal] || "TERM"
|
220
295
|
if @pid && (@pid != 0)
|
221
296
|
notify "stopping", "All good things must come to an end." unless @restarting
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
signal(default_signal) && Process.wait(@pid)
|
226
|
-
end
|
227
|
-
rescue Timeout::Error
|
228
|
-
begin
|
229
|
-
timeout(2) do
|
230
|
-
# escalate to SIGINT aka control-C since some foolish process may be ignoring SIGTERM
|
231
|
-
signal("INT") && Process.wait(@pid)
|
232
|
-
end
|
233
|
-
rescue Timeout::Error
|
234
|
-
# escalate to SIGKILL aka "kill -9" which cannot be ignored
|
235
|
-
signal("KILL") && Process.wait(@pid)
|
236
|
-
end
|
297
|
+
@options[:signal].split(',').each do |signal|
|
298
|
+
success = signal_and_wait(signal)
|
299
|
+
return true if success
|
237
300
|
end
|
238
301
|
end
|
239
|
-
rescue
|
302
|
+
rescue
|
240
303
|
false
|
241
304
|
end
|
242
305
|
|
@@ -251,20 +314,25 @@ module Rerun
|
|
251
314
|
@git_head = File.exists?(git_head_file) && File.read(git_head_file)
|
252
315
|
end
|
253
316
|
|
254
|
-
def notify(title, body)
|
255
|
-
|
317
|
+
def notify(title, body, background = true)
|
318
|
+
Notification.new(title, body, @options).send(background) if @options[:notify]
|
256
319
|
puts
|
257
320
|
say "#{app_name} #{title}"
|
258
321
|
end
|
259
322
|
|
260
323
|
def say msg
|
261
|
-
puts "#{Time.now.strftime("%T")} [rerun] #{msg}"
|
324
|
+
puts "#{Time.now.strftime("%T")} [rerun] #{msg}" unless quiet?
|
325
|
+
end
|
326
|
+
|
327
|
+
def stty(args)
|
328
|
+
system "stty #{args}"
|
262
329
|
end
|
263
330
|
|
264
331
|
# non-blocking stdin reader.
|
265
332
|
# returns a 1-char string if a key was pressed; otherwise nil
|
266
333
|
#
|
267
334
|
def key_pressed
|
335
|
+
return one_char if windows?
|
268
336
|
begin
|
269
337
|
# this "raw input" nonsense is because unix likes waiting for linefeeds before sending stdin
|
270
338
|
|
@@ -274,15 +342,10 @@ module Rerun
|
|
274
342
|
# looks like "raw" flips off the OPOST bit 0x00000001 /* enable following output processing */
|
275
343
|
# which disables #define ONLCR 0x00000002 /* map NL to CR-NL (ala CRMOD) */
|
276
344
|
# so this sets it back on again since all we care about is raw input, not raw output
|
277
|
-
|
278
|
-
|
279
|
-
c = nil
|
280
|
-
if $stdin.ready?
|
281
|
-
c = $stdin.getc
|
282
|
-
end
|
283
|
-
c.chr if c
|
345
|
+
stty "raw opost"
|
346
|
+
one_char
|
284
347
|
ensure
|
285
|
-
|
348
|
+
stty "-raw" # turn raw input off
|
286
349
|
end
|
287
350
|
|
288
351
|
# note: according to 'man tty' the proper way restore the settings is
|
@@ -291,7 +354,6 @@ module Rerun
|
|
291
354
|
# system 'stty "#{tty_state}'
|
292
355
|
# end
|
293
356
|
# but this way seems fine and less confusing
|
294
|
-
|
295
357
|
end
|
296
358
|
|
297
359
|
def clear_screen
|
@@ -299,5 +361,14 @@ module Rerun
|
|
299
361
|
$stdout.print "\033[H\033[2J"
|
300
362
|
end
|
301
363
|
|
364
|
+
private
|
365
|
+
def one_char
|
366
|
+
c = nil
|
367
|
+
if $stdin.ready?
|
368
|
+
c = $stdin.getc
|
369
|
+
end
|
370
|
+
c.chr if c
|
371
|
+
end
|
372
|
+
|
302
373
|
end
|
303
374
|
end
|
data/lib/rerun/system.rb
CHANGED
@@ -6,38 +6,16 @@ module Rerun
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def windows?
|
9
|
-
RUBY_PLATFORM =~ /mswin/i
|
9
|
+
RUBY_PLATFORM =~ /(mswin|mingw32)/i
|
10
10
|
end
|
11
11
|
|
12
12
|
def linux?
|
13
13
|
RUBY_PLATFORM =~ /linux/i
|
14
14
|
end
|
15
15
|
|
16
|
-
|
17
|
-
def growl_available?
|
18
|
-
mac? && (growlcmd != "")
|
19
|
-
end
|
20
|
-
|
21
|
-
def growlcmd
|
22
|
-
growlnotify = `which growlnotify`.chomp
|
23
|
-
# todo: check version of growlnotify and warn if it's too old
|
24
|
-
growlnotify
|
25
|
-
end
|
26
|
-
|
27
|
-
def icon
|
28
|
-
here = File.expand_path(File.dirname(__FILE__))
|
29
|
-
icondir = File.expand_path("#{here}/../../icons")
|
16
|
+
def rails?
|
30
17
|
rails_sig_file = File.expand_path(".")+"/config/boot.rb"
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
def growl(title, body, background = true)
|
35
|
-
if growl_available?
|
36
|
-
icon_str = ("--image \"#{icon}\"" if icon)
|
37
|
-
s = "#{growlcmd} -n \"#{app_name}\" -m \"#{body}\" \"#{app_name} #{title}\" #{icon_str}"
|
38
|
-
s += " &" if background
|
39
|
-
`#{s}`
|
40
|
-
end
|
18
|
+
File.exists? rails_sig_file
|
41
19
|
end
|
42
20
|
|
43
21
|
end
|
data/lib/rerun/watcher.rb
CHANGED
@@ -14,7 +14,11 @@ module Rerun
|
|
14
14
|
class Watcher
|
15
15
|
InvalidDirectoryError = Class.new(RuntimeError)
|
16
16
|
|
17
|
-
|
17
|
+
#def self.default_ignore
|
18
|
+
# Listen::Silencer.new(Listen::Listener.new).send :_default_ignore_patterns
|
19
|
+
#end
|
20
|
+
|
21
|
+
attr_reader :directory, :pattern, :priority, :ignore_dotfiles
|
18
22
|
|
19
23
|
# Create a file system watcher. Start it by calling #start.
|
20
24
|
#
|
@@ -29,12 +33,16 @@ module Rerun
|
|
29
33
|
:directory => ".",
|
30
34
|
:pattern => "**/*",
|
31
35
|
:priority => 0,
|
36
|
+
:ignore_dotfiles => true,
|
32
37
|
}.merge(options)
|
33
38
|
|
34
39
|
@pattern = options[:pattern]
|
35
40
|
@directories = options[:directory]
|
36
41
|
@directories = sanitize_dirs(@directories)
|
37
42
|
@priority = options[:priority]
|
43
|
+
@force_polling = options[:force_polling]
|
44
|
+
@ignore = [options[:ignore]].flatten.compact
|
45
|
+
@ignore_dotfiles = options[:ignore_dotfiles]
|
38
46
|
@thread = nil
|
39
47
|
end
|
40
48
|
|
@@ -55,10 +63,9 @@ module Rerun
|
|
55
63
|
end
|
56
64
|
|
57
65
|
@thread = Thread.new do
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
if((modified.size + added.size + removed.size) > 0)
|
66
|
+
@listener = Listen.to(*@directories, only: watching, ignore: ignoring, wait_for_delay: 1, force_polling: @force_polling) do |modified, added, removed|
|
67
|
+
count = modified.size + added.size + removed.size
|
68
|
+
if count > 0
|
62
69
|
@client_callback.call(:modified => modified, :added => added, :removed => removed)
|
63
70
|
end
|
64
71
|
end
|
@@ -72,11 +79,16 @@ module Rerun
|
|
72
79
|
at_exit { stop } # try really hard to clean up after ourselves
|
73
80
|
end
|
74
81
|
|
75
|
-
def
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
82
|
+
def watching
|
83
|
+
Rerun::Glob.new(@pattern).to_regexp
|
84
|
+
end
|
85
|
+
|
86
|
+
def ignoring
|
87
|
+
patterns = []
|
88
|
+
if ignore_dotfiles
|
89
|
+
patterns << /^\.[^.]/ # at beginning of string, a real dot followed by any other character
|
90
|
+
end
|
91
|
+
patterns + @ignore.map { |x| Rerun::Glob.new(x).to_regexp }
|
80
92
|
end
|
81
93
|
|
82
94
|
# kill the file watcher thread
|
@@ -93,7 +105,7 @@ module Rerun
|
|
93
105
|
# wait for the file watcher to finish
|
94
106
|
def join
|
95
107
|
@thread.join if @thread
|
96
|
-
rescue Interrupt
|
108
|
+
rescue Interrupt
|
97
109
|
# don't care
|
98
110
|
end
|
99
111
|
|
@@ -102,12 +114,21 @@ module Rerun
|
|
102
114
|
end
|
103
115
|
|
104
116
|
def unpause
|
105
|
-
@listener.
|
117
|
+
@listener.start if @listener
|
106
118
|
end
|
107
119
|
|
108
120
|
def running?
|
109
|
-
@listener && @listener.
|
121
|
+
@listener && @listener.processing?
|
110
122
|
end
|
111
123
|
|
124
|
+
def adapter
|
125
|
+
@listener &&
|
126
|
+
(backend = @listener.instance_variable_get(:@backend)) &&
|
127
|
+
backend.instance_variable_get(:@adapter)
|
128
|
+
end
|
129
|
+
|
130
|
+
def adapter_name
|
131
|
+
adapter && adapter.class.name.split('::').last
|
132
|
+
end
|
112
133
|
end
|
113
134
|
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
|
+
s.version = '0.13.1'
|
7
7
|
|
8
8
|
s.description = "Restarts your app when a file changes. A no-frills, command-line alternative to Guard, Shotgun, Autotest, etc."
|
9
9
|
s.summary = "Launches an app, and restarts it whenever the filesystem changes. A no-frills, command-line alternative to Guard, Shotgun, Autotest, etc."
|
@@ -25,7 +25,7 @@ $spec = Gem::Specification.new do |s|
|
|
25
25
|
|
26
26
|
s.extra_rdoc_files = %w[README.md]
|
27
27
|
|
28
|
-
s.
|
28
|
+
s.add_runtime_dependency 'listen', '~> 3.0'
|
29
29
|
|
30
30
|
s.homepage = "http://github.com/alexch/rerun/"
|
31
31
|
s.require_paths = %w[lib]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rerun
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Chaffee
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: listen
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '3.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '3.0'
|
27
27
|
description: Restarts your app when a file changes. A no-frills, command-line alternative
|
28
28
|
to Guard, Shotgun, Autotest, etc.
|
29
29
|
email: alex@stinky.com
|
@@ -39,8 +39,10 @@ files:
|
|
39
39
|
- bin/rerun
|
40
40
|
- icons/rails_grn_sml.png
|
41
41
|
- icons/rails_red_sml.png
|
42
|
+
- lib/goo.rb
|
42
43
|
- lib/rerun.rb
|
43
44
|
- lib/rerun/glob.rb
|
45
|
+
- lib/rerun/notification.rb
|
44
46
|
- lib/rerun/options.rb
|
45
47
|
- lib/rerun/runner.rb
|
46
48
|
- lib/rerun/system.rb
|
@@ -50,7 +52,7 @@ homepage: http://github.com/alexch/rerun/
|
|
50
52
|
licenses:
|
51
53
|
- MIT
|
52
54
|
metadata: {}
|
53
|
-
post_install_message:
|
55
|
+
post_install_message:
|
54
56
|
rdoc_options: []
|
55
57
|
require_paths:
|
56
58
|
- lib
|
@@ -65,9 +67,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
67
|
- !ruby/object:Gem::Version
|
66
68
|
version: '0'
|
67
69
|
requirements: []
|
68
|
-
|
69
|
-
|
70
|
-
signing_key:
|
70
|
+
rubygems_version: 3.1.2
|
71
|
+
signing_key:
|
71
72
|
specification_version: 2
|
72
73
|
summary: Launches an app, and restarts it whenever the filesystem changes. A no-frills,
|
73
74
|
command-line alternative to Guard, Shotgun, Autotest, etc.
|