App-KGB
view release on metacpan or search on metacpan
script/kgb-bot view on Meta::CPAN
contents.
The actual processing and IRC notification is done via an appropriate
C<gitlab_webhook_*> event, asynchronously.
=cut
my @array_webhook_params =
qw( channel pipeline_only_status always_squash_branch only_branch
only_tag always_squash_outside_dir );
my %array_webhook_params = map( ( $_ => 1 ), @array_webhook_params );
sub webhook_request {
my ( $kernel, $request, $response, $dirmatch ) = @_[ KERNEL, ARG0 .. ARG2 ];
unless ( $KGB::config->{webhook}{enabled} ) {
return webhook_error(@_, 'WebHook support not enabled',
HTTP::Status::HTTP_PRECONDITION_FAILED);
}
my $client_ip = $response->connection->remote_ip;
# fix ipv4-as-ipv6 represenation
$client_ip =~ s/^::ffff:(\d+\.\d+\.\d+\.\d+)$/$1/;
$client_ip = Net::IP->new($client_ip);
my $allowed = 0;
my $allowed_nets = $KGB::config->{webhook}{allowed_networks};
if ($allowed_nets) {
for my $net (@$allowed_nets) {
next unless $net->version == $client_ip->version;
$allowed = 1, last
if $net->overlaps($client_ip) != $Net::IP::IP_NO_OVERLAP;
}
}
unless ($allowed) {
return webhook_error(@_, 'Client IP ' . $client_ip->ip . ' is not whitelisted',
HTTP::Status::HTTP_FORBIDDEN);
}
KGB->debug( "got a webhook request from " . $client_ip->ip );
unless( $request->method eq 'POST' ) {
return webhook_error(@_, 'Request method must be "POST"');
}
KGB->debug('DEBUG: method check passed');
my $json;
my $ok = eval {
$json = JSON::from_json( $request->content, { utf8 => 1 } );
1;
};
unless ($ok) {
return webhook_error(@_, 'Error decoding JSON body', undef, $@);
}
KGB->debug('Body decoded');
unless (
$json
and ref($json)
and ref($json) eq 'HASH'
and ( $json->{project}
and defined $json->{project}{name}
and length $json->{project}{name}
or defined $json->{project_name} and length $json->{project_name} )
and defined $json->{object_kind}
and length $json->{object_kind}
)
{
return webhook_error(@_, 'Empty on invalid JSON body');
}
my $module = $json->{project}{name};
my $hook_handler;
if ( $json->{object_kind} eq 'push' ) {
$hook_handler = 'gitlab_webhook_push';
}
elsif ( $json->{object_kind} eq 'tag_push' ) {
$hook_handler = 'gitlab_webhook_tag_push';
}
elsif ( $json->{object_kind} eq 'wiki_page' ) {
$hook_handler = 'gitlab_webhook_wiki_page';
}
elsif ( $json->{object_kind} eq 'issue' ) {
$hook_handler = 'gitlab_webhook_issue';
}
elsif ( $json->{object_kind} eq 'note' ) {
$hook_handler = 'gitlab_webhook_note';
}
elsif ( $json->{object_kind} eq 'merge_request' ) {
$hook_handler = 'gitlab_webhook_merge_request';
}
elsif ( $json->{object_kind} eq 'pipeline' ) {
$hook_handler = 'gitlab_webhook_pipeline';
}
elsif ( $json->{object_kind} eq 'build' ) {
$hook_handler = 'gitlab_webhook_build';
}
else {
return webhook_error(@_,
"Unsupported object_kind: $json->{object_kind}");
}
KGB->debug('Body check passed');
# form a CGI-like param hash
my $param = { map( ( $_ => [] ), @array_webhook_params ) };
$param->{always_squash_branch} = [qw( upstream upstream/master )];
$param->{shorten_urls} = 1;
$param->{use_irc_notices} = 1;
$param->{network} = 'oftc';
my $uri = URI->new( $request->url );
my @query = $request->uri->query_form;
while ( my( $key, $value ) = splice( @query, 0, 2 ) ) {
if ( $array_webhook_params{$key} ) {
script/kgb-bot view on Meta::CPAN
# resending to clients because of prefix.
# Let's trim on 400, to be safe
my $MAGIC_MAX_LINE = ( 400 - length("PRIVMSG ")
- max( map( length($KGB::config->{chanidx}{$_}{name}), @$chanids ) ) );
my @tmp;
while ( $_ = shift @strings ) {
if ( length($_) > $MAGIC_MAX_LINE ) {
push @tmp, substr( $_, 0, $MAGIC_MAX_LINE );
unshift @strings, substr( $_, $MAGIC_MAX_LINE );
}
else {
push @tmp, $_;
}
}
return @tmp;
}
sub webhook_to_irc {
my ( $p ) = @_;
my @chanids = @{ $p->{chanids} };
my @strings = trim_lines(@chanids, $p->{repository}, @{ $p->{strings} });
foreach my $chanid (@chanids) {
if ( $KGB::simulate ) {
my $fh = IO::File->new(">> $KGB::simulate")
or die "Error opening $KGB::simulate for writing: $!\n";
$fh->autoflush(1);
$fh->binmode(':utf8');
for (@strings) {
$fh->print("$chanid $_\n");
}
$fh->close;
}
else {
my ( $net, $chan ) = KGB->get_net_chan($chanid);
my $method = $p->{opts}{use_irc_notices} ? 'notice' : 'privmsg';
if ( not exists $KGB::joining_channels{$chanid} )
{
$p->{kernel}->yield(
irc_notify => $net, $chan, \@strings, $method
);
}
else {
KGB->out(
"Delaying a message to $chanid for after it is joined."
);
my $stash = { message => \@strings, method => $method };
push (@{ $KGB::joining_channels{$chanid}{pending_messages} },
$stash);
}
}
}
}
=head2 gitlab_webhook_push
Handle a gitlab webhook call for the C<push> event (branch update).
Expects the body of the POST request (decoded, as a hash reference) in I<ARG0>
and all the request parameters in I<ARG1>.
The request is expected to conform to the GitLab webhook documentation at
L<https://salsa.debian.org/help/user/project/integrations/webhooks.md#push-events>.
The request parameters should look like the result of the CGI's param() method.
Supported parameters (?param=value¶m=value2...)
=over
=item channel
The name of the channel to post notifications to. Leading hash sign is optional
and should be URL-encoded if present (%23).
=item network
The name of the IRC network, servicing the channel. Supported networks are
configured by the bot's admin.
=item private
A boolean flag, indicating that the notifications shouldn't also be posted to
the C<#commits> channel on Freenode.
=item use_color
A boolean flag enabling colors. Defaults to true.
=item rev_prefix
Optional text to prepend to the commit ID.
=item use_irc_notices
If true, IRC notification uses C<notice> messages, instead of C<privmsg>.
Defaults to C<1>.
C<notice> messages are usually less intrusive.
=item squash_threshold I<number>
For I<push> events, limit the commit notifications to the given I<number>. If a
branch update contains more commits, the usual notifications are replaced by a
single notification about the number of the pushed commits.
B<Default>: 20
=back
=cut
sub gitlab_webhook_push {
my ( $kernel, $chanids, $body, $opts ) = @_[ KERNEL, ARG0 .. ARG2 ];
my $module = $body->{project}{name};
KGB->debug("Handling webhook push request for project $module");
( run in 2.256 seconds using v1.01-cache-2.11-cpan-600a1bdf6e4 )