github.com/shravanasati/hydra@v1.0.1-0.20240122045627-1082d2ed50d2/hydra/hooks/fsmonitor-watchman.sample (about)

     1  #!/usr/bin/perl
     2  
     3  use strict;
     4  use warnings;
     5  use IPC::Open2;
     6  
     7  # An example hook script to integrate Watchman
     8  # (https://facebook.github.io/watchman/) with git to speed up detecting
     9  # new and modified files.
    10  #
    11  # The hook is passed a version (currently 2) and last update token
    12  # formatted as a string and outputs to stdout a new update token and
    13  # all files that have been modified since the update token. Paths must
    14  # be relative to the root of the working tree and separated by a single NUL.
    15  #
    16  # To enable this hook, rename this file to "query-watchman" and set
    17  # 'git config core.fsmonitor .git/hooks/query-watchman'
    18  #
    19  my ($version, $last_update_token) = @ARGV;
    20  
    21  # Uncomment for debugging
    22  # print STDERR "$0 $version $last_update_token\n";
    23  
    24  # Check the hook interface version
    25  if ($version ne 2) {
    26  	die "Unsupported query-fsmonitor hook version '$version'.\n" .
    27  	    "Falling back to scanning...\n";
    28  }
    29  
    30  my $git_work_tree = get_working_dir();
    31  
    32  my $retry = 1;
    33  
    34  my $json_pkg;
    35  eval {
    36  	require JSON::XS;
    37  	$json_pkg = "JSON::XS";
    38  	1;
    39  } or do {
    40  	require JSON::PP;
    41  	$json_pkg = "JSON::PP";
    42  };
    43  
    44  launch_watchman();
    45  
    46  sub launch_watchman {
    47  	my $o = watchman_query();
    48  	if (is_work_tree_watched($o)) {
    49  		output_result($o->{clock}, @{$o->{files}});
    50  	}
    51  }
    52  
    53  sub output_result {
    54  	my ($clockid, @files) = @_;
    55  
    56  	# Uncomment for debugging watchman output
    57  	# open (my $fh, ">", ".git/watchman-output.out");
    58  	# binmode $fh, ":utf8";
    59  	# print $fh "$clockid\n@files\n";
    60  	# close $fh;
    61  
    62  	binmode STDOUT, ":utf8";
    63  	print $clockid;
    64  	print "\0";
    65  	local $, = "\0";
    66  	print @files;
    67  }
    68  
    69  sub watchman_clock {
    70  	my $response = qx/watchman clock "$git_work_tree"/;
    71  	die "Failed to get clock id on '$git_work_tree'.\n" .
    72  		"Falling back to scanning...\n" if $? != 0;
    73  
    74  	return $json_pkg->new->utf8->decode($response);
    75  }
    76  
    77  sub watchman_query {
    78  	my $pid = open2(\*CHLD_OUT, \*CHLD_IN, 'watchman -j --no-pretty')
    79  	or die "open2() failed: $!\n" .
    80  	"Falling back to scanning...\n";
    81  
    82  	# In the query expression below we're asking for names of files that
    83  	# changed since $last_update_token but not from the .git folder.
    84  	#
    85  	# To accomplish this, we're using the "since" generator to use the
    86  	# recency index to select candidate nodes and "fields" to limit the
    87  	# output to file names only. Then we're using the "expression" term to
    88  	# further constrain the results.
    89  	if (substr($last_update_token, 0, 1) eq "c") {
    90  		$last_update_token = "\"$last_update_token\"";
    91  	}
    92  	my $query = <<"	END";
    93  		["query", "$git_work_tree", {
    94  			"since": $last_update_token,
    95  			"fields": ["name"],
    96  			"expression": ["not", ["dirname", ".git"]]
    97  		}]
    98  	END
    99  
   100  	# Uncomment for debugging the watchman query
   101  	# open (my $fh, ">", ".git/watchman-query.json");
   102  	# print $fh $query;
   103  	# close $fh;
   104  
   105  	print CHLD_IN $query;
   106  	close CHLD_IN;
   107  	my $response = do {local $/; <CHLD_OUT>};
   108  
   109  	# Uncomment for debugging the watch response
   110  	# open ($fh, ">", ".git/watchman-response.json");
   111  	# print $fh $response;
   112  	# close $fh;
   113  
   114  	die "Watchman: command returned no output.\n" .
   115  	"Falling back to scanning...\n" if $response eq "";
   116  	die "Watchman: command returned invalid output: $response\n" .
   117  	"Falling back to scanning...\n" unless $response =~ /^\{/;
   118  
   119  	return $json_pkg->new->utf8->decode($response);
   120  }
   121  
   122  sub is_work_tree_watched {
   123  	my ($output) = @_;
   124  	my $error = $output->{error};
   125  	if ($retry > 0 and $error and $error =~ m/unable to resolve root .* directory (.*) is not watched/) {
   126  		$retry--;
   127  		my $response = qx/watchman watch "$git_work_tree"/;
   128  		die "Failed to make watchman watch '$git_work_tree'.\n" .
   129  		    "Falling back to scanning...\n" if $? != 0;
   130  		$output = $json_pkg->new->utf8->decode($response);
   131  		$error = $output->{error};
   132  		die "Watchman: $error.\n" .
   133  		"Falling back to scanning...\n" if $error;
   134  
   135  		# Uncomment for debugging watchman output
   136  		# open (my $fh, ">", ".git/watchman-output.out");
   137  		# close $fh;
   138  
   139  		# Watchman will always return all files on the first query so
   140  		# return the fast "everything is dirty" flag to git and do the
   141  		# Watchman query just to get it over with now so we won't pay
   142  		# the cost in git to look up each individual file.
   143  		my $o = watchman_clock();
   144  		$error = $output->{error};
   145  
   146  		die "Watchman: $error.\n" .
   147  		"Falling back to scanning...\n" if $error;
   148  
   149  		output_result($o->{clock}, ("/"));
   150  		$last_update_token = $o->{clock};
   151  
   152  		eval { launch_watchman() };
   153  		return 0;
   154  	}
   155  
   156  	die "Watchman: $error.\n" .
   157  	"Falling back to scanning...\n" if $error;
   158  
   159  	return 1;
   160  }
   161  
   162  sub get_working_dir {
   163  	my $working_dir;
   164  	if ($^O =~ 'msys' || $^O =~ 'cygwin') {
   165  		$working_dir = Win32::GetCwd();
   166  		$working_dir =~ tr/\\/\//;
   167  	} else {
   168  		require Cwd;
   169  		$working_dir = Cwd::cwd();
   170  	}
   171  
   172  	return $working_dir;
   173  }