summaryrefslogtreecommitdiff
path: root/git-hooks/gerrit-bot-extras
blob: 5b8dd5a75bea7558ed43fe12a5b14af5663d1945 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#! /usr/bin/perl

# Copyright (C) 2017 The Qt Company Ltd.
# Contact: http://www.qt.io/licensing/
#
# You may use this file under the terms of the 3-clause BSD license.
# See the file LICENSE from this package for details.
#

use strict;
use warnings;
use JSON;

# Usage: $0 <project> <SHA1> [instance]
# - default instance is 'sanitybot'
# - configure git: git config --global <instance>.<option> <value>
# Valid options are:
#   watches (optional)
#     Space-separated list of path watches. Each watch requires an own
#     section named watches.<name> with the following keys (note that
#     these sections are NOT namespaced with the instance):
#       projects (default ".*")
#         Regular expression specifying the projects to watch.
#       files
#         Regular expression specifying the filepaths to watch.
#       message (optional)
#         The message to post when this watch triggers.
#       invite (optional)
#         Space-separated list of reviewers to add when this watch triggers.

die "usage: $0 <project> <SHA1> [instance]\n" if ($#ARGV < 1);
my $project = shift @ARGV;
my $rev = shift @ARGV;

my $instance = 'sanitybot';
$instance = $ARGV[0] if ($#ARGV > -1);

# Doing this is less expensive than calling git repeatedly.
my %config = ();
for (`git config -l`) {
    /^([^=]+)=(.*$)/;
    $config{$1} = $2;
}

sub getcfg($;$)
{
    my ($key, $def) = @_;
    my $fkey = $instance.'.'.$key;
    if (defined $config{$fkey}) {
        return $config{$fkey};
    } elsif (@_ > 1) {
        return $def;
    } else {
        die $fkey." not set.\n";
    }
}

my @messages = ();
my %verdict = ();
my @invite = ();

my (@watches, %watch_files, %watch_messages, %watch_invites);
for my $w (split(/\s+/, getcfg('watches', ""))) {
    my $p = $config{'watches.'.$w.'.projects'};
    next if (defined($p) && $project !~ qr/^$p$/);
    my $f = $config{'watches.'.$w.'.files'};
    die "watches.$w.files not set.\n" if (!defined($f));
    $watch_files{$w} = qr/^$f$/m;
    $watch_messages{$w} = $config{'watches.'.$w.'.message'};
    my $i = $config{'watches.'.$w.'.invite'};
    $watch_invites{$w} = defined($i) ? [ split(/\s+/, $i) ] : [];
    push @watches, $w;
}

my ($subject, $files);
{
    local $/;
    my $output = `git show --pretty=\%s\%x00 --name-only --ignore-submodules -C $rev`;
    ($subject, $files) = split(/\x00/, $output);
}

for my $w (@watches) {
    if ($files =~ /$watch_files{$w}/) {
        push @messages, $watch_messages{$w} if (defined($watch_messages{$w}));
        push @invite, @{$watch_invites{$w}};
        last;
    }
}

my $cr = 0;
if ($subject =~ /^[<[]?[Ww]ip\b|\bWIP\b|\*{3}|^(.)\1*$/) {
    unshift @messages, "Apparently pushing a Work In Progress";
    $cr = -2;
} elsif ($subject =~ /^(?:squash|fixup)! /) {
    unshift @messages, "Commit was meant to be squashed";
    $cr = -2;
}
$verdict{labels}{'Code-Review'} = $cr;

$verdict{message} = join("\n\n", @messages) if (@messages);
$verdict{invite} = \@invite if (@invite);

print encode_json(\%verdict)."\n";