File Coverage

blib/lib/Git/Fingerd.pm
Criterion Covered Total %
statement 11 13 84.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 16 18 88.8


line stmt bran cond sub pod time code
1 1     1   21738 use strict;
  1         1  
  1         35  
2 1     1   4 use warnings;
  1         2  
  1         51  
3             package Git::Fingerd;
4             {
5             $Git::Fingerd::VERSION = '2.093521';
6             }
7 1     1   910 use Net::Finger::Server 0.003;
  1         17926  
  1         8  
8 1     1   262 BEGIN { our @ISA = qw(Net::Finger::Server); }
9             # ABSTRACT: let people finger your git server for... some reason
10              
11 1     1   1566 use Git::PurePerl;
  0            
  0            
12             use List::Util qw(max);
13             use Path::Class;
14             use SUPER;
15             use String::Truncate qw(elide);
16             use Text::Table;
17              
18              
19             sub new {
20             my ($class, %config) = @_;
21              
22             my $basedir = delete $config{basedir} || Carp::croak('no basedir supplied');
23             my $self = $class->SUPER(%config, log_level => 0);
24             $self->{__PACKAGE__}{basedir} = $basedir;
25              
26             return $self;
27             }
28              
29             sub basedir { $_[0]->{__PACKAGE__}{basedir} }
30              
31             sub username_regex { qr{[-a-z0-9]+}i }
32              
33             sub listing_reply {
34             my $basedir = $_[0]->basedir;
35             my @dirs = sort <$basedir/*>;
36              
37             my $table = Text::Table->new('Repository', ' Description');
38              
39             my %repo;
40              
41             for my $i (reverse 0 .. $#dirs) {
42             my $dir = $dirs[$i];
43             my $mode = (stat $dir)[2];
44             unless ($mode & 1) {
45             splice @dirs, $i, 1;
46             next;
47             }
48              
49             my $repo = $dir;
50             s{\A$basedir/}{}, s{\.git\z}{} for $repo;
51             my $desc = `cat $dir/description`;
52             chomp $desc;
53              
54             $repo{ $repo } = $desc;
55             }
56              
57             my $desc_len = 79 - 3 - (List::Util::max map { length } keys %repo);
58              
59             for my $repo (sort { lc $a cmp lc $b } keys %repo) {
60             $table->add($repo => ' ' . elide($repo{$repo}, $desc_len));
61             }
62              
63             return "$table";
64             }
65              
66             sub user_reply {
67             my ($self, $username, $arg) = @_;
68              
69             my $basedir = $self->basedir;
70             my $gitdir = "$basedir/$username.git";
71              
72             return "unknown repository\n" unless -d $gitdir;
73              
74             my $mode = (stat $gitdir)[2];
75              
76             return "unknown repository\n" unless $mode & 1;
77              
78             my $repo = Git::PurePerl->new({ gitdir => $gitdir });
79              
80             my $cloneurl = file( $gitdir, 'cloneurl' )->slurp( chomp => 1 );
81             my $desc = $repo->description;
82             chomp($cloneurl, $desc);
83              
84             my @refs = $repo->ref_names;
85             my @tags = grep { s{^refs/tags/}{} } @refs;
86             my @heads = grep { s{^refs/heads/}{} } @refs;
87              
88             my $reply = "Project : $username
89             Desc. : $desc
90             Clone URL: $cloneurl
91             ";
92              
93             $reply .= "\n[heads]\n";
94             for my $head (sort @heads) {
95             my $sha = $repo->ref_sha1("refs/heads/$head");
96             $reply .= sprintf "%-15s = %s\n", $head, $sha;
97             }
98              
99             $reply .= "\n[tags]\n";
100             for my $tag (sort @tags) {
101             my $sha = $repo->ref_sha1("refs/tags/$tag");
102             $reply .= sprintf "%-15s = %s\n", $tag, $sha;
103             }
104              
105             if (my $ref = $repo->ref("refs/heads/master")) {
106             my $tree = $ref->tree;
107             for ($tree->directory_entries) {
108             next unless $_->filename eq 'README';
109             my $obj = $_->object;
110             $reply .= "\n[README]\n" . $obj->content . "\n";
111             }
112             }
113              
114             return $reply;
115             }
116              
117             1;
118              
119             __END__
120              
121             =pod
122              
123             =head1 NAME
124              
125             Git::Fingerd - let people finger your git server for... some reason
126              
127             =head1 VERSION
128              
129             version 2.093521
130              
131             =head1 DESCRIPTION
132              
133             This module implements a simple C<finger> server that describes the contents of
134             a server that hosts git repositories. You can finger C<@servername> for a
135             listing of repositories and finger C<repo@servername> for information about
136             a single repository.
137              
138             This was meant to provide a simple example for Net::Finger::Server, but enough
139             people asked for the code that I've released it as something reusable. Here's
140             an example program using Git::Fingerd:
141              
142             #!/usr/bin/perl
143             use Git::Fingerd -run => {
144             isa => 'Net::Server::INET',
145             basedir => '/var/lib/git',
146             };
147              
148             This program could then run out of F<xinetd>.
149              
150             =for Pod::Coverage new basedir
151              
152             =head1 AUTHOR
153              
154             Ricardo SIGNES <rjbs@cpan.org>
155              
156             =head1 COPYRIGHT AND LICENSE
157              
158             This software is copyright (c) 2013 by Ricardo SIGNES.
159              
160             This is free software; you can redistribute it and/or modify it under
161             the same terms as the Perl 5 programming language system itself.
162              
163             =cut