Last active
May 12, 2022 12:36
-
-
Save Ovid/fd8ba5b758f86b02f1c5f2a0a75c88f4 to your computer and use it in GitHub Desktop.
A small hack to add a 'sqitch grep ...' command that sorts things in the order of the sqitch plan
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package App::Sqitch::Command::grep; | |
use 5.010; | |
use strict; | |
use warnings; | |
use utf8; | |
use App::Sqitch::X qw(hurl); | |
use Moo; | |
use App::Sqitch::Target; | |
use App::Sqitch::Types qw(Str Enum Target Bool); | |
use File::Find::Rule; | |
use File::Basename qw(fileparse); | |
use File::Spec::Functions qw(catdir splitdir); | |
use namespace::autoclean; | |
extends 'App::Sqitch::Command'; | |
our $VERSION = 'v1.1.0'; # VERSION | |
has [qw/type t/] => ( | |
is => 'ro', | |
isa => Enum [qw( deploy verify revert )], | |
); | |
has [qw/insensitive i/] => ( | |
is => 'ro', | |
isa => Bool, | |
); | |
has [qw/list l/] => ( | |
is => 'ro', | |
isa => Bool, | |
); | |
has target => ( | |
is => 'ro', | |
isa => Target, | |
lazy => 1, | |
default => sub { return App::Sqitch::Target->new(sqitch => shift->sqitch) }, | |
); | |
sub options { | |
my $self = shift; | |
return $self->SUPER::options(@_), qw( | |
t|type=s | |
i|insensitive | |
l|list | |
); | |
} | |
sub execute { | |
my ($self, @args) = @_; | |
unless (@args) { | |
die "No search terms supplied for 'sqitch grep'"; | |
} | |
my $target = $self->target; | |
# get our change names indexed by the order they appear in the plan | |
my $i = 0; | |
my %order_by = map { $_->name => $i++ } grep { $_->isa('App::Sqitch::Plan::Change') } $target->plan->lines; | |
my ($deploy_dir, $verify_dir, $revert_dir) = map { $target->$_ } qw/deploy_dir verify_dir revert_dir/; | |
my $type = $self->type // $self->t // ''; | |
my $search_dir = | |
'deploy' eq $type ? $deploy_dir | |
: 'verify' eq $type ? $verify_dir | |
: 'revert' eq $type ? $revert_dir | |
: $target->top_dir; | |
my @files = $self->get_files($search_dir, @args); | |
my $extension = $target->extension; | |
my %name_for; | |
# sort files by the order in which the names show up in the plan | |
my $by_plan = sub { | |
return 0 if $a eq $b; # shouldn't happen? | |
# if a and b are not exact matches, always return in the order of | |
# deploy, verify, revert | |
return -1 if $a =~ /^$deploy_dir/ and $b !~ /^$deploy_dir/; | |
return -1 if $a =~ /^$verify_dir/ and $b =~ /^$revert_dir/; | |
return 1 if $b =~ /^$deploy_dir/ and $a !~ /^$deploy_dir/; | |
return 1 if $b =~ /^$verify_dir/ and $a =~ /^$revert_dir/; | |
# ok, we got to here. Their top-level directory is the same, so let's | |
# figure out the sort order | |
my $dir; | |
foreach ($deploy_dir, $verify_dir, $revert_dir) { | |
$dir = $_ if $a =~ /^$_/; | |
} | |
unless ($dir) { | |
# we have no idea what this file is (probably junk), so sort it | |
# last | |
return 1; | |
} | |
my @remove = splitdir($dir); | |
foreach my $this_file ($a, $b) { | |
# this takes some time, so cache these puppies | |
unless (exists $name_for{$this_file}) { | |
my ($name, $path, undef) = fileparse($this_file, $extension); | |
my @path = splitdir(catdir($path, $name)); | |
my $last_index = -1 * (@path - scalar @remove); | |
my $this_name = catdir(splice @path, $last_index); # strip leading dir | |
$this_name =~ s/\.$//; # remove trailing dot | |
$name_for{$this_file} = $this_name; | |
} | |
} | |
# all of these *should* exist, but sometimes sqitch directories can have | |
# "old" sqitch files which didn't make it into the plan. | |
# Or, um, there's a bug in my code. | |
return ($order_by{$name_for{$a}} // $i) <=> ($order_by{$name_for{$b}} // $i); | |
}; | |
@files = sort $by_plan @files; | |
if ($self->list || $self->l) { | |
print join "\n" => @files; | |
} | |
else { | |
$self->show_matches(\@files, @args); | |
} | |
} ## end sub execute | |
sub show_matches { | |
my ($self, $files, @args) = @_; | |
my $regex = ($self->insensitive || $self->i) ? qr/@args/i : qr/@args/; | |
FILE: foreach my $file (@$files) { | |
if (open my $fh, '<', $file) { | |
while (my $line = <$fh>) { | |
if ($line =~ /$regex/) { | |
print sprintf "%s:%d: %s" => $file, $., $line; | |
} | |
} | |
} | |
else { | |
warn "Could not search '$file': $!"; | |
} | |
} | |
} | |
1; | |
sub get_files { | |
my ($self, $search_dir, @args) = @_; | |
my $target = $self->target; | |
my $extension = $target->extension; | |
my $rule = File::Find::Rule->file->name("*.$extension"); | |
$rule->grep(($self->insensitive || $self->i) ? qr/@args/i : qr/@args/); | |
return $rule->in($search_dir); | |
} | |
1; | |
__END__ | |
=head1 Name | |
App::Sqitch::Command::grep - Search sqitch changes | |
=head1 Synopsis | |
my $cmd = App::Sqitch::Command::grep->new(%params); | |
$cmd->execute; | |
=head1 Description | |
A very lightweight version of C<grep>, this command allows you to search for | |
files in your C<sqitch> directories, but it returns them in the order they | |
were defined in your plan (with deploy, verify, and revert directories being | |
sorted in that order). | |
I find that regular grep can't sort things according to the plan, so sometimes | |
it's hard for me to find the specific changes I'm looking for. | |
=head1 Command Line Options | |
All options are optional | |
--type -t deploy/verify/revert Which sqitch change type to search | |
--list -l Only show filenames | |
--insensitive -i Case-insensitive search | |
Example: search all C<deploy> changes for C<ALTER TABLE>, case-insensitively: | |
sqitch grep --type deploy -i ALTER TABLE |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment