Code-TidyAll

 view release on metacpan or  search on metacpan

lib/Code/TidyAll/Git/Prereceive.pm  view on Meta::CPAN

        else {
            print STDERR "*** Error running pre-receive hook (allowing push to proceed):\n$error";
        }
    };
    die "$fail_msg\n" if $fail_msg;
}

sub check_input {
    my ( $self, $input ) = @_;

    my @lines = split( "\n", $input );
    my ( @results, $tidyall );
    foreach my $line (@lines) {
        chomp($line);
        my ( $base, $commit, $ref ) = split( /\s+/, $line );

        # Create tidyall using configuration found in first commit
        #
        $tidyall ||= $self->create_tidyall($commit);

        my @files = $self->get_changed_files( $base, $commit );
        foreach my $file (@files) {
            my $contents = $self->get_file_contents( $file, $commit );
            if ( $contents =~ /\S/ && $contents =~ /\n/ ) {
                push( @results, $tidyall->process_source( $contents, $file ) );
            }
        }
    }

    my $fail_msg;
    if ( my @error_results = grep { $_->error } @results ) {
        unless ( $self->check_repeated_push($input) ) {
            my $error_count = scalar(@error_results);
            $fail_msg = sprintf(
                '%d file%s did not pass tidyall check',
                $error_count, $error_count > 1 ? 's' : q{}
            );
        }
    }
    return $fail_msg;
}

sub create_tidyall {
    my ( $self, $commit ) = @_;

    my $temp_dir    = tempdir_simple();
    my @conf_names  = $self->conf_name ? ( $self->conf_name ) : Code::TidyAll->default_conf_names;
    my ($conf_file) = grep { $self->get_file_contents( $_, $commit ) } @conf_names
        or die sprintf( 'could not find conf file %s', join( ' or ', @conf_names ) );
    foreach my $rel_file ( $conf_file, @{ $self->extra_conf_files } ) {
        my $contents = $self->get_file_contents( $rel_file, $commit )
            or die sprintf( q{could not find file '%s' in repo root}, $rel_file );
        $temp_dir->child($rel_file)->spew($contents);
    }
    my $tidyall = $self->tidyall_class->new_from_conf_file(
        "$temp_dir/" . $conf_file,
        mode  => 'commit',
        quiet => 1,
        %{ $self->tidyall_options },
        no_cache   => 1,
        no_backups => 1,
        check_only => 1,
    );
    return $tidyall;
}

sub get_changed_files {
    my ( $self, $base, $commit ) = @_;
    my $output = capturex( $self->git_path, 'diff', '--numstat', '--name-only', "$base..$commit" );
    my @files  = grep {/\S/} split( "\n", $output );
    return @files;
}

sub get_file_contents {
    my ( $self, $file, $commit ) = @_;
    my ( $contents, $error ) = capture { system( $self->git_path, 'show', "$commit:$file" ) };
    return $contents;
}

sub check_repeated_push {
    my ( $self, $input ) = @_;

    my $allow = $self->allow_repeated_push;
    return 0 unless defined $allow;

    my $cwd            = path($0)->realpath->parent;
    my $last_push_file = $cwd->child('.prereceive_lastpush');
    if ( -w $cwd || -w $last_push_file ) {
        my $push_sig = sha1_hex($input);
        if ( -f $last_push_file ) {
            my ( $last_push_sig, $count ) = split( /\s+/, $last_push_file->slurp );
            if ( $last_push_sig eq $push_sig ) {
                ++$count;
                print STDERR "*** Identical push seen $count times\n";
                if ( $count >= $allow ) {
                    print STDERR "*** Allowing push to proceed despite errors\n";
                    unlink($last_push_file);
                    return 1;
                }
                $last_push_file->spew( join( q{ }, $push_sig, $count ) );
                return 0;
            }
        }
        $last_push_file->spew( join( q{ }, $push_sig, 1 ) );
    }

    return 0;
}

1;

# ABSTRACT: Git pre-receive hook that requires files to be tidyall'd

__END__

=pod

=encoding UTF-8

=head1 NAME



( run in 2.382 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )