A script to generate Atom feeds for git commit logs.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

213 lines
5.4 KiB

9 years ago
9 years ago
9 years ago
  1. #!/usr/bin/env perl
  2. =pod
  3. =head1 NAME
  4. git-feed - generate an Atom feed of git commits in the current repository
  5. =head1 SYNOPSIS
  6. git-feed [ B<--project_url=...> ]
  7. =head1 DESCRIPTION
  8. I keep a lot of projects in git, and I like to provide feeds for things I'm
  9. working on. This script wraps git and XML::Atom::SimpleFeed to generate a
  10. basic Atom feed of commits. It tries to use information already available in
  11. the environment, but needs to be given a URL for your project (and probably for
  12. the feed itself).
  13. It can be configured for a given repository by doing something like the
  14. following:
  15. git config feed.projecturl https://p1k3.com/userland-book/
  16. git config feed.url https://p1k3.com/userland-book/feed.xml
  17. echo 'userland: a book about the command line for human beings' > .git/description
  18. I have I<no idea> whether this sort of thing is considered an acceptable use of
  19. git configuration, but it seems to work.
  20. ...or you can supply various options on the command line.
  21. B<--project_url=...>
  22. Set a URL for the project.
  23. B<--feed_url=...>
  24. Set an explicit URL for the feed itself.
  25. B<--title=...>
  26. Set an explicit title for the feed. Will otherwise be read from
  27. .git/description, if available.
  28. B<--entries=>I<n>
  29. Explicitly request I<n> entries. Defaults to 10.
  30. B<--author=...>
  31. Explicitly set an author string. Defaults to git config's current idea of
  32. user.name.
  33. =head1 EXAMPLES
  34. git-feed --project_url=https://p1k3.com/userland-book/ > feed.xml
  35. =head1 INSTALLING
  36. I'll wrap this in a module and add a build script. For now, install
  37. XML::Atom::SimpleFeed first. I would probably do that either with:
  38. cpanp -i XML::Atom::SimpleFeed
  39. or:
  40. apt-get install libxml-atom-simplefeed-perl
  41. ...depending on which one gave me less static on a given system. I don't
  42. really know what I'm doing in the Perl ecosystem any more, though.
  43. Anyway, if you put git-feed in your path, you can probably invoke it like any
  44. other git subcommand:
  45. git feed > feed.xml
  46. =head1 AUTHOR
  47. Brennen Bearnes <bbearnes@gmail.com>
  48. =head1 LICENSE
  49. This utility is free software, licensed under the terms of the GPL, v2. See
  50. COPYING for a complete copy of the license.
  51. =cut
  52. # temporary hack
  53. # use lib '/home/brennen/code/perl_libs';
  54. use warnings;
  55. use strict;
  56. use 5.10.0;
  57. use XML::Atom::SimpleFeed;
  58. use HTML::Entities;
  59. use Getopt::Long;
  60. use POSIX qw(strftime);
  61. # Turns out to be pretty much exactly what I want for system calls:
  62. use IPC::System::Simple qw(capturex);
  63. # Figure out where top-level .git/ resides. Naturally, "rev-parse" was the
  64. # first command that leapt to mind when I wondered if there was a builtin way
  65. # to ask that question. I mean, what kind of loser would have had to google
  66. # something so _blindingly obvious_?
  67. my $repo_base = git('rev-parse', '--show-toplevel');
  68. chomp($repo_base);
  69. my $title = 'git log';
  70. $title = slurp("$repo_base/.git/description")
  71. if (-f "$repo_base/.git/description");
  72. my $author = git_config('user.name');
  73. chomp($author);
  74. my $feed_url = git_config('feed.url');
  75. my $project_url = git_config('feed.projecturl');
  76. my $entries = 10;
  77. GetOptions(
  78. "author:s" => \$author,
  79. "entries:i" => \$entries,
  80. "feed_url:s" => \$feed_url,
  81. "project_url:s" => \$project_url,
  82. "title:s" => \$title,
  83. );
  84. # Where does this project live on the web?
  85. unless ($project_url) {
  86. say "Project URL required - use one of:";
  87. say "\tgit feed --project_url=https://yourlinkhere/";
  88. say "\tgit config feed.projecturl https://yourlinkhere/";
  89. exit(1);
  90. }
  91. # No feed link set? Make something up.
  92. unless ($feed_url) {
  93. $feed_url = $project_url . 'feed.xml';
  94. }
  95. my $log = capturex('git', 'log', "-$entries", qq{--pretty=format:%H _ %ai _ %s});
  96. my $feed = XML::Atom::SimpleFeed->new(
  97. title => $title,
  98. link => $project_url,
  99. link => { rel => 'self', href => $feed_url, },
  100. author => $author,
  101. id => $project_url,
  102. generator => 'XML::Atom::SimpleFeed',
  103. # This could be better - just uses current time, at the moment:
  104. updated => iso_date(time()),
  105. );
  106. while ($log =~ m/^([a-z0-9]+) _ (.*) _ (.*)$/gm) {
  107. my $hash = $1;
  108. my $full_commit = git('show', $hash);
  109. my $formatted_commit = '<pre>' . encode_entities($full_commit) . '</pre>';
  110. my $date = $2;
  111. my $subj = $3;
  112. # Right now, this doesn't do anything clever with links for individual
  113. # commits. That's because I'm mostly using it for things that are single
  114. # HTML files, so it doesn't seem to matter much. It would probably be smart
  115. # to let users specify a different base path for these so they can link
  116. # GitHub or what-have-you.
  117. $feed->add_entry(
  118. title => $subj,
  119. link => $project_url,
  120. id => $hash,
  121. content => $formatted_commit,
  122. updated => $date,
  123. );
  124. }
  125. print $feed->as_string;
  126. sub iso_date {
  127. my ($time) = @_;
  128. return strftime("%Y-%m-%dT%H:%M:%SZ", localtime($time));
  129. }
  130. # Get contents of a file as a string, because a File::Slurp dependency seemed
  131. # like overkill:
  132. sub slurp {
  133. my ($file) = @_;
  134. my $everything;
  135. open my $fh, '<', $file
  136. or die "Couldn't open $file: $!\n";
  137. # line separator:
  138. local $/ = undef;
  139. $everything = <$fh>;
  140. close $fh
  141. or die "Couldn't close $file: $!";
  142. return $everything;
  143. }
  144. # Run git with some options, return result:
  145. sub git { capturex('git', @_); }
  146. # Return a git config value - will be an empty string for unset values:
  147. sub git_config {
  148. my ($key) = @_;
  149. my $value = capturex([0,1], 'git', 'config', '--get', $key);
  150. chomp($value);
  151. return $value;
  152. }