#!/usr/bin/perl 
#
# rpmgrill-fetch-build - given NVR + dir, unpack all koji RPMs & logs
#
# $Id$
#
package RPM::Grill::FetchBuild;

use v5.6.0;
use strict;
use warnings;
use XMLRPC::Lite;
use File::Spec;

(our $ME = $0) =~ s|.*/||;

our $VERSION = '0.0';                 # Will be replaced by Makefile

# For debugging, show data structures using DumpTree($var)
#use Data::TreeDumper; $Data::TreeDumper::Displayaddress = 0;

###############################################################################
# BEGIN user-customizable section

# Standard RPM command
our @RPM = qw(rpm -qp --nosignature --nodigest);

# Fields used to generate the RPM.per_file_metadata file.  This will
# get passed to the rpm command as a --queryformat string in the form:
#      %{field1}\t%{field2}\t...
# The special-casing of (\d+) is so we get a fixed-width %64{filemd5s},
# which is nice for making readable output.
our @Per_File_Metadata = map { /(.*?)(\((\d+)\))?$/; "%" . ($3||'') . "{$1}" }
    qw(
          filemd5s(64)
          filemodes:perms
          fileusername
          filegroupname
          fileflags
          filecolors
          filenames
  );
our $Per_File_Metadata = join("\\t", @Per_File_Metadata);

# Metadata files we create.  For each line below, we create a file RPM.xxx
# which will be read by rpmgrill.  Format of the list below is:
#
#    <name><nodups?>    [<rpm options>]
#
#    name          Name of the RPM.xxx file, eg 'provides' => RPM.provides
#                  If there is nothing on the right-hand side, this is
#                  also the name of the rpm option (e.g. 'rpm --provides')
#
#    nodups?       A tilde here means 'filter duplicate lines'.  Needed
#                  to handle an odd case in libpng-1.2.10-7.1.el5_5.3 (and
#                  probably others) in which 'rpm --provides' on 32-bit
#                  arches spits out two identical 'libpng.so.3' lines.
#                  (on 64-bit, the second line includes '()(64bit)').
#
#    rpm options   Options passed to the rpm command for generating this file.
#                  If blank, prepend '--' to <name>; e.g. --provides
#
our $Metadata_Files = <<"END_METADATA_FILES";
provides~
requires~
scripts
obsoletes
conflicts
changelog
triggers
info
version           --queryformat %{EPOCH}:%{VERSION}-%{RELEASE}\\n
per_file_metadata --queryformat [$Per_File_Metadata\\n]
END_METADATA_FILES

# Parse the above.  Main data structure is a list of names, everything else
# is keyed off of that.
our @Metadata_Files;
our %Metadata_File_Opts;
our %Filter_Dups;
for my $line (split "\n", $Metadata_Files) {
    my ($name, @rest) = split(' ', $line);

    # FIRST, before doing anything with $name, see if it has a
    # trailing tilde.  If it does, strip it off: it means that
    # this metadata file does not want duplicates.
    $Filter_Dups{$name} = ($name =~ s/~$//);

    push @Metadata_Files, $name;

    # rpm options defined?  Use them.  Otherwise, same as RPM.xxxx name
    $Metadata_File_Opts{$name} = (@rest ? \@rest
                                        : [ "--$name" ]);
}

# These arches don't need a build log.
our %Doesnt_Need_Build_Log = map { $_ => 1 } ('noarch');

our $DEFAULT_SERVER_URL = 'http://koji.fedoraproject.org/kojihub';
our $DEFAULT_TOP_URL = 'http://kojipkgs.fedoraproject.org';

# END   user-customizable section
###############################################################################

use Carp;
use File::Path                  qw(mkpath);

###############################################################################
# BEGIN boilerplate args checking, usage messages

sub usage {
    print  <<"END_USAGE";
Usage: $ME [OPTIONS]  BUILDINFO  DESTDIR

$ME extracts a full set of RPMs from a koji build (including
scratch builds). $ME writes into DESTDIR, something like:

   DESTDIR/
   ├── i686/
   │   ├── foo/
   │   │   └── payload/
   │   ├── foo-subpkg/
   │   │   └── payload/
   └── src/
       └── foo/
           └── payload/

  BUILDINFO  may be one of:
    N-V-R                         mypackage-1.2-3.el5
    koji task ID                  12345

  DESTDIR    destination directory into which to extract.
             Directory must not exist; $ME will create it.

CACHING:
   If the environment variable \$RPMGRILL_FETCH_BUILD_CACHE is defined,
   and points to an existing directory, and that directory has a
   subdirectory named N-V-R, $ME fetches RPMs and
   build logs from there.

OPTIONS:

  --download-only  Create DESTDIR, download RPMs and build logs, then exit
                   without unpacking. Probably only useful for debugging.
  --server-url     URL of the Koji XMLRPC endpoint.
                   (Default: $DEFAULT_SERVER_URL)
  --top-url        Download URL for the Koji file access.
                   (Default: $DEFAULT_TOP_URL)

  -v, --verbose  show verbose progress indicators to stdout

  --help         display this message
  --man          display program man page
  --version      display program name and version
END_USAGE

    exit;
}

sub man {
    # Read the POD contents.  If it hasn't been filled in yet, abort.
    my $pod = do { local $/; <DATA>; };
    if ($pod =~ /=head1 \s+ NAME \s+ FIXME/xm) {
        warn "$ME: No man page available.  Please try $ME --help\n";
        exit;
    }

    # Use Pod::Man to convert our __DATA__ section to *roff
    eval { require Pod::Man }
        or die "$ME: Cannot generate man page; Pod::Man unavailable: $@\n";
    my $parser = Pod::Man->new(name => $ME, release => $VERSION, section => 1);

    # If called without output redirection, man-ify.
    my $out_fh;
    if (-t *STDOUT) {                           ## no critic
        my $pager = $ENV{MANPAGER} || $ENV{PAGER} || 'less';
        open $out_fh, "| nroff -man | $pager";  ## no critic
    }
    else {
        open $out_fh, '>&STDOUT';               ## no critic
    }

    # Read the POD contents, and have Pod::Man read from fake filehandle.
    # This requires 5.8.0.
    open my $pod_handle, '<', \$pod;
    $parser->parse_from_filehandle($pod_handle, $out_fh);
    exit;
}


# Command-line options.  Note that this operates directly on @ARGV !
our $download_only;
our $server_url = $DEFAULT_SERVER_URL;
our $top_url = $DEFAULT_TOP_URL;
our $debug   = 0;
our $force   = 0;
our $verbose = 0;
our $NOT     = '';              # print "blahing the blah$NOT\n" if $debug
sub handle_opts {
    use Getopt::Long;
    GetOptions(
        'download-only!' => \$download_only,
        'server-url=s'   => \$server_url,
        'top-url=s'      => \$top_url,
        'debug!'         => \$debug,
        'dry-run|n!'     => sub { $NOT = ' [NOT]' },
        'force'          => \$force,
        'verbose|v'      => \$verbose,

        help             => \&usage,
        man              => \&man,
        version          => sub { print "$ME version $VERSION\n"; exit 0 },
    ) or die "Try `$ME --help' for help\n";
}

# END   boilerplate args checking, usage messages
###############################################################################
# BEGIN code for parsing koji buildinfo

package Koji::Build;
use Carp;
use File::Copy                  qw(copy);
use List::AllUtils              qw(uniq);

#########
#  new  #  Given NVR or build ID, run 'koji buildinfo'
#########
sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;

    my $self = {
        build_info => shift,
    };

    $self = bless $self, $class;
    $self->_validate();

    return $self;
}

sub _validate {
    my $self = shift;
    my $build_info = $self->{build_info};
    my @expected_keys = qw(id nvr package_name);
    my @missing_keys = grep { !defined $$build_info{$_} } @expected_keys;

    croak "Keys @{[ join', ', @missing_keys ]} missing in build info.",
          " Wrong build specified for this Koji instance?" if @missing_keys;
    return 0;
}

sub id {
    my $self = shift;

    return $self->{build_info}->{id};
}

#########
#  nvr  #  Returns the build N-V-R
#########
sub nvr {
    my $self = shift;

    my $b = $self->{build_info}->{nvr}
        or die "$ME: No 'nvr' field found in koji buildinfo";
    $b =~ /^(\S+)-(\S+)-(\S+)/
        or die "$ME: Internal error: Cannot grok koji build string '$b'";

    return wantarray ? ($1, $2, $3) : "$1-$2-$3";
}

sub package_name {
    my $self = shift;

    return $self->{build_info}->{package_name},
}

############
#  arches  #  Returns a list of arches that had builds
############
sub arches {
    my ($self, $rpms) = @_;

    return sort uniq map { $_->{'arch'} } @$rpms;
}


package Koji::Client;
use File::Fetch;
use Cwd                         qw(abs_path);

sub new {
    my $proto = shift;
    my $class = ref($proto) || $proto;

    my $self = {
        _xmlrpc_client => shift,
        top_url => shift,
    };
    return bless $self, $class;
}

sub get_xmlrpc_client {
    my $self = shift;
    return $self->{_xmlrpc_client};
}

sub get_build_info {
    my $self = shift;
    my $build_nvr = shift;

    return $self->_retrieve_build_info($build_nvr);
}

sub _retrieve_build_info {
    my $self = shift;
    my $build_nvr = shift;

    # Sample output from koji buildinfo:
    #
    # {
    # 'owner_name' => 'rjoost',
    # 'creation_time' => '2015-02-12 19:56:36.420956',
    # 'volume_name' => 'DEFAULT',
    # 'owner_id' => '2385',
    # 'package_name' => 'rpmdiff',
    # 'name' => 'rpmdiff',
    # 'creation_ts' => '1423788996.4209599',
    # 'id' => '421490',
    # 'creation_event_id' => '10729829',
    # 'volume_id' => '0',
    # 'state' => '1',
    # 'completion_time' => '2015-02-12 19:59:07.602378',
    # 'nvr' => 'rpmdiff-1.8.11-1.el5',
    # 'package_id' => '2936',
    # 'task_id' => '8731919',
    # 'completion_ts' => '1423789147.60238',
    # 'release' => '1.el5',
    # 'epoch' => undef,
    # 'version' => '1.8.11'
    # };

    my $xml_client = $self->get_xmlrpc_client();
    my $build = $xml_client->getBuild($build_nvr);
    return Koji::Build->new($build->result);
}

##########
#  rpms  #  Returns list of rpm files
##########
sub rpms {
    my $self = shift;
    my $build = shift;

    # Call returns list of hashes:
    #
    # [{
    # 'external_repo_name' => 'INTERNAL',
    # 'version' => '1.8.11',
    # 'payloadhash' => '460cb0c2e47ae5aba9e14788f21497f4',
    # 'nvr' => 'rpmdiff-1.8.11-1.el5',
    # 'buildtime' => '1423789075',
    # 'arch' => 'src',
    # 'size' => '3561764',
    # 'release' => '1.el5',
    # 'epoch' => undef,
    # 'id' => '3740308',
    # 'name' => 'rpmdiff',
    # 'buildroot_id' => '2471950',
    # 'build_id' => '421490',
    # 'external_repo_id' => '0'
    # }]

    my $build_id = $build->id();
    my $xml_client = $self->get_xmlrpc_client();
    my $rpms = $xml_client->listRPMs($build_id);
    return @{$rpms->result};
}

# END   general build info code
#-------------------------------------------------------------------------------
# BEGIN rpm download

##############################
#  _download_rpms_from_koji  #  No local cache; use koji command to fetch rpms
##############################
sub _download_rpms_from_koji {
    my $self = shift;
    my $build = shift;
    my $destdir = shift;
    my $top_url = $self->{top_url};

    # Template for generating a URL to a build log. %s will be replaced
    # with N, V, R, arch respectively.
    my $build_download_path_template = '/packages/%s/%s/%s/%s/%s';
    my @rpms = $self->rpms($build);
    die "$ME: Can't download rpms because RPM information is empty.\n" unless @rpms;

    foreach my $rpm_info (@rpms) {
        my $rpm_name = RPM::Grill::FetchBuild::get_full_rpm_name($rpm_info);
        my $download_url = sprintf(
            "$top_url/$build_download_path_template",
            $build->package_name(),
            $$rpm_info{'version'},
            $$rpm_info{'release'},
            $$rpm_info{'arch'},
            $rpm_name,
        );

        print "\$ GET $download_url $destdir\n" if $verbose;
        my $ff = File::Fetch->new(uri => $download_url);
        $ff->fetch(to => $destdir) or warn $ff->error;

    }
}


# END   rpm download
#-------------------------------------------------------------------------------
# BEGIN log file download

###################
#  build_log_url  #  URL to build.log for a given N-V-R and arch
###################
sub build_log_url {
    my $self = shift;
    my $arch = shift;                           # in: 'i686', etc
    my $build = shift;
    my $build_log_path_template = '/packages/%s/%s/%s/data/logs/%s/build.log';

    return sprintf("$top_url/$build_log_path_template", $build->nvr(), $arch);
}

###################
#  _download_log  #  Fetch a build log directly from koji
###################
sub _download_log {
    my $self = shift;
    my $arch = shift;                           # in: i386, x86_64, noarch, etc
    my $build = shift;

    my $url = $self->build_log_url($arch, $build);

    print "GET $url $arch\n"   if $verbose;
    my $ff = File::Fetch->new(uri => $url);
    return $ff->fetch(to => $arch) or warn $ff->error;
}

###################
#  download_logs  #  Download build logs for all arches
###################
sub download_logs {
    my $self = shift;
    my $build = shift;
    my @rpms = $self->rpms($build);

    # Now fetch the build logs.
    $self->{logs} = [];
    for my $arch (grep { $_ ne 'src' } $build->arches(\@rpms)) {
        if (my $log = $self->_download_log($arch, $build)) {
            push @{ $self->{logs} }, $log;
        }
    }
}

##########
#  logs  #  Returns a list of "build.log.<arch>" files (possibly empty)
##########
sub logs {
    my $self = shift;

    if (my $build_log = $self->{logs}) {
        return @$build_log;
    }
    return;
}
# END   log file download
#-------------------------------------------------------------------------------

##############
#  download  #  Download RPMs and build logs into the current directory
##############
sub download {
    my $self = shift;
    my $build = shift;
    my $destdir = shift;

    $self->_download_rpms_from_koji($build, $destdir);
    $self->download_logs($build);

    # FIXME: implement a --save or --preserve option, to copy rpms and logs
    # into a cachedir?
}

# Back to main package
package RPM::Grill::FetchBuild;

# END   code for parsing koji buildinfo
###############################################################################

############################## CODE BEGINS HERE ###############################

# The term is "modulino".
__PACKAGE__->main()                                     unless caller();

our @ARGV_orig;

# Main starting point.
sub main {
    # Preserve original ARGV; used for logging details about this run
    @ARGV_orig = @ARGV;

    # Note that we operate directly on @ARGV, not on function parameters.
    # This is deliberate: it's because Getopt::Long only operates on @ARGV
    # and there's no clean way to make it use @_.
    handle_opts();                      # will set package globals

    # Fetch command-line arguments.  Barf if too many.
    my $build_arg = shift(@ARGV)
        or die "$ME: missing BUILDINFO argument; try $ME --help\n";
    my $destdir = shift(@ARGV)
        or die "$ME: missing DESTDIR argument; try $ME --help\n";
    die "$ME: Too many arguments; try $ME --help\n"                 if @ARGV;

    # Destination directory must not exist.
    $destdir = Cwd::abs_path($destdir);
    die "$ME: Will not overwrite existing $destdir\n"   if -d $destdir;

    my $xml_client = XMLRPC::Lite->new(proxy => $server_url);
    $xml_client->on_debug( sub { print @_; } ) if $debug;

    my $client = Koji::Client->new($xml_client, $top_url);
    do_unpack($client, $build_arg, $destdir);
}


###############
#  do_unpack  #  Create output dir, unpack into it
###############
sub do_unpack {
    my $koji_client = shift;
    my $build_arg   = shift;        # in: NVR or task ID
    my $destdir     = shift;        # in: dir (may be relative; must not exist)

    # Now's the time to create the destination directory
    mkdir $destdir, 0755
        or die "$ME: mkdir $destdir: $!\n";
    chdir $destdir
        or die "$ME: Cannot cd $destdir: $!\n";

    my $build = $koji_client->get_build_info($build_arg);
    write_readme( $build, <<'END_TEMP_WARNING');
 **************************************************************************
***  WARNING: If you're reading this text, it means the unpacking is     ***
***  incomplete. Perhaps it's in progress. More likely, the unpacking    ***
***  process crashed. Caveat lector.                                     ***
 **************************************************************************
END_TEMP_WARNING

    # Download (or fetch) all RPMs and build logs. This leaves us with
    # one or more *.rpm files and zero or more build.log.<arch> files.
    $koji_client->download($build, $destdir);

    # Called with --download-only? Update the README, then exit cleanly.
    if ($download_only) {
        write_readme( $build, <<'END_WARNING');
** Download complete, but RPMs and logs not unpacked due to --download-only
END_WARNING
        exit 0;
    }

    extract_rpm($_, $destdir, $verbose)         for $koji_client->rpms($build);

    # Done. Rewrite the README, without the in-progress warning.
    write_readme( $build );
}


##################
#  write_readme  #  Write a README file to assist spelunkers
##################
sub write_readme {
    my $build       = shift;            # in: build info
    croak 'Something went wrong. Undefined build instance.' unless $build;

    # Write to a temp file, then mv it into place.
    my $readme_path = 'README';
    my $tmpfile     = "$readme_path.tmp.$$";
    unlink $tmpfile;

    # Top part is always standard.
    open my $readme_fh, '>', $tmpfile
        or die "$ME: Cannot create $tmpfile: $!\n";
    printf $readme_fh <<"END_README",

     Contents: %s
           on: %s
           by: $ME $VERSION
   invoked as: $ME @ARGV_orig

END_README
        scalar($build->nvr), scalar(CORE::localtime);

    # Extra args? Append to file. This may signal a temporary file
    # or a failure in extraction.
    print $readme_fh @_     if @_;

    close $readme_fh
        or die "$ME: Error writing $readme_path: $!\n";
    chmod 0444 => $tmpfile;
    rename $tmpfile => $readme_path
        or die "$ME: Could not mv $tmpfile: $!\n";
}

################
# return the full rpm name based on the information given by Koji.
#
# e.g. {'nvr' => 'rpmdiff-1.8.11-1.el5', 'arch' => 'i386' } results in:
#
#   'rpmdiff-1.8.11-1.el5.i386.rpm'
#################
sub get_full_rpm_name {
    my $rpm = shift;

    return sprintf('%s.%s.rpm', $$rpm{'nvr'}, $$rpm{'arch'});
}

#################
#  extract_rpm  #  Extract one RPM file
#################
sub extract_rpm {
    my $rpm = shift;
    my $destdir = shift;
    my $verbose = shift;

    # Get the name of the package.  From that name, and the rpm architecture,
    # create a new directory into which we unpack.
    my $rpm_file = File::Spec->catfile(
        $destdir, RPM::Grill::FetchBuild::get_full_rpm_name($rpm) );

    my $rpmdir = File::Spec->catfile($destdir, $$rpm{'arch'}, $$rpm{'name'});
    if (-d $rpmdir) {
        die "$ME: FATAL: Directory $rpmdir already exists."
            . " (this means we've already been invoked for this rpm)\n";
    }

    print "\$ mkdir -p $rpmdir/payload\n"               if $verbose;
    eval { mkpath "$rpmdir/payload", 0, 02755 };        ## no critic
    die "$ME: Could not mkdir $rpmdir/payload: $@\n"    if $@;

    # Move the locally-cached rpm into that directory
    rename $rpm_file => "$rpmdir/rpm.rpm"
        or die "$ME: Cannot mv cached $rpm_file -> $rpmdir/rpm.rpm: $!\n";

    chdir $rpmdir
        or die "$ME: Cannot cd $rpmdir: $!";

    # Extract contents
    my $cmd = "rpm2cpio rpm.rpm | (cd payload && cpio -i -d --quiet)";
    print "\$ $cmd\n"                                   if $verbose;
    system($cmd) == 0
        or die "$ME: FATAL: error running command: $cmd\n";

    # Create each RPM.xxx file
    for my $opt (@Metadata_Files) {
        my @foo = run_rpm( $opt, 'rpm.rpm' );
    }
    chdir "../.."
        or die "$ME: Cannot cd ../.. !?!?!";
}

#############
#  run_rpm  #  Invokes rpm command with given args, preserves its output
#############
sub run_rpm {
    my $meta = shift;                           # in: AREF or string
    my $rpm  = shift;                           # in: path to RPM

    my @opts;
    my $filter_dups;
    my $outfile;

    # When invoked with explicit command-line arguments (via an AREF)
    # invoke rpm with those arguments and return the output to our caller.
    #
    # When invoked with a simple string, use the command-line arguments
    # predefined at the top of the script, then save the output to RPM.* file.
    if (ref($meta)) {                   # eg [ '--queryformat' => '%%NAME' ]
        @opts = @$meta;
    }
    else {                              # eg 'provides', 'requires'
        @opts        = @{ $Metadata_File_Opts{$meta} };
        $filter_dups = $Filter_Dups{$meta};
        $outfile     = "RPM.$meta";
    }

    my @cmd = (@RPM, @opts, $rpm);
    if ($verbose) {
        printf "\$ rpm ... %-12s", "@opts";
        print  " >$outfile"                     if $outfile;
        print  "\n";
        print "[ @cmd ]\n"                      if $debug;
    }

    my $out_fh;
    my $out_tmp;
    if ($outfile) {
        $out_tmp = "$outfile.tmp.$$";
        # FIXME: barf if outfile already exists?
        open $out_fh, '>', $out_tmp
            or die "$ME: Cannot create $out_tmp: $!\n";
    }

    my @retval;
    open my $rpm_fh, '-|', @cmd
        or die "$ME: Cannot fork: $!\n";
    my %seen;
  RPMLINE:
    while (my $line = <$rpm_fh>) {
        next RPMLINE            if $filter_dups && $seen{$line}++;

        print { $out_fh } $line                 if $out_fh;

        chomp $line;
        push @retval, $line;
    }
    close $rpm_fh
        or die "$ME: Error running @cmd: status=$?\n";

    if ($outfile) {
        close $out_fh
            or die "$ME: Error writing $out_tmp: $!\n";
        chmod 0444 => $out_tmp;
        rename $out_tmp => $outfile
            or die "$ME: Cannot rename $out_tmp: $!\n";
    }

    # Called in array context?  Return everything we read.
    return @retval              if wantarray;

    # Called in VOID context?  Don't return anything.
    return                      if ! defined wantarray;

    # Called in scalar context.  We _should_ have seen only one line of output.
    return $retval[0]           if  @retval == 1;
    return                      if !@retval;
    die "$ME: Internal error: Expecting 1 line of output from @cmd, got: @retval\n";
}


1;

__DATA__

###############################################################################
#
# Documentation
#

=head1	NAME

rpmgrill-fetch-build - extract all RPMs and build logs from a Koji build

=head1	SYNOPSIS

rpmgrill-fetch-build BUILDINFO DESTDIR

rpmgrill-fetch-build  B<--help>  |  B<--version> | B<--man>

=head1	DESCRIPTION

B<rpmgrill-fetch-build> extracts all RPMs and build logs from a koji
build. This is a helper step required by the B<rpmgrill> analysis tool.

=head1  USAGE EXAMPLES

  # By name
  $ rpmgrill-fetch-build  coreutils-8.15-10.fc17  my-coreutils-8.15-10.fc17

  # Same thing, by Koji task ID
  $ rpmgrill-fetch-build  381337 my-coreutils-8.15-10.fc17

=head1  ARGUMENTS

=over 2

=item *

B<BUILDINFO> describes what to extract. It may be an B<N-V-R>
or a B<task ID>. FIXME: test with scratch builds.

=item *

B<DESTDIR> is the path to a directory into which rpmgrill-fetch-build will
extract RPMs. It must not exist: if it does, rpmgrill-fetch-build will abort
with a fatal error.

=back

=head1	OPTIONS

=over 4

=item   B<--download-only>

Download the RPMs and build logs into DESTDIR, then exit cleanly.

RPM filenames will be exactly as downloaded; log files will
be named C<build.log.ARCH> for all available ARCHes.

=item B<--server-url>

URL of the Koji XMLRPC endpoint.

(Default: http://koji.fedoraproject.org/kojihub)

=item B<--top-url>

Download URL for the Koji file access.

(Default: http://kojipkgs.fedoraproject.org/)

=item B<--verbose>

Show progress messages.

=item B<--help>

Emit usage hints.

=item B<--version>

Display program version.

=item B<--man>

Display this man page.

=back


=head1	DIAGNOSTICS

FIXME

=head1	ENVIRONMENT

If the environment variable $RPMGRILL_FETCH_BUILD_CACHE is defined,
and points to an existing directory, and that directory has a
subdirectory named N-V-R, rpmgrill-fetch-build fetches RPMs and
build logs from there.

The format of this directory is:

    \$RPMGRILL_FETCH_BUILD_CACHE/
    `-- mypkg-1.0-1.fc19/
        |-- build.log.i686
        |-- build.log.x86_64
        |-- mypkg-1.0-1.fc19.i686.rpm
        `-- mypkg-1.0-1.fc19.x86_64.rpm

That is: all *.rpm files (as fetched from koji), and all build logs
using the convention C<build.log.ARCH> for all available ARCHes.

=head1	FILES

rpmgrill-fetch-build writes a README file to DESTDIR. On successful completion
of all rpms, it will look like:

      Contents: coreutils-8.15-10.fc17
            on: Fri May  3 12:02:03 2013
            by: rpmgrill-fetch-build 0.0
    invoked as: rpmgrill-fetch-build 381337 my-coreutils-8.15-10.fc17

During extraction, or if there's a failure, the README will have
the above text and also a note explaining that the directory is
incomplete and should not be used.

=head1	AUTHOR

Ed Santiago <santiago@redhat.com>

=cut
