From b01b50858cac10ff8cd9722fd5be9bcaf314b83c Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 15 Jan 2016 03:00:13 +0000 Subject: repobrowse: implement preliminary summary page This should provide a decent landing page for projects. Alternative README files may be configured with the per-repo "readme" directive. --- lib/PublicInbox/Repobrowse.pm | 46 ++++++++------ lib/PublicInbox/RepobrowseConfig.pm | 2 +- lib/PublicInbox/RepobrowseGitSummary.pm | 102 ++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 20 deletions(-) create mode 100644 lib/PublicInbox/RepobrowseGitSummary.pm (limited to 'lib') diff --git a/lib/PublicInbox/Repobrowse.pm b/lib/PublicInbox/Repobrowse.pm index 75dee72f..9e97593b 100644 --- a/lib/PublicInbox/Repobrowse.pm +++ b/lib/PublicInbox/Repobrowse.pm @@ -63,33 +63,41 @@ sub run { }; my $cmd = shift @extra; + my $vcs_lc = $repo_info->{vcs}; + my $vcs = $VCS{$vcs_lc} or return r404(); + my $mod; if (defined $cmd && length $cmd) { - my $vcs_lc = $repo_info->{vcs}; - my $vcs = $VCS{$vcs_lc} or return r404(); - my $mod = $CMD{$cmd}; + $mod = $CMD{$cmd}; unless ($mod) { unshift @extra, $cmd; $mod = 'Fallback'; } - $mod = load_once("PublicInbox::Repobrowse$vcs$mod"); - $vcs = load_once("PublicInbox::$vcs"); - $repo_info->{$vcs_lc} ||= $vcs->new($repo_info->{path}); $req->{relcmd} = '../' x scalar(@extra); - while (@extra && $extra[-1] eq '') { - pop @extra; - ++$req->{tslash}; - } - $req->{expath} = join('/', @extra); - my $rv = eval { $mod->new->call($cmd, $req) }; - $rv || r404(); } else { - $req->{relcmd} = defined $cmd ? '' : './'; - summary($req); + $mod = 'Summary'; + $cmd = 'summary'; + if ($path_info =~ m!/\z!) { + $req->{tslash} = $path_info =~ tr!/!!; + $req->{relcmd} = ''; + } else { + my @repo = split('/', $repo_path); + if (@repo > 1) { + $req->{relcmd} = "./$repo[-1]/"; + } else { + $req->{relcmd} = "/$repo[-1]/"; + } + } } -} - -sub summary { - r404(); + $mod = load_once("PublicInbox::Repobrowse$vcs$mod"); + $vcs = load_once("PublicInbox::$vcs"); + $repo_info->{$vcs_lc} ||= $vcs->new($repo_info->{path}); + while (@extra && $extra[-1] eq '') { + pop @extra; + ++$req->{tslash}; + } + $req->{expath} = join('/', @extra); + my $rv = eval { $mod->new->call($cmd, $req) }; # RepobrowseBase::call + $rv || r404(); } sub r404 { r(404, 'Not Found') } diff --git a/lib/PublicInbox/RepobrowseConfig.pm b/lib/PublicInbox/RepobrowseConfig.pm index 2f780b65..b643f7dc 100644 --- a/lib/PublicInbox/RepobrowseConfig.pm +++ b/lib/PublicInbox/RepobrowseConfig.pm @@ -45,7 +45,7 @@ sub lookup { $rv->{desc_html} = PublicInbox::Hval->new_oneline($rv->{description})->as_html; - foreach my $key (qw(publicinbox vcs)) { + foreach my $key (qw(publicinbox vcs readme)) { $rv->{$key} = $self->{"repo.$repo_path.$key"}; } diff --git a/lib/PublicInbox/RepobrowseGitSummary.pm b/lib/PublicInbox/RepobrowseGitSummary.pm new file mode 100644 index 00000000..65e32b6a --- /dev/null +++ b/lib/PublicInbox/RepobrowseGitSummary.pm @@ -0,0 +1,102 @@ +# Copyright (C) 2016 all contributors +# License: AGPL-3.0+ + +# The main summary/landing page of a git repository viewer +package PublicInbox::RepobrowseGitSummary; +use strict; +use warnings; +use PublicInbox::Hval qw(utf8_html); +use base qw(PublicInbox::RepobrowseBase); + +sub call_git_summary { + my ($self, $req) = @_; + sub { + my ($res) = @_; # Plack streaming callback + emit_summary($self, $req, $res); + } +} + +use constant EACH_REF_FMT => '--format=' . + join(' ', map { "%($_)" } + qw(refname objecttype objectname creatordate:short subject)); + +sub emit_summary { + my ($self, $req, $res) = @_; + my $repo_info = $req->{repo_info}; + my $git = $repo_info->{git}; + my $count = 10; # TODO: configurable + my $fh; + + # n.b. we would use %(HEAD) in for-each-ref --format if we could + # rely on git 1.9.0+, but it's too soon for that in early 2016... + chomp(my $head_ref = $git->qx(qw(rev-parse HEAD~0))); + + my $refs = $git->popen(qw(for-each-ref --sort=-creatordate), + EACH_REF_FMT, "--count=$count", + qw(refs/heads/ refs/tags/)); + $fh = $res->([200, ['Content-Type'=>'text/html; charset=UTF-8']]); + # ref names are unpredictable in length and requires tables :< + $fh->write($self->html_start($req, + "$repo_info->{path_info}: overview") . + ''); + + my $rel = $req->{relcmd}; + foreach (<$refs>) { + my ($ref, $type, $hex, $date, $s) = split(' ', $_, 5); + $ref =~ s!\Arefs/(?:heads|tags)/!!; + $ref = PublicInbox::Hval->utf8($ref); + my $h = $ref->as_html; + $ref = $ref->as_href; + my $sref; + if ($type eq 'tag') { + $h = "$h"; + $sref = $ref = $rel . 'tag?h=' . $ref; + } elsif ($type eq 'commit') { + $sref = $rel . 'commit?h=' . $ref; + $ref = $rel . 'log?h=' . $ref; + } else { + # no point in wasting code to support tagged + # trees/blobs... + next; + } + chomp $s; + my $x = $hex eq $head_ref ? ' (HEAD)' : ''; + $fh->write(qq() . + qq('); + + } + $fh->write('
$h$x$date ) . utf8_html($s) . + '
'); + + # some people will use README.md or even README.sh here... + my $readme = $repo_info->{readme}; + defined $readme or $readme = 'README'; + my $doc = $git->cat_file('HEAD:'.$readme); + if (defined $doc) { + $fh->write('
' .
+			readme_path_links($rel, $readme) . " (HEAD)\n\n");
+		$fh->write(utf8_html($$doc));
+		$fh->write('
'); + } + $fh->write(''); + $fh->close; +} + +sub readme_path_links { + my ($rel, $readme) = @_; + my @path = split(m!/+!, $readme); + + my $s = "tree root/"; + my @t; + $s .= join('/', (map { + push @t, $_; + my $e = PublicInbox::Hval->utf8($_, join('/', @t)); + my $ep = $e->as_path; + my $eh = $e->as_html; + $e = "$eh"; + # bold the last one + scalar(@t) == scalar(@path) ? "$e" : $e; + } @path)); +} + +1; -- cgit v1.2.3-24-ge0c7