summaryrefslogtreecommitdiffstats
path: root/lib/Crawl/Bot/Plugin/Commit.pm
blob: 0b57b4fe0064bf3163d19c226099f1101f193df2 (plain) (blame)
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
package Crawl::Bot::Plugin::Commit;
use Moose;
use autodie;
use File::pushd;
extends 'Crawl::Bot::Plugin';

has repo_uri => (
    is      => 'ro',
    isa     => 'Str',
    default => 'git://gitorious.org/crawl/crawl.git',
);

has checkout => (
    is      => 'ro',
    isa     => 'Str',
    lazy    => 1,
    default => sub {
        my $self = shift;
        my $checkout = File::Spec->catdir($self->data_dir, 'crawl-ref');
        mkdir $checkout unless -d $checkout;
        my $dir = pushd($checkout);
        if (!-f 'HEAD') {
            system('git clone --mirror ' . $self->repo_uri . " $checkout");
        }
        $checkout;
    },
);

has heads => (
    traits  => [qw(Hash)],
    isa     => 'HashRef[Str]',
    lazy    => 1,
    handles => {
        has_branch => 'exists',
        head       => 'accessor',
    },
    default => sub {
        my $self = shift;
        my $dir = pushd($self->checkout);
        return { map { $_ => `git rev-parse $_` } $self->branches };
    },
);

sub tick {
    my $self = shift;
    my $dir = pushd($self->checkout);
    system('git fetch');
    for my $branch ($self->branches) {
        my $old_head = $self->head($branch) || '';
        my $head = `git rev-parse $branch`;
        chomp ($old_head, $head);
        next if $old_head eq $head;

        if (!$self->has_branch($branch)) {
            $self->say_all("New branch created: $branch");
        }

        my @revs = split /\n/, `git rev-list $old_head..$head`;
        my %commits = map { $_, $self->parse_commit($_) } @revs;

        my $cherry_picks = @revs;
        @revs = grep { $commits{$_}->{subject} !~ /\(cherry picked from /
                    && $commits{$_}->{body}    !~ /\(cherry picked from / } @revs;
        $cherry_picks -= @revs;
        $self->say_all("Cherry-picked $cherry_picks commits into $branch")
            if $cherry_picks > 0;

        for my $rev (@revs) {
            my $commit = $commits{$rev};
            my $abbr = substr($rev, 0, 12);
            $self->say_all("$commit->{author} * r$abbr ($commit->{nfiles} changed): $commit->{subject}");
        }

        $self->head($branch => $head);
    }
}

sub branches {
    my $self = shift;
    my $dir = pushd($self->checkout);
    return map { s/^[ \*]*//; $_ } split /\n/, `git branch`;
}

sub parse_commit {
    my $self = shift;
    my ($rev) = @_;
    my $dir = pushd($self->checkout);
    my $info = `git log -1 --pretty=format:%aN%x00%s%x00%b%x00 $rev`;
    $info =~ /(.*?)\x00(.*?)\x00(.*?)\x00(.*)/s;
    my ($author, $subject, $body, $stat) = ($1, $2, $3, $4);
    $stat =~ s/(\d+) files changed/$1/;
    return {
        author  => $author,
        subject => $subject,
        body    => $body,
        nfiles  => $stat,
    };
}

__PACKAGE__->meta->make_immutable;
no Moose;

1;