#! /usr/bin/env ruby Main do name 'rego' version Rego.version description <<-__ run arbitrary commands easily when files change __ examples <<-__ # say hai whenever the file foo.txt changes # ~> rego foo.txt -- echo hai # say hai whenever any file (recursively) in bar changes # ~> rego ./bar/ -- echo hai # echo *the file that changed* when any file (recursively) in bar changes # ~> rego ./bar/ -- echo "@ was changed" # run a specific test whenever anything in lib, test, app, or config changes # ~> rego {lib,test,app,config} -- ruby -Itest ./test/units/foo_test.rb --name teh_test # run a specific test whenever it, or your app, has changed # ~> rego ./test -- ruby -Itest @ __ option('--path=path', '-p') option('--paths=paths') option('--command=command', '-c') def run parse_the_command_line! print_a_summary_of_watched_files! loop_watching_files_and_running_commands! end def parse_the_command_line! @paths = @params[:paths].values + @params[:path].values @command = @params[:command].values state = :paths @argv.each do |arg| if arg.strip == '--' state = :commands next end if state == :paths @paths << arg else @command << arg end end @paths.push(Dir.pwd) if @paths.empty? @command.push('ls') if @command.empty? @pretty = { command: @command.map { |token| Shellwords.escape(token) }.join(' '), paths: @paths.map { |path| Rego.relative_path(path, from: Dir.pwd) }.join(', ') } return unless @paths.empty? abort "no paths to watch found in `#{$0} #{@argv.join(' ')}`" end def print_a_summary_of_watched_files! Rego.say("#=> rego.command: #{@pretty[:command]}", color: :cyan) Rego.say("#=> rego.paths: #{@pretty[:paths]}", color: :cyan) puts end def loop_watching_files_and_running_commands! cmdno = '0' rego = proc do puts Rego.say("#=> rego.#{cmdno} @ #{Time.now.strftime('%H:%M:%S')} -> #{@pretty[:command]}", color: :yellow) success = system(*@command) puts Rego.say("#=> rego.#{cmdno} @ #{Time.now.strftime('%H:%M:%S')} -> #{$?.exitstatus}", color: (success ? :green : :red)) puts cmdno.succ! end listener = Listen.to(*@paths) do |_modified, _added, _removed| rego.call end begin rego.call listener.start sleep rescue SignalException exit(0) end end end BEGIN { # setup a child process to catch signals and brutally shut down the parent as # a monkey-patch to listen/rb-fsevent's busted ctrl-c handling... # unless (pid = fork) ppid = Process.ppid begin trap('SIGINT') do %w[ SIGTERM SIGINT SIGQUIT SIGKILL ].each do |signal| begin Process.kill("-#{signal}", ppid) rescue Object nil end sleep(rand) end end loop do Process.kill(0, ppid) sleep(1) end rescue Object => e exit!(0) end end require 'pathname' this = Pathname.new(__FILE__).realpath.to_s bindir = File.dirname(this) rootdir = File.dirname(bindir) libdir = File.join(rootdir, 'lib') lib = File.join(libdir, 'rego.rb') require(lib) STDOUT.sync = true STDERR.sync = true STDIN.sync = true }