File Coverage

blib/lib/PkgForge/Job.pm
Criterion Covered Total %
statement 10 12 83.3
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 14 16 87.5


line stmt bran cond sub pod time code
1             package PkgForge::Job; # -*-perl-*-
2 3     3   65702 use strict;
  3         7  
  3         121  
3 3     3   16 use warnings;
  3         7  
  3         133  
4              
5             # $Id: Job.pm.in 17740 2011-06-30 05:10:48Z squinney@INF.ED.AC.UK $
6             # $Source:$
7             # $Revision: 17740 $
8             # $HeadURL: https://svn.lcfg.org/svn/source/tags/PkgForge/PkgForge_1_4_8/lib/PkgForge/Job.pm.in $
9             # $Date: 2011-06-30 06:10:48 +0100 (Thu, 30 Jun 2011) $
10              
11             our $VERSION = '1.4.8';
12              
13 3     3   18 use Carp ();
  3         5  
  3         50  
14 3     3   4591 use Data::UUID::Base64URLSafe ();
  0            
  0            
15             use English qw(-no_match_vars);
16             use File::Copy ();
17             use File::Path ();
18             use PkgForge::SourceUtils ();
19             use PkgForge::Utils ();
20              
21             use overload q{""} => sub { shift->stringify };
22              
23             use Moose;
24             use MooseX::Types::Moose qw(Bool Int Str);
25             use Moose::Util::TypeConstraints;
26             use PkgForge::Types qw(AbsolutePathDirectory EmailAddressList UserName
27             PkgForgeList SourcePackageList PkgForgeID);
28              
29             with 'PkgForge::YAMLStorage';
30              
31             has 'directory' => (
32             is => 'rw',
33             isa => AbsolutePathDirectory,
34             predicate => 'has_directory',
35             documentation => 'The directory in which the job is stored',
36             );
37              
38             has 'id' => (
39             traits => ['PkgForge::Serialise'],
40             is => 'rw',
41             isa => PkgForgeID,
42             required => 1,
43             builder => 'gen_id',
44             documentation => 'The unique identifier for this job',
45             );
46              
47             sub gen_id {
48             return Data::UUID::Base64URLSafe->new()->create_b64_urlsafe();
49             }
50              
51             has 'platforms' => (
52             traits => [ 'Array','PkgForge::Serialise', 'Getopt' ],
53             is => 'rw',
54             isa => PkgForgeList,
55             coerce => 1,
56             required => 1,
57             default => sub { ['auto'] },
58             auto_deref => 1,
59             handles => {
60             has_no_platforms => 'is_empty',
61             filter_platforms => 'grep',
62             },
63             cmd_aliases => 'p',
64             documentation => 'The platforms on which packages should be built',
65             );
66              
67             has 'archs' => (
68             traits => [ 'Array', 'PkgForge::Serialise', 'Getopt' ],
69             is => 'rw',
70             isa => PkgForgeList,
71             coerce => 1,
72             required => 1,
73             default => sub { ['all'] },
74             auto_deref => 1,
75             handles => {
76             has_no_archs => 'is_empty',
77             filter_archs => 'grep',
78             },
79             cmd_aliases => 'a',
80             documentation => 'The architectures for which packages should be built',
81             );
82              
83             has 'bucket' => (
84             traits => [ 'PkgForge::Serialise', 'Getopt' ],
85             is => 'rw',
86             isa => Str,
87             required => 1,
88             cmd_aliases => 'B',
89             documentation => 'The bucket to which the packages will be submitted.',
90             );
91              
92             has 'subtime' => (
93             traits => ['PkgForge::Serialise'],
94             is => 'rw',
95             isa => Int,
96             documentation => 'The date/time the job was submitted',
97             );
98              
99             has 'size' => (
100             traits => ['PkgForge::Serialise'],
101             is => 'rw',
102             isa => Int,
103             builder => '_job_size',
104             lazy => 1,
105             documentation => 'The total size of the source packages in bytes',
106             );
107              
108             sub _job_size {
109             my ($self) = @_;
110              
111             my $total = 0;
112             for my $pkg ($self->packages) {
113             $total += $pkg->size;
114             }
115              
116             return $total;
117             }
118              
119             has 'packages' => (
120             traits => [ 'Array', 'PkgForge::Serialise' ],
121             is => 'ro',
122             isa => SourcePackageList,
123             auto_deref => 1,
124             default => sub { [] },
125             handles => {
126             add_packages => 'push',
127             count_packages => 'count',
128             filter_packages => 'grep',
129             packages_list => 'elements',
130             },
131             pack => sub { PkgForge::SourceUtils::pack_packages($_[0]) },
132             unpack => sub { PkgForge::SourceUtils::unpack_packages($_[0]) },
133             documentation => 'The set of source packages to be built',
134             );
135              
136             after 'add_packages' => sub {
137             my ($self) = @_;
138             $self->update_job_size;
139             return;
140             };
141              
142             sub update_job_size {
143             my ($self) = @_;
144              
145             $self->size( $self->_job_size );
146              
147             return;
148             }
149              
150             has 'submitter' => (
151             traits => ['PkgForge::Serialise'],
152             is => 'rw',
153             isa => UserName,
154             required => 1,
155             coerce => 1,
156             default => sub { getpwuid($<) },
157             documentation => 'The name of the person who submitted this job',
158             );
159              
160             has 'report' => (
161             traits => ['PkgForge::Serialise','Getopt','Array'],
162             is => 'rw',
163             isa => EmailAddressList,
164             coerce => 1,
165             cmd_aliases => 'r',
166             predicate => 'report_required',
167             handles => {
168             report_list => 'elements',
169             },
170             documentation => 'Email addresses to which build reports will be sent',
171             );
172              
173             has 'verbose' => (
174             traits => ['Getopt'],
175             is => 'rw',
176             isa => Bool,
177             default => 0,
178             documentation => 'Verbose output',
179             );
180              
181             around 'new_from_yamlfile' => sub {
182             my $orig = shift;
183             my $class = shift;
184              
185             my $obj = $class->$orig(@_);
186              
187             if ( $obj->has_directory ) {
188             my $dir = $obj->directory;
189              
190             for my $package ($obj->packages_list) {
191             $package->basedir($dir);
192             }
193             }
194              
195             return $obj;
196             };
197              
198             no Moose;
199             __PACKAGE__->meta->make_immutable;
200              
201             sub stringify {
202             my ($self) = @_;
203             return $self->id;
204             }
205              
206             sub clone {
207             my ($self) = @_;
208              
209             require Storable;
210             my $clone = Storable::dclone($self);
211              
212             return $clone;
213             }
214              
215             sub process_build_targets {
216             my ( $self, @available ) = @_;
217              
218             my @all;
219             my @auto;
220             my %targets;
221             for my $item (@available) {
222             my ( $name, $arch, $auto )
223             = ( $item->{name}, $item->{arch}, $item->{auto} );
224              
225             push @all, $name;
226             push @auto, $name if $auto;
227              
228             push @{ $targets{$name} }, $arch;
229             }
230              
231             my @names = $self->process_platforms( \@all, \@auto );
232              
233             my @wanted;
234             for my $name (@names) {
235             my @archs = $self->process_archs($targets{$name});
236              
237             for my $arch (@archs) {
238             push @wanted, [ $name, $arch ];
239             }
240             }
241              
242             return @wanted;
243             }
244              
245             sub process_platforms {
246             my ( $self, $all, $auto ) = @_;
247              
248             # Process the selections (anything which is not prefixed with an '!')
249              
250             my @additions = $self->filter_platforms( sub { !m/^!/ } );
251              
252             my @selected;
253             if ( $self->has_no_platforms || scalar @additions == 0 ) {
254             @selected = @{$auto};
255             }
256             else {
257             for my $addition (@additions) {
258             if ( $addition eq 'all' ) {
259             push @selected, @{$all};
260             } elsif ( $addition eq 'auto' ) {
261             push @selected, @{$auto};
262             } else {
263             my @matches = grep { lc($_) eq lc($addition) } @{$all};
264             push @selected, @matches;
265             }
266             }
267             }
268              
269             # Process any deletions
270              
271             my @deletions;
272             for my $deletion ( $self->filter_platforms( sub { m/^!/ } ) ) {
273             $deletion =~ s/^!//;
274             push @deletions, $deletion;
275             }
276              
277             # uniqueify the list and make it easier to handle deletions
278              
279             my %result = map { $_ => 1 } @selected;
280              
281             # Need to handle the deletions list in a case-insensitive manner
282              
283             for my $deletion (@deletions) {
284             $deletion = lc $deletion;
285              
286             for my $key ( keys %result ) {
287             if ( lc($key) eq $deletion ) {
288             delete $result{$key};
289             }
290             }
291             }
292              
293             return (sort keys %result);
294             }
295              
296             sub process_archs {
297             my ( $self, $available ) = @_;
298              
299             my @deletions;
300             for my $deletion ( $self->filter_archs( sub { m/^!/ } ) ) {
301             $deletion =~ s/^!//;
302             push @deletions, $deletion;
303             }
304              
305             my @additions = $self->filter_archs( sub { !m/^!/ } );
306              
307             my %result;
308             if ( $self->has_no_archs
309             || scalar @additions == 0 || grep { $_ eq 'all' } @additions ) {
310             %result = map { $_ => 1 } @{$available};
311             } else {
312             for my $addition (@additions) {
313             for my $match ( grep { lc($_) eq lc($addition) } @{$available} ) {
314             $result{$match} = 1;
315             }
316             }
317             }
318              
319             # Need to handle the deletions list in a case-insensitive manner
320              
321             for my $deletion (@deletions) {
322             $deletion = lc $deletion;
323              
324             for my $key ( keys %result ) {
325             if ( lc($key) eq $deletion ) {
326             delete $result{$key};
327             }
328             }
329             }
330              
331             return (sort keys %result);
332             }
333              
334             sub validate {
335             my ($self) = @_;
336              
337             my @packages = $self->packages;
338              
339             if ( scalar @packages == 0 ) {
340             die "No packages to build\n";
341             }
342              
343             for my $package (@packages) {
344             my $file = $package->file;
345              
346             if ( !$package->check_sha1sum ) {
347             die "The sha1sum does not match for $file\n";
348             }
349              
350             my $ok = eval { $package->validate };
351             if ( !$ok ) {
352             die "File $file is not a valid source package\n";
353             }
354             }
355              
356             return 1;
357             }
358              
359             sub transfer {
360             my ( $self, $target ) = @_;
361              
362             my $id = $self->id;
363              
364             my $new_dir = File::Spec->catdir( $target, $id );
365             if ( -e $new_dir ) {
366             die "It appears that job $id is already transferred\n";
367             }
368              
369             my $created = eval { File::Path::mkpath($new_dir) };
370              
371             if ($EVAL_ERROR) {
372             die "Failed to create $new_dir: $EVAL_ERROR\n";
373             }
374             if ( $created != 1 ) {
375             die "Unexpected results from creating $new_dir: created $created directories\n";
376             }
377              
378             # Doing a clone allows us to update the metadata for the
379             # package objects.
380              
381             my $new_obj = $self->clone;
382              
383             my $ok;
384             for my $package ($new_obj->packages) {
385             my $fullpath = $package->fullpath;
386             if ( $self->verbose ) {
387             warn "Copying '$fullpath' to '$new_dir'\n";
388             }
389             $ok = File::Copy::copy( $fullpath, $new_dir );
390             last if !$ok;
391             $package->basedir($new_dir);
392             }
393              
394             if (!$ok) {
395             _transfer_failure( $new_dir, "Failed to transfer some packages for job $id" );
396             }
397              
398             eval {
399             my $build_file = File::Spec->catfile( $new_dir, 'build.yml' );
400             $new_obj->yamlfile($build_file);
401             $new_obj->directory($new_dir);
402             $new_obj->store_in_yamlfile();
403             };
404             if ($EVAL_ERROR) {
405             _transfer_failure( $new_dir, "Failed to load transferred job $id" );
406             }
407              
408             if ( $self->verbose ) {
409             warn "Copy finished, will now validate.\n";
410             }
411             my $valid = eval { $new_obj->validate() };
412             if ( !$valid || $EVAL_ERROR ) {
413             _transfer_failure( $new_dir, "Failed to validate transfer of job $id" );
414             }
415              
416             return $new_obj;
417             }
418              
419             sub _transfer_failure {
420             my ( $dir, $msg ) = @_;
421              
422             if ( -d $dir ) {
423             PkgForge::Utils::remove_tree($dir);
424             }
425              
426             die "$msg\n";
427             }
428              
429             sub overdue {
430             my ( $self, $timeout ) = @_;
431              
432             my $now = time;
433             return ( ($now - $timeout) > $self->subtime );
434             }
435              
436             sub new_from_qentry {
437             my ( $class, $qentry ) = @_;
438              
439             my $dir = $qentry->path;
440              
441             my $obj = $class->new_from_dir($dir);
442              
443             # Submitter
444             my $uid = $qentry->owner;
445             my $owner = getpwuid($uid);
446             $obj->submitter( $owner || $uid );
447              
448             # Submission time
449             $obj->subtime($qentry->timestamp);
450              
451             return $obj;
452             }
453              
454             sub new_from_dir {
455             my ( $class, $dir ) = @_;
456              
457             if ( !-d $dir ) {
458             die "Build job directory '$dir' does not exist\n";
459             }
460              
461             my $build_file = File::Spec->catfile( $dir, 'build.yml' );
462              
463             if ( !-f $build_file ) {
464             die "Build job file '$build_file' does not exist\n";
465             }
466              
467             return $class->new_from_yamlfile( yamlfile => $build_file,
468             directory => $dir );
469             }
470              
471             sub scrub {
472             my ( $self, $options ) = @_;
473              
474             PkgForge::Utils::remove_tree( $self->directory, $options );
475              
476             undef $self;
477              
478             return;
479             }
480              
481             1;
482             __END__
483              
484             =head1 NAME
485              
486             PkgForge::Job - Represents a build job for the LCFG Package Forge
487              
488             =head1 VERSION
489              
490             This documentation refers to PkgForge::Job version 1.4.8
491              
492             =head1 SYNOPSIS
493              
494             use PkgForge::Job;
495             use PkgForge::Source::SRPM;
496              
497             my $job = PkgForge::Job->new( bucket => "lcfg",
498             archs => ["i386","x86_64"],
499             platform => ["sl5"] );
500              
501             my $package =
502             PkgForge::Source::SRPM->new( file => "foo-1.2.src.rpm");
503              
504             $job->add_packages($package);
505              
506             my $ok = eval { $job->validate };
507             if ( !$ok || $@ ) {
508             die "Invalid job $job: $@\n";
509             }
510              
511             =head1 DESCRIPTION
512              
513             This module provides a representation of a build job which is used by
514             the LCFG Package Forge software suite.
515              
516             It can be used to submit new jobs and also query and validate jobs
517             which have already been submitted.
518              
519             The object represents a set of source packages which should be built
520             as a single build job. It also holds all the information covering the
521             platforms and architectures on which the packages should be built, the
522             repository into which the generated binary packages should be
523             submitted, who submitted the job and when.
524              
525             =head1 ATTRIBUTES
526              
527             =over 4
528              
529             =item platforms
530              
531             This is the list of platforms for which the set of packages in the job
532             should be built. By default this list contains just the string C<auto>
533             and the generated list of platforms is based on those which are active
534             and listed as being available for adding automatically. If the list
535             contains the string C<all> then the build will be attempted on all
536             available platforms.
537              
538             It is possible to block some platforms to ensure they are
539             not attempted by prefixing the platform name with C<!> (exclamation
540             mark). If the list only contains negated platforms then builds will be
541             attempted on all platforms except those negated. If the list contains
542             a mixture of platforms and negated platforms then only those requested
543             will be attempted. If we consider an example where three platforms are
544             supported, e.g. el5, f12 and f13, here are possible values:
545              
546             =over
547              
548             =item C<[ "all" ]> gives C<[ "el5", "f12", "f13" ]>
549              
550             =item C<[ "all", "!el5" ]> gives C<[ "f12", "f13" ]>
551              
552             =item C<[ "f12", "f13" ]> gives C<[ "f12", "f13" ]>
553              
554             =item C<[ "el5", "!f13" ]> gives C<[ "el5" ]>
555              
556             =back
557              
558             Any platform string which is not recognised is ignored.
559              
560             =item archs
561              
562             This is the list of architectures for which the set of packages in the
563             job should be built. If the list contains the string "all" then the
564             build will be attempted on all available architectures (which is the
565             default).
566              
567             In the same way as for the platforms, it is possible to block some
568             architectures to ensure they are not attempted by prefixing the
569             platform name with C<!> (exclamation mark). If the list only contains
570             negated architectures then builds will be attempted on all
571             architectures except those negated. If the list contains a mixture of
572             architectures and negated architectures then only those requested will
573             be attempted. If we consider an example where three architectures are
574             supported, e.g. i386, x86_64 and ppc, here are possible values:
575              
576             =over
577              
578             =item C<[ "all" ]> gives C<[ "i386", "x86_64", "ppc" ]>
579              
580             =item C<[ "all", "!i386" ]> gives C<[ "x86_64", "ppc" ]>
581              
582             =item C<[ "x86_64", "ppc" ]> gives C<[ "x86_64", "ppc" ]>
583              
584             =item C<[ "i386", "!ppc" ]> gives C<[ "i386" ]>
585              
586             Note that it is B<NOT> possible to specify a single build job as being
587             for different sets of architectures on each of the different specified
588             platforms.
589              
590             Any architecture string which is not recognised is ignored.
591              
592             =item bucket
593              
594             This is the LCFG package bucket into which built packages will be
595             submitted. This is normally something like "lcfg", "world", "uoe" or
596             "inf". There is no default value and the bucket C<MUST> be specified.
597              
598             When building RPMs with mock this bucket is also used to control which
599             mock configuration file is used. This controls which package
600             repositories mock has access to for fulfilling build
601             requirements. This is done to ensure that packages do not have
602             auto-generated dependency lists which cannot be fulfilled from within
603             that bucket or the base/updates package repositories.
604              
605             =item packages
606              
607             This is a list of source packages for the build job which are to be
608             built for the set of platforms and architectures. The list takes
609             objects which implement the PkgForge::Source role. You can
610             specify as many source packages as you like and mix the types within a
611             single job. It is left to the individual build daemons to decide
612             whether they are capable of building from particular types of source
613             package.
614              
615             When building RPMs, within a single job, once a package has been built
616             it becomes available immediately for use as a build-requirement for
617             the building of subsequent packages. This means that the order in
618             which the packages are specified is significant. Note that no attempt
619             is made to solve the build-dependencies for the source packages within
620             a build job. This extension might be considered at some point in the
621             future.
622              
623             A build job is not valid if no packages have been specified.
624              
625             =item size
626              
627             This is the total size of the source packages, measured in bytes.
628              
629             =item report
630              
631             This is a list of email addresses to which a final report will be
632             sent. By default no reports are sent.
633              
634             =item directory
635              
636             This is the directory in which the packages and the configuration file
637             for a build job are stored. It does not have to be specified, the
638             default is assumed to be the current directory where necessary.
639              
640             =item yamlfile
641              
642             This is the location of the build job configuration file. This is used
643             for serialisation of the job object for later reuse. Note that not all
644             attributes are stored when this is written and not all are read when
645             it is reloaded. See C<store_in_yamlfile> and C<new_from_yamlfile> for
646             details.
647              
648             =item id
649              
650             This is the UUID for the build job. If none has been specified then a
651             default value is generated using the L<Data::UUID> module, in which
652             case the UUID is also converted into base64 and made URL-safe. Any
653             string which only contains characters matching the set C<A-Za-z0-9_->
654             is acceptable but beware that if you submit a job with a
655             user-specified ID and it has previously been used the job will be
656             rejected.
657              
658             =item subtime
659              
660             This is the time of submission for a job, it only really has meaning
661             from the point-of-view of the build system. Jobs are built in order of
662             submission time but setting this before submitting a job will not have
663             any effect on the sequence in which jobs will be built.
664              
665             =item submitter
666              
667             This is the user name of the submitter. Currently it is taken from the
668             ownership of the submitted job directory. If a move was made to
669             digitally-signed build files then the submitter attribute could
670             reflect that instead. It is not used for any authorization checks, the
671             submitter is purely used for tracking jobs so that users can easily
672             query the status of their own jobs.
673              
674             =item verbose
675              
676             This is a boolean value which controls the verbosity of output to
677             STDERR when class methods are called. By default the methods will not
678             be verbose.
679              
680             =back
681              
682             =head1 SUBROUTINES/METHODS
683              
684             =over 4
685              
686             =item clone
687              
688             This will do a deep clone of a Job object using the C<dclone> function
689             provided by the L<Storable> module.
690              
691             =item new()
692              
693             This will create a new Job object. You must specify the package bucket.
694              
695             =item new_from_yamlfile( $file, $dir )
696              
697             This will load an object from the data stored in the meta-file. You
698             must also specify the directory name, that will then be used to set
699             the C<directory> attribute for the Job and the C<basedir> attribute
700             for the Source package objects. Any setting of the C<directory> and
701             C<yamlfile> attributes in the meta-file are ignored and reset to the
702             passed in arguments. Only attributes which have the
703             C<PkgForge::Serialise> trait will be loaded.
704              
705             =item new_from_dir($dir)
706              
707             This creates a new Job object based on the meta-file and packages
708             stored within the specified directory. It uses C<new_from_yamlfile> to
709             load the meta-file and set the C<directory> attributes appropriately.
710              
711             =item new_from_qentry($qentry)
712              
713             This creates a new Job object from the information stored in a
714             L<PkgForge::Queue::Entry> object. The C<new_from_dir> method is
715             used with the Queue::Entry C<path> attribute. The C<submitter> and
716             C<subtime> attributes are set for the Job based on the values in the
717             Queue::Entry object.
718              
719             =item overdue($timeout)
720              
721             This takes a timeout, in seconds, and returns a boolean value which
722             signifies whether or not the Job is more than that many seconds old.
723              
724             =item process_build_targets(@platforms)
725              
726             This takes a list of available, active, platforms, each entry in the
727             list is a reference to a hash which has values for name, arch and
728             auto. The arch is the architecture, e.g. C<i386> or C<x86_64>. The
729             C<auto> value is a boolean which shows whether the platform should be
730             added automatically or only when explicitly requested.
731              
732             The method returns a list of requested platforms. Each entry in the
733             incoming and returned lists is a pair of platform name and
734             architecture. For example:
735              
736             [ ['sl5','i386' ], ['sl5','x86_64'] ]
737              
738             This is basically just a convenience method with does the work of
739             C<process_platforms> and C<process_archs> in one step. See the
740             documentation below for details of how the processing is done.
741              
742             =item process_platforms(\@all, \@auto)
743              
744             This method takes references to two lists of platform names. The first
745             is the complete set of platforms and the second is the set of
746             platforms which should be added automatically. The two sets may well
747             be identical. Platforms which are only in the 'auto' set will only be
748             added if explicitly requested.
749              
750             This method uses the platform lists to process the rules in the
751             C<platforms> attribute, it then returns a list of requested
752             platforms. See the documentation above on the C<platforms> attribute
753             for full details on how to write the rules.
754              
755             For example, if the C<platforms> attribute is set to:
756              
757             [ "all", "!el5" ]
758              
759             and the platforms list passed in as an argument is:
760              
761             ( "el5", "f12" )
762              
763             then the returned list is:
764              
765             ("f12")
766              
767             =item process_archs(\@archs)
768              
769             This takes a reference to a list of available archs and uses them to
770             process the rules in the C<archs> attribute, it then returns a list of
771             requested archs. See the documentation above on the C<archs> attribute
772             for full details on how to write the rules.
773              
774             For example, if the C<archs> attribute is set to:
775              
776             [ "all", "!i386" ]
777              
778             and the archs list passed in as an argument is:
779              
780             ( "i386", "x86_64" )
781              
782             then the returned list is:
783              
784             ("x86_64")
785              
786             =item store_in_yamlfile([$file])
787              
788             This will save an object to the meta-file. If the file name is not
789             passed in as an argument then the C<yamlfile> attribute will be
790             examined. This method will fail if no file is specified through either
791             route. The C<directory> and C<yamlfile> attributes for the Job and the
792             C<basedir> attribute for the packages are not stored into the
793             meta-file. Only attributes which have the C<PkgForge::Serialise> trait
794             will be stored.
795              
796              
797             =item transfer($target_dir)
798              
799             This will take a Job stored in one directory and copy it all to a new
800             target directory. The Job will be stored using the C<store_in_yamlfile>
801             method. Once the copy is complete it calls C<validate> to ensure that
802             the copied Job is correct. If anything fails then the target directory
803             will be erased. If the transfer succeeds then a new Job object will be
804             returned which represents the copy.
805              
806             =item validate
807              
808             This method validates the state of the Job. It requires that there are
809             Source packages, checks the SHA1 sum for each package and calls the
810             C<validate> method on each package. If anything fails then the method
811             will die with an appropriate message. If the method succeeds then a
812             boolean true value will be returned.
813              
814             =item scrub($options)
815              
816             This method will erase the directory associated with this build
817             job. Note that it also blows away the object since it no longer has
818             any physical meaning once the directory is gone. Internally this uses
819             the C<remove_tree> subroutine provided by L<PkgForge::Utils>. It is
820             possible, optionally, to pass in a reference to a hash of options to
821             control how the C<remove_tree> subroutine functions.
822              
823             =item update_job_size
824              
825             This will recalculate the job size by summing the sizes of all the
826             source packages. It is not normally necessary to do this manually as
827             it will be updated automatically whenever the packages list is
828             altered.
829              
830             =back
831              
832             =head1 DEPENDENCIES
833              
834             This module is powered by L<Moose> and uses L<MooseX::Types>. It also
835             requires L<Data::UUID::Base64URLSafe> for UUID generation,
836             L<UNIVERSAL::require> for loading source package modules,
837             l<YAML::Syck> and L<Data::Structure::Util> for reading the build files
838             and converting them back into Job objects.
839              
840             =head1 SEE ALSO
841              
842             L<PkgForge>, L<PkgForge::Source>, L<PkgForge::Utils>
843             and L<PkgForge::Types>
844              
845             =head1 PLATFORMS
846              
847             This is the list of platforms on which we have tested this
848             software. We expect this software to work on any Unix-like platform
849             which is supported by Perl.
850              
851             ScientificLinux5, Fedora13
852              
853             =head1 BUGS AND LIMITATIONS
854              
855             Please report any bugs or problems (or praise!) to bugs@lcfg.org,
856             feedback and patches are also always very welcome.
857              
858             =head1 AUTHOR
859              
860             Stephen Quinney <squinney@inf.ed.ac.uk>
861              
862             =head1 LICENSE AND COPYRIGHT
863              
864             Copyright (C) 2010 University of Edinburgh. All rights reserved.
865              
866             This library is free software; you can redistribute it and/or modify
867             it under the terms of the GPL, version 2 or later.
868              
869             =cut