Skip to content

Instantly share code, notes, and snippets.

@geekq
Created January 6, 2016 08:45
Show Gist options
  • Save geekq/87e3fd54d1ebbed9129e to your computer and use it in GitHub Desktop.
Save geekq/87e3fd54d1ebbed9129e to your computer and use it in GitHub Desktop.
Runs a user-defined command when files are modified
#!/usr/bin/ruby
require 'rubygems'
require 'rake'
KNOWN_FILE_TYPES = %w(rb txt py yaml erb pp)
# Based on work by Mike Clark.
#
# From http://www.pragmaticautomation.com/cgi-bin/pragauto.cgi/Monitor/StakingOutFileChanges.rdoc
# Modified (with permission) by Geoffrey Grosenbach to call growlnotify for
#
# rspec and Test::Unit output.
#
# See the PeepCode screencast on rSpec or other blog articles for instructions on
# setting up growlnotify.
# gnome notification, default patterns by vladimir@geekq.net
EXPIRATION_IN_SECONDS = 20
IMG_FOLDER = '/home/vd/bin/autotest_images'
CLEAR_AND_RESET_TERMINAL = "\ec\e[2J"
if ARGV.length < 1
puts "
Runs a user-defined command when files are modified.
Like autotest, but more customizable. This is useful when you want to do
something other than run tests. For example, generate a PDF book, run
a single test, or run a legacy Test::Unit suite in an app that also
has an rSpec suite.
Can use Ruby's Dir[] to get file glob. Quote your args to take advantage of this.
Usage:
#{$0} '<command-to-run>' <files-to-watch...>
If files-to-watch is omitted, then files of some known types are watched
(s. source for details)
Examples:
#{$0} 'rake test:recent' **/*.rb
# watch all .rb files in subdirectories and runs rake on file change
#{$0} './runtests.rb
# watch all known file types in subdirectories and run ./runtests.rb on change
"
exit 1
end
$failed = false
def growl(title, msg, img, pri=0, sticky="")
# system "growlnotify -n autotest --image ~/.autotest_images/#{img} -p #{pri} -m #{msg.inspect} #{title} #{sticky}"
img_path = File.join(IMG_FOLDER,img)
# puts "\nyad --notification --timeout=10 --text '#{title} - #{msg}' --image=#{img_path} &"
# puts "\nnotify-send -t #{EXPIRATION_IN_SECONDS * 1000} -i #{img_path} '#{title}' '#{msg}'"
system "yad --notification --timeout=10 --text '#{title} - #{msg}' --image=#{img_path} &"
system "notify-send -t #{EXPIRATION_IN_SECONDS * 1000} -i #{img_path} '#{title}' '#{msg}'"
end
def self.growl_fail(output)
$failed = true
growl "FAIL", "#{output}", "fail.png", 2
end
def self.growl_pass(output)
puts "rstakeout.rb - in growl_pass"
unless $failed # safeguard - if we already failed we can not show the green light
growl "Pass", "#{output}", "pass.png"
end
end
command = ARGV[0]
patterns = ARGV[1..-1]
if patterns.length == 0
patterns = KNOWN_FILE_TYPES.map {|ext| "**/*.#{ext}"}
else
patterns = ARGV
end
puts "Patterns: #{patterns.inspect}"
files = {}
patterns.each do |arg|
Dir[arg].each { |file|
files[file] = File.mtime(file)
}
end
puts "Watching #{files.keys.join(', ')}\nFiles: #{files.keys.length}"
trap('INT') do
puts "\nQuitting..."
exit
end
changed_file = false
run_number = 0
loop do
begin #if changed_file
# clear log file(s)
FileList["log/test*.log"].each do |log_file| # or use "log/*.log"
f = File.open(log_file, "w")
f.close
end
$logfile = File.open('/tmp/rstakeout.log','w')
puts CLEAR_AND_RESET_TERMINAL if run_number > 0
run_number += 1
puts "running #{command}"
IO.popen(command) { |f|
f.each do |results|
puts results
if results.include? 'tests'
output = results.slice(/(\d+)\s+tests?,\s*(\d+)\s+assertions?,\s*(\d+)\s+failures?(,\s*(\d+)\s+errors)?/)
if output
puts "rstakeout detected failures, errors: #{$~[3]}, #{$~[5]} "
($~[3].to_i + $~[5].to_i) > 0 ? growl_fail(output) : growl_pass(output)
end
else
output = results.slice(/(\d+)\s+examples?,\s*(\d+)\s+failures?(,\s*(\d+)\s+not implemented)?/)
if output
$~[2].to_i > 0 ? growl_fail(output) : growl_pass(output)
end
end
# for python doctest
if results.include? '***Test Failed***'
growl_fail(output)
end
if results.include? 'Test passed.'
growl_pass(output)
end
# for cucumber
if results.include? 'Failing Scenarios'
growl_fail(output)
elsif results =~ /^\d+ scenario \(/
growl_pass(output)
end
# for JavaScript buster tests
$logfile.puts '~' + results
if results =~ /\d+ test.*, \d+ assertion.*, \d+ runtim.* .* failure/
growl_fail("JS-failure")
elsif results =~ /\.\.\. OK/
$logfile.puts "Detected 'OK'"
growl_pass('JS-tests-OK')
end
# TODO Generic growl notification for other actions
end
}
$logfile.close unless $logfile.closed?
puts "=> done"
end
until changed_file do
sleep 1
changed_file, last_changed = files.find { |file, last_changed|
File.mtime(file) > last_changed
}
end
while changed_file do # wait until all files were saved and everything calmed down
files[changed_file] = File.mtime(changed_file)
puts "=> #{changed_file} changed"
sleep 2
changed_file, last_changed = files.find { |file, last_changed|
File.mtime(file) > last_changed
}
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment