#!/usr/bin/env perl
|
|
|
|
=pod
|
|
|
|
=head1 NAME
|
|
|
|
git-feed - generate an Atom feed of git commits in the current repository
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
git-feed [ B<--project_url=...> ]
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
I keep a lot of projects in git, and I like to provide feeds for things I'm
|
|
working on. This script wraps git and XML::Atom::SimpleFeed to generate a
|
|
basic Atom feed of commits. It tries to use information already available in
|
|
the environment, but needs to be given a URL for your project (and probably for
|
|
the feed itself).
|
|
|
|
It can be configured for a given repository by doing something like the
|
|
following:
|
|
|
|
git config feed.projecturl https://p1k3.com/userland-book/
|
|
git config feed.url https://p1k3.com/userland-book/feed.xml
|
|
echo 'userland: a book about the command line for human beings' > .git/description
|
|
|
|
I have I<no idea> whether this sort of thing is considered an acceptable use of
|
|
git configuration, but it seems to work.
|
|
|
|
...or you can supply various options on the command line.
|
|
|
|
B<--project_url=...>
|
|
|
|
Set a URL for the project.
|
|
|
|
B<--feed_url=...>
|
|
|
|
Set an explicit URL for the feed itself.
|
|
|
|
B<--title=...>
|
|
|
|
Set an explicit title for the feed. Will otherwise be read from
|
|
.git/description, if available.
|
|
|
|
B<--entries=>I<n>
|
|
|
|
Explicitly request I<n> entries. Defaults to 10.
|
|
|
|
B<--author=...>
|
|
|
|
Explicitly set an author string. Defaults to git config's current idea of
|
|
user.name.
|
|
|
|
=head1 EXAMPLES
|
|
|
|
git-feed --project_url=https://p1k3.com/userland-book/ > feed.xml
|
|
|
|
=head1 INSTALLING
|
|
|
|
I'll wrap this in a module and add a build script. For now, install
|
|
XML::Atom::SimpleFeed first. I would probably do that either with:
|
|
|
|
cpanp -i XML::Atom::SimpleFeed
|
|
|
|
or:
|
|
|
|
apt-get install libxml-atom-simplefeed-perl
|
|
|
|
...depending on which one gave me less static on a given system. I don't
|
|
really know what I'm doing in the Perl ecosystem any more, though.
|
|
|
|
Anyway, if you put git-feed in your path, you can probably invoke it like any
|
|
other git subcommand:
|
|
|
|
git feed > feed.xml
|
|
|
|
=head1 AUTHOR
|
|
|
|
Brennen Bearnes <bbearnes@gmail.com>
|
|
|
|
=head1 LICENSE
|
|
|
|
This utility is free software, licensed under the terms of the GPL, v2. See
|
|
COPYING for a complete copy of the license.
|
|
|
|
=cut
|
|
|
|
# temporary hack
|
|
# use lib '/home/brennen/code/perl_libs';
|
|
|
|
use warnings;
|
|
use strict;
|
|
use 5.10.0;
|
|
use XML::Atom::SimpleFeed;
|
|
use HTML::Entities;
|
|
use Getopt::Long;
|
|
use POSIX qw(strftime);
|
|
|
|
# Turns out to be pretty much exactly what I want for system calls:
|
|
use IPC::System::Simple qw(capturex);
|
|
|
|
# Figure out where top-level .git/ resides. Naturally, "rev-parse" was the
|
|
# first command that leapt to mind when I wondered if there was a builtin way
|
|
# to ask that question. I mean, what kind of loser would have had to google
|
|
# something so _blindingly obvious_?
|
|
my $repo_base = git('rev-parse', '--show-toplevel');
|
|
chomp($repo_base);
|
|
|
|
my $title = 'git log';
|
|
$title = slurp("$repo_base/.git/description")
|
|
if (-f "$repo_base/.git/description");
|
|
|
|
my $author = git_config('user.name');
|
|
chomp($author);
|
|
|
|
my $feed_url = git_config('feed.url');
|
|
my $project_url = git_config('feed.projecturl');
|
|
my $entries = 10;
|
|
|
|
GetOptions(
|
|
"author:s" => \$author,
|
|
"entries:i" => \$entries,
|
|
"feed_url:s" => \$feed_url,
|
|
"project_url:s" => \$project_url,
|
|
"title:s" => \$title,
|
|
);
|
|
|
|
# Where does this project live on the web?
|
|
unless ($project_url) {
|
|
say "Project URL required - use one of:";
|
|
say "\tgit feed --project_url=https://yourlinkhere/";
|
|
say "\tgit config feed.projecturl https://yourlinkhere/";
|
|
exit(1);
|
|
}
|
|
|
|
# No feed link set? Make something up.
|
|
unless ($feed_url) {
|
|
$feed_url = $project_url . 'feed.xml';
|
|
}
|
|
|
|
my $log = capturex('git', 'log', "-$entries", qq{--pretty=format:%H _ %ai _ %s});
|
|
|
|
my $feed = XML::Atom::SimpleFeed->new(
|
|
title => $title,
|
|
link => $project_url,
|
|
link => { rel => 'self', href => $feed_url, },
|
|
author => $author,
|
|
id => $project_url,
|
|
generator => 'XML::Atom::SimpleFeed',
|
|
|
|
# This could be better - just uses current time, at the moment:
|
|
updated => iso_date(time()),
|
|
);
|
|
|
|
while ($log =~ m/^([a-z0-9]+) _ (.*) _ (.*)$/gm) {
|
|
my $hash = $1;
|
|
my $full_commit = git('show', $hash);
|
|
my $formatted_commit = '<pre>' . encode_entities($full_commit) . '</pre>';
|
|
my $date = $2;
|
|
my $subj = $3;
|
|
|
|
# Right now, this doesn't do anything clever with links for individual
|
|
# commits. That's because I'm mostly using it for things that are single
|
|
# HTML files, so it doesn't seem to matter much. It would probably be smart
|
|
# to let users specify a different base path for these so they can link
|
|
# GitHub or what-have-you.
|
|
|
|
$feed->add_entry(
|
|
title => $subj,
|
|
link => $project_url,
|
|
id => $hash,
|
|
content => $formatted_commit,
|
|
updated => $date,
|
|
);
|
|
}
|
|
|
|
print $feed->as_string;
|
|
|
|
sub iso_date {
|
|
my ($time) = @_;
|
|
return strftime("%Y-%m-%dT%H:%M:%SZ", localtime($time));
|
|
}
|
|
|
|
# Get contents of a file as a string, because a File::Slurp dependency seemed
|
|
# like overkill:
|
|
sub slurp {
|
|
my ($file) = @_;
|
|
my $everything;
|
|
|
|
open my $fh, '<', $file
|
|
or die "Couldn't open $file: $!\n";
|
|
|
|
# line separator:
|
|
local $/ = undef;
|
|
$everything = <$fh>;
|
|
|
|
close $fh
|
|
or die "Couldn't close $file: $!";
|
|
|
|
return $everything;
|
|
}
|
|
|
|
# Run git with some options, return result:
|
|
sub git { capturex('git', @_); }
|
|
|
|
# Return a git config value - will be an empty string for unset values:
|
|
sub git_config {
|
|
my ($key) = @_;
|
|
my $value = capturex([0,1], 'git', 'config', '--get', $key);
|
|
chomp($value);
|
|
return $value;
|
|
}
|