Skip to content

Instantly share code, notes, and snippets.

@Ovid
Last active May 12, 2022 12:36
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Ovid/fd8ba5b758f86b02f1c5f2a0a75c88f4 to your computer and use it in GitHub Desktop.
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
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