Batch Filesystem Events
inotifywait
is a handy utility that listens for filesystem events you specify and prints them to standard out. This is particularly useful if, say, you want to automatically redeploy a website whenever something changes.
(Jekyll comes with this functionality out-of-the-box, but if you have more than one site, or other non-Jekyll content that changes, it may be desirable to deploy everything together.)
The naive approach to this problem might look something like:
# Filesystem events specific to file creation or modification
events="modify,attrib,close_write,move,create,delete"
# -m = monitor (don’t quit after the first event)
# -r = recursive
inotifywait -m -r -e "$events" $site_dir | while read ; do deploy_site ; done
But the problem with filesystem events is that lots of them can fire simultaneously. A single file save produces multiple events, and a project-wide find/replace opens the floodgates. Since the site will deploy on every individual event, even minor changes kick off 3-4 rebuilds in a row. That’s pretty silly.
I found a few patterns out on the ’net to batch inotifywait
messages, but most of them were pretty complicated. I wondered if it might be possible to do the same thing in a more straightforward fashion, and came up with this:
echo "Watching source directory for changes."
events="modify,attrib,close_write,move,create,delete"
while true
do
count=0
while read -t 1
do
(( count++ ))
done
if [[ $count -gt 0 ]]
then
echo "$count changes detected."
deploy_site
fi
done < <(inotifywait -m -r -e "$events" --exclude '/\..+' $dir 2>/dev/null)
As it turns out, read
accepts a timeout value! Now any inotify events less than a second apart will be counted as part of the same batch and will result in only a single deploy_site
call.
A word of warning: failure to exclude .git
folders will result in a ton of filesystem events. In the above code, I ignore all dotfiles (and folders) to avoid this problem.