summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLucas Kanashiro <kanashiro.duarte@gmail.com>2016-02-17 21:49:13 -0200
committerLucas Kanashiro <kanashiro.duarte@gmail.com>2016-02-17 21:49:13 -0200
commit8f76b7628d422cc884b680909a3de8d8773ce8f2 (patch)
treebb3d16a76dfecc5893303a3933c55e6f7d96a5ab
parent51ff20c794b8b27ee50f0199739c8d1d3f31107f (diff)
parentad1dced46cee68ddce1b701e8c774ba1e5c6146b (diff)
Merge tag 'upstream/0.09'
Upstream version 0.09 # gpg: Signature made Wed 17 Feb 2016 09:49:09 PM BRST using RSA key ID 9883C97C # gpg: Good signature from "Lucas Kanashiro <kanashiro.duarte@gmail.com>" # gpg: WARNING: This key is not certified with a trusted signature! # gpg: There is no indication that the signature belongs to the owner. # Primary key fingerprint: 8ED6 C3F8 BAC9 DB7F C130 A870 F823 A272 9883 C97C
-rw-r--r--Changes14
-rw-r--r--LICENSE8
-rw-r--r--MANIFEST1
-rw-r--r--META.json19
-rw-r--r--META.yml10
-rw-r--r--Makefile.PL29
-rw-r--r--README4
-rw-r--r--README.pod124
-rwxr-xr-xbin/inotify-hookable2
-rwxr-xr-xlib/App/Inotify/Hookable.pm28
-rw-r--r--t/00-compile.t121
11 files changed, 272 insertions, 88 deletions
diff --git a/Changes b/Changes
index 1448427..fbc39fe 100644
--- a/Changes
+++ b/Changes
@@ -1,5 +1,19 @@
Revision history for App::Inotify::Hookable
+0.09 2016-02-15 20:33:43
+
+ - I forgot to write a changelog for 0.08, this release is
+ identical to it except I'm now updating the Changes file:
+
+ - Fixed a common race condition bug where inotify-hookable would
+ just plain die if a directory it tried to watch had been deleted
+ (happens e.g. in the middle of "git checkout").
+
+ Now we just ignore that and move on, before the whola watcher
+ would just stop.
+
+0.08 2016-02-15 20:29:38
+
0.07 2012-12-21 16:18:36
- Use find(1) again instead of File::Find::Rule, it's noticably
diff --git a/LICENSE b/LICENSE
index 45f66ce..32a87e2 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-This software is copyright (c) 2012 by Ævar Arnfjörð Bjarmason.
+This software is copyright (c) 2016 by Ævar Arnfjörð Bjarmason.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
@@ -12,7 +12,7 @@ b) the "Artistic License"
--- The GNU General Public License, Version 1, February 1989 ---
-This software is Copyright (c) 2012 by Ævar Arnfjörð Bjarmason.
+This software is Copyright (c) 2016 by Ævar Arnfjörð Bjarmason.
This is free software, licensed under:
@@ -22,7 +22,7 @@ This is free software, licensed under:
Version 1, February 1989
Copyright (C) 1989 Free Software Foundation, Inc.
- 51 Franklin St, Suite 500, Boston, MA 02110-1335 USA
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -272,7 +272,7 @@ That's all there is to it!
--- The Artistic License 1.0 ---
-This software is Copyright (c) 2012 by Ævar Arnfjörð Bjarmason.
+This software is Copyright (c) 2016 by Ævar Arnfjörð Bjarmason.
This is free software, licensed under:
diff --git a/MANIFEST b/MANIFEST
index 1f150ca..c7663d8 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -5,6 +5,7 @@ META.json
META.yml
Makefile.PL
README
+README.pod
bin/inotify-hookable
dist.ini
lib/App/Inotify/Hookable.pm
diff --git a/META.json b/META.json
index 776ff53..3f67b95 100644
--- a/META.json
+++ b/META.json
@@ -1,10 +1,10 @@
{
"abstract" : "blocking command-line interface to inotify",
"author" : [
- "\u00c3\u0086var Arnfj\u00c3\u00b6r\u00c3\u00b0 Bjarmason <avar@cpan.org>"
+ "\u00c6var Arnfj\u00f6r\u00f0 Bjarmason <avar@cpan.org>"
],
"dynamic_config" : 0,
- "generated_by" : "Dist::Zilla version 4.300021, CPAN::Meta::Converter version 2.120921",
+ "generated_by" : "Dist::Zilla version 4.300040, CPAN::Meta::Converter version 2.143240",
"license" : [
"perl_5"
],
@@ -20,6 +20,11 @@
"ExtUtils::MakeMaker" : "6.30"
}
},
+ "develop" : {
+ "requires" : {
+ "Test::Pod" : "1.41"
+ }
+ },
"runtime" : {
"requires" : {
"Class::Inspector" : "0",
@@ -39,9 +44,11 @@
},
"test" : {
"requires" : {
- "File::Find" : "0",
- "File::Temp" : "0",
- "Test::More" : "0"
+ "File::Spec" : "0",
+ "IO::Handle" : "0",
+ "IPC::Open3" : "0",
+ "Test::More" : "0",
+ "perl" : "5.006"
}
}
},
@@ -61,7 +68,7 @@
"web" : "http://github.com/avar/app-inotify-hookable"
}
},
- "version" : "0.07",
+ "version" : "0.09",
"x_authority" : "cpan:AVAR"
}
diff --git a/META.yml b/META.yml
index 6582df7..cf4c9a4 100644
--- a/META.yml
+++ b/META.yml
@@ -3,13 +3,15 @@ abstract: 'blocking command-line interface to inotify'
author:
- 'Ævar Arnfjörð Bjarmason <avar@cpan.org>'
build_requires:
- File::Find: 0
- File::Temp: 0
+ File::Spec: 0
+ IO::Handle: 0
+ IPC::Open3: 0
Test::More: 0
+ perl: 5.006
configure_requires:
ExtUtils::MakeMaker: 6.30
dynamic_config: 0
-generated_by: 'Dist::Zilla version 4.300021, CPAN::Meta::Converter version 2.120921'
+generated_by: 'Dist::Zilla version 4.300040, CPAN::Meta::Converter version 2.143240'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -35,5 +37,5 @@ resources:
homepage: http://metacpan.org/release/App-Inotify-Hookable
license: http://dev.perl.org/licenses/
repository: git://github.com/avar/app-inotify-hookable.git
-version: 0.07
+version: 0.09
x_authority: cpan:AVAR
diff --git a/Makefile.PL b/Makefile.PL
index b4614db..4d549f7 100644
--- a/Makefile.PL
+++ b/Makefile.PL
@@ -10,12 +10,8 @@ use ExtUtils::MakeMaker 6.30;
my %WriteMakefileArgs = (
"ABSTRACT" => "blocking command-line interface to inotify",
- "AUTHOR" => "\303\206var Arnfj\303\266r\303\260 Bjarmason <avar\@cpan.org>",
- "BUILD_REQUIRES" => {
- "File::Find" => 0,
- "File::Temp" => 0,
- "Test::More" => 0
- },
+ "AUTHOR" => "\x{c6}var Arnfj\x{f6}r\x{f0} Bjarmason <avar\@cpan.org>",
+ "BUILD_REQUIRES" => {},
"CONFIGURE_REQUIRES" => {
"ExtUtils::MakeMaker" => "6.30"
},
@@ -40,13 +36,32 @@ my %WriteMakefileArgs = (
"strict" => 0,
"warnings" => 0
},
- "VERSION" => "0.07",
+ "TEST_REQUIRES" => {
+ "File::Spec" => 0,
+ "IO::Handle" => 0,
+ "IPC::Open3" => 0,
+ "Test::More" => 0
+ },
+ "VERSION" => "0.09",
"test" => {
"TESTS" => "t/*.t"
}
);
+unless ( eval { ExtUtils::MakeMaker->VERSION(6.63_03) } ) {
+ my $tr = delete $WriteMakefileArgs{TEST_REQUIRES};
+ my $br = $WriteMakefileArgs{BUILD_REQUIRES};
+ for my $mod ( keys %$tr ) {
+ if ( exists $br->{$mod} ) {
+ $br->{$mod} = $tr->{$mod} if $tr->{$mod} > $br->{$mod};
+ }
+ else {
+ $br->{$mod} = $tr->{$mod};
+ }
+ }
+}
+
unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) {
my $br = delete $WriteMakefileArgs{BUILD_REQUIRES};
my $pp = $WriteMakefileArgs{PREREQ_PM};
diff --git a/README b/README
index 7aa1691..a420ace 100644
--- a/README
+++ b/README
@@ -46,6 +46,10 @@ DESCRIPTION
probably run in another process via POE or something). Patches welcome.
OPTIONS
+ Note that boolean options can be negated with "--no-OPTION", e.g.
+ "--no-r" or "--no-recursive" to turn off the "--recursive" option which
+ is on by default.
+
"-w" or "--watch-directories"
Specify this to watch a directory, you can give this however many times
you like to watch lots of directories.
diff --git a/README.pod b/README.pod
new file mode 100644
index 0000000..4a7f2dd
--- /dev/null
+++ b/README.pod
@@ -0,0 +1,124 @@
+=encoding utf8
+
+=head1 NAME
+
+App::Inotify::Hookable - blocking command-line interface to inotify
+
+=head1 SYNOPSIS
+
+Watch a directory, tell us when things change in it:
+
+ inotify-hookable --watch-directories /tmp/watch-this
+
+Watch a git tree, some configs, and a repository of static assets,
+restart the webserver or compress those assets if anything changes:
+
+ inotify-hookable \
+ --watch-directories /etc/uwsgi \
+ --watch-directories /git_tree/central \
+ --watch-directories /etc/app-config \
+ --watch-directories /git_tree/static_assets \
+ --on-modify-path-command "^(/etc/uwsgi|/git_tree/central|/etc/app-config)=sudo /etc/init.d/uwsgi restart" \
+ --on-modify-path-command "^/git_tree/static_assets=(cd /git_tree/static_assets && compress_static_assets)"
+
+Or watch specific files:
+
+ inotify-hookable \
+ --watch-files /var/www/cgi-bin/mod_perl_handler \
+ --on-modify-command "apachectl restart"
+
+=head1 DESCRIPTION
+
+This simple command-line program is my replacement for the
+functionality offered by L<Plack>'s L<Filesys::Notify::Simple>. I
+found that on very large git trees Plack would spend an inordinate
+amount watching the filesystem for changes.
+
+This program uses L<Linux::Inotify2>, so the kernel will notify it
+B<instantly> when something changes (actually it's so fast that we
+have to work around how fast it sends us events).
+
+The result is that you can run this e.g. in a screen session and have
+it watch your development environment, and your webserver will have
+begun restarting before your finger leaves the I<save> button.
+
+vim and emacs temporary files are ignored by default (see C<--ignore-paths>.)
+so you can edit your files without your server restarting unnecessarily.
+
+Currently the command-line interface for this is the only one that
+really makes sense, this module is entirely blocking (although it
+could probably run in another process via L<POE> or
+something). Patches welcome.
+
+=head1 OPTIONS
+
+Note that boolean options can be negated with C<--no-OPTION>,
+e.g. C<--no-r> or C<--no-recursive> to turn off the C<--recursive>
+option which is on by default.
+
+=head2 C<-w> or C<--watch-directories>
+
+Specify this to watch a directory, you can give this however many
+times you like to watch lots of directories.
+
+=head2 C<-f> or C<--watch-files>
+
+Watch a file, specify multiple times for multiple files.
+You can watch files and directories in the same command.
+
+=head2 C<-r> or C<--recursive>
+
+If you supply this any directory you give will be recursively
+watched. This is on by default.
+
+=head2 C<-c> or C<--on-modify-command>
+
+A command that will be run when something is modified.
+
+=head2 C<-C> or C<--on-modify-path-command>
+
+A key-value pair where the key is a regex that'll be matched against a
+modified path, and the value is a command that'll be run. See the
+L</SYNOPSIS> for an example.
+
+Useful for e.g. restarting a webserver if you modify directory F<A>
+but compressing some static assets if you modify directory F<B>.
+
+=head2 C<-t> or C<--buffer-time>
+
+Linux will send you inotify events B<really> fast, so fast that if you
+run something like:
+
+ touch foo bar
+
+You might get an event for F<foo> in one batch, followed by an event
+for F<bar> later on.
+
+To deal with this we enter a loop when we start getting events and sleep for a
+default of 100 microseconds, as long as we keep getting events we keep sleeping
+for 100 microseconds, but as soon as we haven't received anything new we fire
+off our event handlers.
+
+=head2 C<-i> or C<--ignore-paths>
+
+Regexes for files/directories to ignore events for. By default this is set to
+regexes for vim and emacs temporary files, C<qr{\..*sw.\z}> and
+C<qr{\.\#[^/]+\z}> respectively.
+
+The regexes match after any C</> in the path or the beginning of the string.
+
+=head2 C<-d> or C<--debug>
+
+Spew out some verbose debug output while running.
+
+=head1 ACKNOWLEDGMENT
+
+This module was originally developed at and for Booking.com. With
+approval from Booking.com, this module was generalized and put on
+CPAN, for which the authors would like to express their gratitude.
+
+=head1 AUTHOR
+
+Ævar Arnfjörð Bjarmason <avar@cpan.org>
+
+=cut
diff --git a/bin/inotify-hookable b/bin/inotify-hookable
index 9274b04..14e585e 100755
--- a/bin/inotify-hookable
+++ b/bin/inotify-hookable
@@ -4,7 +4,7 @@ BEGIN {
$main::AUTHORITY = 'cpan:AVAR';
}
{
- $main::VERSION = '0.07';
+ $main::VERSION = '0.09';
}
use strict;
use warnings;
diff --git a/lib/App/Inotify/Hookable.pm b/lib/App/Inotify/Hookable.pm
index cbdf561..e5f189a 100755
--- a/lib/App/Inotify/Hookable.pm
+++ b/lib/App/Inotify/Hookable.pm
@@ -4,7 +4,7 @@ BEGIN {
$App::Inotify::Hookable::AUTHORITY = 'cpan:AVAR';
}
{
- $App::Inotify::Hookable::VERSION = '0.07';
+ $App::Inotify::Hookable::VERSION = '0.09';
}
use Moose;
use MooseX::Types::Moose ':all';
@@ -91,7 +91,7 @@ has buffer_time => (
isa => Int,
default => 100,
cmd_aliases => 't',
- documentation => "How many us should we buffer inotify for? (default 100)",
+ documentation => "How many milliseconds should we buffer inotify for? (default 100)",
);
has ignore_paths => (
@@ -138,7 +138,7 @@ sub log {
my $dumper_squashed = sub {
my $val = shift;
-
+
my $dd = Data::Dumper->new([]);
$dd->Terse(1)->Indent(1)->Useqq(1)->Deparse(1)->Quotekeys(0)->Sortkeys(1)->Indent(0);
return $dd->Values([ $val ])->Dump;
@@ -147,7 +147,7 @@ my $dumper_squashed = sub {
sub run {
my ($self) = @_;
- # Catch sigint so DEMOLISH can run
+ # Catch sigint so DEMOLISH can run
local $SIG{INT} = sub { exit 1 };
my @watch_dirs = $self->watch_directories;
@@ -156,7 +156,8 @@ sub run {
$self->log(
"Starting up, " .
(@watch_dirs ?
- ($self->recursive ? "recursively " : "") .
+ ($self->recursive ? "recursively" : "non-recursively") .
+ " " .
"watching directories <@watch_dirs>" .
(@watch_files ? " and " : "")
: "") .
@@ -218,11 +219,11 @@ sub run {
@events = $notifier->read;
ualarm(0);
} catch {
- $self->log("We have no more events with a timeout of $sleep_ms us") if $self->debug;
+ $self->log("We have no more events with a timeout of $sleep_ms ms") if $self->debug;
};
if (@events) {
- $self->log("We have events, waiting another $sleep_ms us and checking again") if $self->debug;
+ $self->log("We have events, waiting another $sleep_ms ms and checking again") if $self->debug;
$log_modified_paths->(\@events);
} else {
@@ -368,7 +369,7 @@ sub setup_watch {
$path,
(
# Is this is a directory?
- ($type eq 'directory' ?
+ ($type eq 'directory' ?
# Modifications I care about
IN_MODIFY
|
@@ -415,6 +416,13 @@ The original error was:
$error
DIE
+ } elsif ($error == ENOENT) {
+ # Don't hard die on the common race condition where a
+ # file/directory we found with our "find" call has
+ # since gone away (e.g. due to a different "git
+ # checkout" removing it).
+ $self->log("Couldn't watch $type '$path': $error");
+ next WATCH;
} else {
die $error;
}
@@ -513,6 +521,10 @@ something). Patches welcome.
=head1 OPTIONS
+Note that boolean options can be negated with C<--no-OPTION>,
+e.g. C<--no-r> or C<--no-recursive> to turn off the C<--recursive>
+option which is on by default.
+
=head2 C<-w> or C<--watch-directories>
Specify this to watch a directory, you can give this however many
diff --git a/t/00-compile.t b/t/00-compile.t
index 6771e12..843cdcc 100644
--- a/t/00-compile.t
+++ b/t/00-compile.t
@@ -1,73 +1,78 @@
-#!perl
-
+use 5.006;
use strict;
use warnings;
-use Test::More;
+# this test was generated with Dist::Zilla::Plugin::Test::Compile 2.051
+use Test::More;
+plan tests => 2 + ($ENV{AUTHOR_TESTING} ? 1 : 0);
-use File::Find;
-use File::Temp qw{ tempdir };
+my @module_files = (
+ 'App/Inotify/Hookable.pm'
+);
-my @modules;
-find(
- sub {
- return if $File::Find::name !~ /\.pm\z/;
- my $found = $File::Find::name;
- $found =~ s{^lib/}{};
- $found =~ s{[/\\]}{::}g;
- $found =~ s/\.pm$//;
- # nothing to skip
- push @modules, $found;
- },
- 'lib',
+my @scripts = (
+ 'bin/inotify-hookable'
);
-sub _find_scripts {
- my $dir = shift @_;
-
- my @found_scripts = ();
- find(
- sub {
- return unless -f;
- my $found = $File::Find::name;
- # nothing to skip
- open my $FH, '<', $_ or do {
- note( "Unable to open $found in ( $! ), skipping" );
- return;
- };
- my $shebang = <$FH>;
- return unless $shebang =~ /^#!.*?\bperl\b\s*$/;
- push @found_scripts, $found;
- },
- $dir,
- );
-
- return @found_scripts;
-}
+# no fake home requested
-my @scripts;
-do { push @scripts, _find_scripts($_) if -d $_ }
- for qw{ bin script scripts };
+my $inc_switch = -d 'blib' ? '-Mblib' : '-Ilib';
-my $plan = scalar(@modules) + scalar(@scripts);
-$plan ? (plan tests => $plan) : (plan skip_all => "no tests to run");
+use File::Spec;
+use IPC::Open3;
+use IO::Handle;
+open my $stdin, '<', File::Spec->devnull or die "can't open devnull: $!";
+
+my @warnings;
+for my $lib (@module_files)
{
- # fake home for cpan-testers
- # no fake requested ## local $ENV{HOME} = tempdir( CLEANUP => 1 );
-
- like( qx{ $^X -Ilib -e "require $_; print '$_ ok'" }, qr/^\s*$_ ok/s, "$_ loaded ok" )
- for sort @modules;
-
- SKIP: {
- eval "use Test::Script 1.05; 1;";
- skip "Test::Script needed to test script compilation", scalar(@scripts) if $@;
- foreach my $file ( @scripts ) {
- my $script = $file;
- $script =~ s!.*/!!;
- script_compiles( $file, "$script script compiles" );
- }
+ # see L<perlfaq8/How can I capture STDERR from an external command?>
+ my $stderr = IO::Handle->new;
+
+ my $pid = open3($stdin, '>&STDERR', $stderr, $^X, $inc_switch, '-e', "require q[$lib]");
+ binmode $stderr, ':crlf' if $^O eq 'MSWin32';
+ my @_warnings = <$stderr>;
+ waitpid($pid, 0);
+ is($?, 0, "$lib loaded ok");
+
+ if (@_warnings)
+ {
+ warn @_warnings;
+ push @warnings, @_warnings;
}
}
+
+foreach my $file (@scripts)
+{ SKIP: {
+ open my $fh, '<', $file or warn("Unable to open $file: $!"), next;
+ my $line = <$fh>;
+
+ close $fh and skip("$file isn't perl", 1) unless $line =~ /^#!\s*(?:\S*perl\S*)((?:\s+-\w*)*)(?:\s*#.*)?$/;
+ my @flags = $1 ? split(' ', $1) : ();
+
+ my $stderr = IO::Handle->new;
+
+ my $pid = open3($stdin, '>&STDERR', $stderr, $^X, $inc_switch, @flags, '-c', $file);
+ binmode $stderr, ':crlf' if $^O eq 'MSWin32';
+ my @_warnings = <$stderr>;
+ waitpid($pid, 0);
+ is($?, 0, "$file compiled ok");
+
+ # in older perls, -c output is simply the file portion of the path being tested
+ if (@_warnings = grep { !/\bsyntax OK$/ }
+ grep { chomp; $_ ne (File::Spec->splitpath($file))[2] } @_warnings)
+ {
+ warn @_warnings;
+ push @warnings, @_warnings;
+ }
+} }
+
+
+
+is(scalar(@warnings), 0, 'no warnings found')
+ or diag 'got warnings: ', ( Test::More->can('explain') ? Test::More::explain(\@warnings) : join("\n", '', @warnings) ) if $ENV{AUTHOR_TESTING};
+
+