Skip to content

Commit

Permalink
add support for .rerun config file
Browse files Browse the repository at this point in the history
  • Loading branch information
alexch committed Jan 24, 2018
1 parent d5ee964 commit baf46e7
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 116 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ end
group :test do
gem 'rspec', ">=3.0"
gem 'wrong', ">=0.6.2"
gem 'files'
end

gem 'wdm', '>= 0.1.0' if Gem.win_platform?
24 changes: 23 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ Procfile processes locally and restart them all when necessary.

# Options:

These options can be specified on the command line and/or inside a `.rerun` config file (see below).

`--dir` directory (or directories) to watch (default = "."). Separate multiple paths with ',' and/or use multiple `-d` options.

`--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.
Expand Down Expand Up @@ -167,6 +169,23 @@ This may be useful for forcing the respective process to terminate as quickly as

Also `--version` and `--help`, naturally.

## Config file

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:

```
--quiet
--pattern **/*.{rb,js,scss,sass,html,md}
```

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:

```sh
rerun --no-quiet rackup
```

If you're not sure what options are being overwritten, use `--verbose` and rerun will show you the final result of the parsing.

# Notifications

If you have `growlnotify` available on the `PATH`, it sends notifications to
Expand Down Expand Up @@ -250,11 +269,11 @@ rerun -p "**/*.rb" rake test
# To Do:

## Must have for v1.0
* ".rerun" file to specify options per project or in $HOME.
* Make sure to pass through quoted options correctly to target process [bug]
* Optionally do "bundle install" before and "bundle exec" during launch

## Nice to have
* ".rerun" file in $HOME
* If the last element of the command is a `.ru` file and there's no other command then use `rackup`
* Figure out an algorithm so "-x" is not needed (if possible) -- maybe by accepting a "--port" option or reading `config.ru`
* Specify (or deduce) port to listen for to determine success of a web server launch
Expand Down Expand Up @@ -368,6 +387,9 @@ Based upon and/or inspired by:

# Version History

*
* `.rerun` config file

* v0.12.0 23 January 2018
* smarter `--signal` option, allowing you to specify a series of signals to try in order; also `--wait` to change how long between tries
* `--force-polling` option (thanks ajduncan)
Expand Down
12 changes: 9 additions & 3 deletions bin/rerun
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ $LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
require 'rerun'
require 'optparse'

options = Rerun::Options.parse
exit if options.nil?
runner = Rerun::Runner.keep_running(options[:cmd], options)
options = Rerun::Options.parse config_file: ".rerun"

if options and options[:verbose]
puts "\nrerun options:\n\t#{options}"
end

exit if options.nil? or options[:cmd].nil? or options[:cmd].empty?

Rerun::Runner.keep_running(options[:cmd], options)
115 changes: 59 additions & 56 deletions lib/rerun/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,126 +17,129 @@ class Options
DEFAULT_DIRS = ["."]

DEFAULTS = {
:pattern => DEFAULT_PATTERN,
:signal => (windows? ? "TERM,KILL" : "TERM,INT,KILL"),
:wait => 2,
:notify => true,
:quiet => false,
:verbose => false,
:name => Pathname.getwd.basename.to_s.capitalize,
:ignore => [],
:dir => DEFAULT_DIRS,
:force_polling => false,
:pattern => DEFAULT_PATTERN,
:signal => (windows? ? "TERM,KILL" : "TERM,INT,KILL"),
:wait => 2,
:notify => true,
:quiet => false,
:verbose => false,
:background => false,
:name => Pathname.getwd.basename.to_s.capitalize,
:ignore => [],
:dir => DEFAULT_DIRS,
:force_polling => false,
}

def self.parse args = ARGV
def self.parse args: ARGV, config_file: nil

default_options = DEFAULTS.dup
options = {
ignore: []
ignore: []
}

opts = OptionParser.new("", 24, ' ') do |opts|
opts.banner = "Usage: rerun [options] [--] cmd"
if config_file && File.exist?(config_file)
require 'shellwords'
config_args = File.read(config_file).shellsplit
args = config_args + args
end

option_parser = OptionParser.new("", 24, ' ') do |o|
o.banner = "Usage: rerun [options] [--] cmd"

opts.separator ""
opts.separator "Launches an app, and restarts it when the filesystem changes."
opts.separator "See http://github.com/alexch/rerun for more info."
opts.separator "Version: #{$spec.version}"
opts.separator ""
opts.separator "Options:"
o.separator ""
o.separator "Launches an app, and restarts it when the filesystem changes."
o.separator "See http://github.com/alexch/rerun for more info."
o.separator "Version: #{$spec.version}"
o.separator ""
o.separator "Options:"

opts.on("-d dir", "--dir dir", "directory to watch, default = \"#{DEFAULT_DIRS}\". Specify multiple paths with ',' or separate '-d dir' option pairs.") do |dir|
o.on("-d dir", "--dir dir", "directory to watch, default = \"#{DEFAULT_DIRS}\". Specify multiple paths with ',' or separate '-d dir' option pairs.") do |dir|
elements = dir.split(",")
options[:dir] = (options[:dir] || []) + elements
end

# todo: rename to "--watch"
opts.on("-p pattern", "--pattern pattern", "file glob to watch, default = \"#{DEFAULTS[:pattern]}\"") do |pattern|
o.on("-p pattern", "--pattern pattern", "file glob to watch, default = \"#{DEFAULTS[:pattern]}\"") do |pattern|
options[:pattern] = pattern
end

opts.on("-i pattern", "--ignore pattern", "file glob to ignore (can be set many times). To ignore a directory, you must append '/*' e.g. --ignore 'coverage/*'") do |pattern|
o.on("-i pattern", "--ignore pattern", "file glob to ignore (can be set many times). To ignore a directory, you must append '/*' e.g. --ignore 'coverage/*'") do |pattern|
options[:ignore] += [pattern]
end

opts.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|
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|
options[:signal] = signal
end

opts.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")
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")

opts.on("-r", "--restart", "expect process to restart itself, so just send a signal and continue watching. Uses the HUP signal unless overridden using --signal") do |signal|
o.on("-r", "--restart", "expect process to restart itself, so just send a signal and continue watching. Uses the HUP signal unless overridden using --signal") do |signal|
options[:restart] = true
default_options[:signal] = "HUP"
end

opts.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 |dir|
options[:exit] = true
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|
options[:exit] = value
end

opts.on("-c", "--clear", "clear screen before each run") do
options[:clear] = true
o.on("-c", "--clear", "clear screen before each run") do |value|
options[:clear] = value
end

opts.on("-b", "--background", "disable on-the-fly commands, allowing the process to be backgrounded") do
options[:background] = true
o.on("-b", "--background", "disable on-the-fly commands, allowing the process to be backgrounded") do |value|
options[:background] = value
end

opts.on("-n name", "--name name", "name of app used in logs and notifications, default = \"#{DEFAULTS[:name]}\"") do |name|
o.on("-n name", "--name name", "name of app used in logs and notifications, default = \"#{DEFAULTS[:name]}\"") do |name|
options[:name] = name
end

opts.on("--force-polling", "use polling instead of a native filesystem scan (useful for Vagrant)") do
options[:force_polling] = true
o.on("--[no-]force-polling", "use polling instead of a native filesystem scan (useful for Vagrant)") do |value|
options[:force_polling] = value
end

opts.on("--no-growl", "don't use growl [OBSOLETE]") do
o.on("--no-growl", "don't use growl [OBSOLETE]") do
options[:growl] = false
$stderr.puts "--no-growl is obsolete; use --no-notify instead"
return
end

opts.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|
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|
notifier = true if notifier.nil?
options[:notify] = notifier
end

opts.on("-q", "--quiet", "don't output any logs") do
options[:quiet] = true
o.on("-q", "--[no-]quiet", "don't output any logs") do |value|
options[:quiet] = value
end

opts.on("--verbose", "log extra stuff like PIDs (unless you also specified `--quiet`") do
options[:verbose] = true
o.on("--[no-]verbose", "log extra stuff like PIDs (unless you also specified `--quiet`") do |value|
options[:verbose] = value
end

opts.on_tail("-h", "--help", "--usage", "show this message") do
puts opts
o.on_tail("-h", "--help", "--usage", "show this message and immediately exit") do
puts o
return
end

opts.on_tail("--version", "show version") do
o.on_tail("--version", "show version and immediately exit") do
puts $spec.version
return
end

opts.on_tail ""
opts.on_tail "On top of --pattern and --ignore, we ignore any changes to files and dirs starting with a dot."
o.on_tail ""
o.on_tail "On top of --pattern and --ignore, we ignore any changes to files and dirs starting with a dot."

end

if args.empty?
puts opts
nil
else
opts.parse! args
default_options[:cmd] = args.join(" ")
option_parser.parse! args
options = default_options.merge(options)
options[:cmd] = args.join(" ").strip # todo: better arg word handling

options = default_options.merge(options)
puts option_parser if args.empty?

options
end
options
end

end

end
9 changes: 7 additions & 2 deletions lib/rerun/runner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -333,6 +333,10 @@ def say msg
puts "#{Time.now.strftime("%T")} [rerun] #{msg}" unless quiet?
end

def stty(args)
system "stty #{args}"
end

# non-blocking stdin reader.
# returns a 1-char string if a key was pressed; otherwise nil
#
Expand All @@ -346,17 +350,18 @@ def key_pressed
# looks like "raw" flips off the OPOST bit 0x00000001 /* enable following output processing */
# which disables #define ONLCR 0x00000002 /* map NL to CR-NL (ala CRMOD) */
# so this sets it back on again since all we care about is raw input, not raw output
system("stty raw opost")
stty "raw opost"

c = nil
if $stdin.ready?
c = $stdin.getc
end
c.chr if c
ensure
system "stty -raw" # turn raw input off
stty "-raw" # turn raw input off
end


# note: according to 'man tty' the proper way restore the settings is
# tty_state=`stty -g`
# ensure
Expand Down
Loading

0 comments on commit baf46e7

Please sign in to comment.