|
|
- #!/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;
- }
|