App-GitHooks

 view release on metacpan or  search on metacpan

lib/App/GitHooks/Hook/CommitMsg.pm  view on Meta::CPAN

App::GitHooks::Hook::CommitMsg - Handle the commit-msg hook.


=head1 VERSION

Version 1.9.0

=cut

our $VERSION = '1.9.0';


=head1 METHODS

=head2 run()

Run the hook handler and return an exit status to pass to git.

	my $exit_status = App::GitHooks::Hook::CommitMsg->run(
		app => $app,
	);

Arguments:

=over 4

=item * app I<(mandatory)>

An App::GitHooks object.

=back

=cut

sub run
{
	my ( $class, %args ) = @_;
	my $app = delete( $args{'app'} );
	croak 'Unknown argument(s): ' . join( ', ', keys %args )
		if scalar( keys %args ) != 0;

	# Check parameters.
	croak "The 'app' argument is mandatory"
		if !Data::Validate::Type::is_instance( $app, class => 'App::GitHooks' );

	# Reassigns standard input back to the keyboard.
	# Note: this will silently fail in some non-interactive shells where /dev/tty
	# can't be referenced, which is fine since there will be no user typing anyway.
	my $console = $^O eq 'MSWin32'
		? 'CON:'
		: '/dev/tty';
	open( STDIN, '<', $console ); ## no critic (InputOutput::RequireCheckedOpen)

	# If the terminal isn't interactive, we won't have a human available to fix the
	# problems so we just let the commit go as is.
	my $config = $app->get_config();
	my $force_interactive = $config->get( 'testing', 'force_interactive' );
	return $HOOK_EXIT_SUCCESS
		if !$app->get_terminal()->is_interactive() && !$force_interactive;

	# Analyze the commit message and prompt the user to fix it if needed until it
	# passes the checks.
	my $has_errors = 0;
	while ( 1 )
	{
		# Retrieve the commit message.
		my $command_line_arguments = $app->get_command_line_arguments();
		my $commit_message_file = $command_line_arguments->[0];
		my $commit_message = App::GitHooks::CommitMessage->new(
			message => Path::Tiny::path( $commit_message_file )->slurp_utf8() // '',
			app     => $app,
		);

		# If the commit message is empty, don't bother running any checks - git will
		# abort the commit.
		last
			if $commit_message->is_empty();

		# Find all the tests we will need to run.
		my $plugins = $app->get_hook_plugins( $app->get_hook_name() );
		$has_errors = 0;
		foreach my $plugin ( @$plugins )
		{
			my $return_code = $plugin->run_commit_msg(
				app            => $app,
				commit_message => $commit_message,
			);
			$has_errors = 1
				if $return_code == $PLUGIN_RETURN_FAILED;
		}

		# If errors were found, let the user try to fix the commit message,
		# otherwise finish and let git complete.
		if ( $has_errors )
		{
			print "Press <Enter> to edit the commit message or Ctrl-C to abort the commit.\n";
			if ( $app->get_terminal()->is_interactive() )
			{
				my $input = <STDIN>; ## no critic (InputOutput::ProhibitExplicitStdin)

				my $editor = $ENV{'EDITOR'} // 'vim';
				system("$editor $commit_message_file");
				print "\n";
			}
			else
			{
				# $has_errors is set to 1, but we're not in interactive mode so we
				# can't wait for STDIN.
				last;
			}
		}
		else
		{
			# No errors, $has_errors is set to 0, finish.
			last;
		}
	}

	# Success.
	return $has_errors
		? $HOOK_EXIT_FAILURE



( run in 0.659 second using v1.01-cache-2.11-cpan-6aa56a78535 )