X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Debbugs%2FControl.pm;h=30a642b0bff22214fed508fa0423095e2a0f5dcf;hb=c19c50af1e0b3680fe2edb6c4d307b9460b6fff0;hp=1003e9c1662ece47a05f43e6282ffe63c8cf39c2;hpb=be4f44cfbf65f509a237926f168fe6ffc19ccf96;p=debbugs.git diff --git a/Debbugs/Control.pm b/Debbugs/Control.pm index 1003e9c..30a642b 100644 --- a/Debbugs/Control.pm +++ b/Debbugs/Control.pm @@ -64,7 +64,7 @@ the above information is faked, and appended to the log file. When it is true, the above options must be present, and their values are used. -=head1 FUNCTIONS +=head1 GENERAL FUNCTIONS =cut @@ -78,37 +78,56 @@ BEGIN{ $DEBUG = 0 unless defined $DEBUG; @EXPORT = (); - %EXPORT_TAGS = (archive => [qw(bug_archive bug_unarchive), + %EXPORT_TAGS = (owner => [qw(owner)], + archive => [qw(bug_archive bug_unarchive), ], log => [qw(append_action_to_log), ], ); @EXPORT_OK = (); - Exporter::export_ok_tags(qw(archive log)); + Exporter::export_ok_tags(keys %EXPORT_TAGS); $EXPORT_TAGS{all} = [@EXPORT_OK]; } use Debbugs::Config qw(:config); -use Debbugs::Common qw(:lock buglog make_list get_hashname); -use Debbugs::Status qw(bug_archiveable :read :hook); +use Debbugs::Common qw(:lock buglog :misc get_hashname); +use Debbugs::Status qw(bug_archiveable :read :hook writebug); use Debbugs::CGI qw(html_escape); use Debbugs::Log qw(:misc); +use Debbugs::Recipients qw(:add); use Params::Validate qw(validate_with :types); use File::Path qw(mkpath); use IO::File; -# These are a set of options which are common to all of these functions +use Debbugs::Text qw(:templates); -my %common_options = (debug => {type => SCALARREF, +use Debbugs::Mail qw(rfc822_date); + +use POSIX qw(strftime); + +use Carp; + +# These are a set of options which are common to all of these functions + +my %common_options = (debug => {type => SCALARREF|HANDLE, optional => 1, }, - transcript => {type => SCALARREF, + transcript => {type => SCALARREF|HANDLE, optional => 1, }, affected_bugs => {type => HASHREF, optional => 1, }, + affected_packages => {type => HASHREF, + optional => 1, + }, + recipients => {type => HASHREF, + default => {}, + }, + limit => {type => HASHREF, + default => {}, + }, ); @@ -137,6 +156,127 @@ my %append_action_options = ); +# this is just a generic stub for Debbugs::Control functions. +# sub foo { +# my %param = validate_with(params => \@_, +# spec => {bug => {type => SCALAR, +# regex => qr/^\d+$/, +# }, +# # specific options here +# %common_options, +# %append_action_options, +# }, +# ); +# our $locks = 0; +# $locks = 0; +# local $SIG{__DIE__} = sub { +# if ($locks) { +# for (1..$locks) { unfilelock(); } +# $locks = 0; +# } +# }; +# my ($debug,$transcript) = __handle_debug_transcript(%param); +# my (@data); +# ($locks, @data) = lock_read_all_merged_bugs($param{bug}); +# __handle_affected_packages(data => \@data,%param); +# add_recipients(data => \@data, +# recipients => $param{recipients} +# ); +# } + +=head1 OWNER FUNCTIONS + +=head2 owner + + eval { + owner(bug => $ref, + transcript => $transcript, + ($dl > 0 ? (debug => $transcript):()), + requester => $header{from}, + request_addr => $controlrequestaddr, + message => \@log, + recipients => \%recipients, + owner => undef, + ); + }; + if ($@) { + $errors++; + print {$transcript} "Failed to mark $ref as having an owner: $@"; + } + +Handles all setting of the owner field; given an owner of undef or of +no length, indicates that a bug is not owned by anyone. + +=cut + +sub owner { + my %param = validate_with(params => \@_, + spec => {bug => {type => SCALAR, + regex => qr/^\d+$/, + }, + owner => {type => SCALAR|UNDEF, + }, + %common_options, + %append_action_options, + }, + ); + our $locks = 0; + $locks = 0; + local $SIG{__DIE__} = sub { + if ($locks) { + for (1..$locks) { unfilelock(); } + $locks = 0; + } + }; + my ($debug,$transcript) = __handle_debug_transcript(%param); + my (@data); + ($locks, @data) = lock_read_all_merged_bugs($param{bug}); + __handle_affected_packages(data => \@data,%param); + @data and defined $data[0] or die "No bug found for $param{bug}"; + add_recipients(data => \@data, + recipients => $param{recipients} + ); + my $action = ''; + for my $data (@data) { + print {$debug} "Going to change owner to '".(defined $param{owner}?$param{owner}:'(going to unset it)')."'\n"; + print {$debug} "Owner is currently '$data->{owner}' for bug $data->{bug_num}\n"; + if (not defined $param{owner} or not length $param{owner}) { + $param{owner} = ''; + $action = "Removed annotation that $config{bug} was owned by " . + "$data->{owner}."; + } + else { + if (length $data->{owner}) { + $action = "Owner changed from $data->{owner} to $param{owner}."; + } + else { + $action = "Owner recorded as $param{owner}." + } + } + $data->{owner} = $param{owner}; + append_action_to_log(bug => $data->{bug_num}, + get_lock => 0, + __return_append_to_log_options( + %param, + action => $action, + ), + ) + if not exists $param{append_log} or $param{append_log}; + writebug($data->{bug_num},$data); + print {$transcript} "$action\n"; + add_recipients(data => $data, + recipients => $param{recipients}, + ); + } + if ($locks) { + for (1..$locks) { unfilelock(); } + } +} + + +=head1 ARCHIVE FUNCTIONS + + =head2 bug_archive my $error = ''; @@ -156,16 +296,35 @@ my %append_action_options = This routine archives a bug +=over + +=item bug -- bug number + +=item check_archiveable -- check wether a bug is archiveable before +archiving; defaults to 1 + +=item archive_unarchived -- whether to archive bugs which have not +previously been archived; defaults to 1. [Set to 0 when used from +control@] + +=item ignore_time -- whether to ignore time constraints when archiving +a bug; defaults to 0. + +=back + =cut sub bug_archive { my %param = validate_with(params => \@_, spec => {bug => {type => SCALAR, - regex => qr/^\d+/, + regex => qr/^\d+$/, }, check_archiveable => {type => BOOLEAN, default => 1, }, + archive_unarchived => {type => BOOLEAN, + default => 1, + }, ignore_time => {type => BOOLEAN, default => 0, }, @@ -173,6 +332,14 @@ sub bug_archive { %append_action_options, }, ); + our $locks = 0; + $locks = 0; + local $SIG{__DIE__} = sub { + if ($locks) { + for (1..$locks) { unfilelock(); } + $locks = 0; + } + }; my $action = "$config{bug} archived."; my ($debug,$transcript) = __handle_debug_transcript(%param); if ($param{check_archiveable} and @@ -183,40 +350,31 @@ sub bug_archive { die "Bug $param{bug} cannot be archived"; } print {$debug} "$param{bug} considering\n"; - my ($locks, $data) = lockreadbugmerge($param{bug}); + my (@data); + ($locks, @data) = lock_read_all_merged_bugs($param{bug}); + __handle_affected_packages(data => \@data,%param); print {$debug} "$param{bug} read $locks\n"; - defined $data or die "No bug found for $param{bug}"; - print {$debug} "$param{bug} read ok (done $data->{done})\n"; + @data and defined $data[0] or die "No bug found for $param{bug}"; print {$debug} "$param{bug} read done\n"; - my @bugs = ($param{bug}); - # my %bugs; - # @bugs{@bugs} = (1) x @bugs; - if (length($data->{mergedwith})) { - push(@bugs,split / /,$data->{mergedwith}); + + if (not $param{archive_unarchived} and + not exists $data[0]{unarchived} + ) { + print {$transcript} "$param{bug} has not been archived previously\n"; + die "$param{bug} has not been archived previously"; } + add_recipients(recipients => $param{recipients}, + data => \@data, + ); + my @bugs = map {$_->{bug_num}} @data; print {$debug} "$param{bug} bugs ".join(' ',@bugs)."\n"; for my $bug (@bugs) { - my $newdata; - print {$debug} "$param{bug} $bug check\n"; - if ($bug != $param{bug}) { - print {$debug} "$param{bug} $bug reading\n"; - $newdata = lockreadbug($bug) || die "huh $bug ?"; - print {$debug} "$param{bug} $bug read ok\n"; - $locks++; - } else { - $newdata = $data; - } - print {$debug} "$param{bug} $bug read/not\n"; - my $expectmerge= join(' ',grep($_ != $bug, sort { $a <=> $b } @bugs)); - $newdata->{mergedwith} eq $expectmerge || - die "$param{bug} differs from $bug: ($newdata->{mergedwith}) vs. ($expectmerge) (".join(' ',@bugs).")"; - print {$debug} "$param{bug} $bug merge-ok\n"; - if ($param{check_archiveable}) { - die "Bug $bug cannot be archived (but $param{bug} can?)" - unless bug_archiveable(bug=>$bug, - ignore_time => $param{ignore_time}, - ); - } + if ($param{check_archiveable}) { + die "Bug $bug cannot be archived (but $param{bug} can?)" + unless bug_archiveable(bug=>$bug, + ignore_time => $param{ignore_time}, + ); + } } # If we get here, we can archive/remove this bug print {$debug} "$param{bug} removing\n"; @@ -227,26 +385,25 @@ sub bug_archive { append_action_to_log(bug => $bug, get_lock => 0, __return_append_to_log_options( - (map {exists $param{$_}?($_,$param{$_}):()} - keys %append_action_options, - ), + %param, action => $action, ) ) if not exists $param{append_log} or $param{append_log}; - my @files_to_remove = map {s#db-h/$dir/##; $_} glob("db-h/$dir/$bug.*"); + my @files_to_remove = map {s#$config{spool_dir}/db-h/$dir/##; $_} glob("$config{spool_dir}/db-h/$dir/$bug.*"); if ($config{save_old_bugs}) { - mkpath("archive/$dir"); + mkpath("$config{spool_dir}/archive/$dir"); foreach my $file (@files_to_remove) { - link( "db-h/$dir/$file", "archive/$dir/$file" ) || copy( "db-h/$dir/$file", "archive/$dir/$file" ); + link( "$config{spool_dir}/db-h/$dir/$file", "$config{spool_dir}/archive/$dir/$file" ) or + copy( "$config{spool_dir}/db-h/$dir/$file", "$config{spool_dir}/archive/$dir/$file" ); } print {$transcript} "archived $bug to archive/$dir (from $param{bug})\n"; } - unlink(map {"db-h/$dir/$_"} @files_to_remove); + unlink(map {"$config{spool_dir}/db-h/$dir/$_"} @files_to_remove); print {$transcript} "deleted $bug (from $param{bug})\n"; - bughook_archive($bug); } + bughook_archive(@bugs); if (exists $param{bugs_affected}) { @{$param{bugs_affected}}{@bugs} = (1) x @bugs; } @@ -285,58 +442,41 @@ sub bug_unarchive { %append_action_options, }, ); + our $locks = 0; + local $SIG{__DIE__} = sub { + if ($locks) { + for (1..$locks) { unfilelock(); } + $locks = 0; + } + }; my $action = "$config{bug} unarchived."; my ($debug,$transcript) = __handle_debug_transcript(%param); print {$debug} "$param{bug} considering\n"; - my ($locks, $data) = lockreadbugmerge($param{bug},'archive'); + my @data = (); + ($locks, @data) = lock_read_all_merged_bugs($param{bug},'archive'); + __handle_affected_packages(data => \@data,%param); print {$debug} "$param{bug} read $locks\n"; - if (not defined $data) { - print {$transcript} "No bug found for $param{bug}\n"; - die "No bug found for $param{bug}"; + if (not @data or not defined $data[0]) { + print {$transcript} "No bug found for $param{bug}\n"; + die "No bug found for $param{bug}"; } - print {$debug} "$param{bug} read ok (done $data->{done})\n"; print {$debug} "$param{bug} read done\n"; - my @bugs = ($param{bug}); - # my %bugs; - # @bugs{@bugs} = (1) x @bugs; - if (length($data->{mergedwith})) { - push(@bugs,split / /,$data->{mergedwith}); - } + my @bugs = map {$_->{bug_num}} @data; print {$debug} "$param{bug} bugs ".join(' ',@bugs)."\n"; - for my $bug (@bugs) { - my $newdata; - print {$debug} "$param{bug} $bug check\n"; - if ($bug != $param{bug}) { - print {$debug} "$param{bug} $bug reading\n"; - $newdata = lockreadbug($bug,'archive') or die "huh $bug ?"; - print {$debug} "$param{bug} $bug read ok\n"; - $locks++; - } else { - $newdata = $data; - } - print {$debug} "$param{bug} $bug read/not\n"; - my $expectmerge= join(' ',grep($_ != $bug, sort { $a <=> $b } @bugs)); - if ($newdata->{mergedwith} ne $expectmerge ) { - print {$transcript} "$param{bug} differs from $bug: ($newdata->{mergedwith}) vs. ($expectmerge) (@bugs)"; - die "$param{bug} differs from $bug: ($newdata->{mergedwith}) vs. ($expectmerge) (@bugs)"; - } - print {$debug} "$param{bug} $bug merge-ok\n"; - } - # If we get here, we can archive/remove this bug - print {$debug} "$param{bug} removing\n"; + print {$debug} "$param{bug} unarchiving\n"; my @files_to_remove; for my $bug (@bugs) { print {$debug} "$param{bug} removing $bug\n"; my $dir = get_hashname($bug); - my @files_to_copy = map {s#archive/$dir/##; $_} glob("archive/$dir/$bug.*"); + my @files_to_copy = map {s#$config{spool_dir}/archive/$dir/##; $_} glob("$config{spool_dir}/archive/$dir/$bug.*"); mkpath("archive/$dir"); foreach my $file (@files_to_copy) { # die'ing here sucks - link( "archive/$dir/$file", "db-h/$dir/$file" ) or - copy( "archive/$dir/$file", "db-h/$dir/$file" ) or - die "Unable to copy archive/$dir/$file to db-h/$dir/$file"; + link( "$config{spool_dir}/archive/$dir/$file", "$config{spool_dir}/db-h/$dir/$file" ) or + copy( "$config{spool_dir}/archive/$dir/$file", "$config{spool_dir}/db-h/$dir/$file" ) or + die "Unable to copy $config{spool_dir}/archive/$dir/$file to $config{spool_dir}/db-h/$dir/$file"; } - push @files_to_remove, map {"archive/$dir/$_"} @files_to_copy; + push @files_to_remove, map {"$config{spool_dir}/archive/$dir/$_"} @files_to_copy; print {$transcript} "Unarchived $config{bug} $bug\n"; } unlink(@files_to_remove) or die "Unable to unlink bugs"; @@ -351,14 +491,15 @@ sub bug_unarchive { append_action_to_log(bug => $bug, get_lock => 0, __return_append_to_log_options( - (map {exists $param{$_}?($_,$param{$_}):()} - keys %append_action_options, - ), + %param, action => $action, ) ) if not exists $param{append_log} or $param{append_log}; writebug($bug,$newdata); + add_recipients(recipients => $param{recipients}, + data => $newdata, + ); } print {$debug} "$param{bug} unlocking $locks\n"; if ($locks) { @@ -428,11 +569,34 @@ sub append_action_to_log{ =head1 PRIVATE FUNCTIONS +=head2 __handle_affected_packages + + __handle_affected_packages(affected_packages => {}, + data => [@data], + ) + + + +=cut + +sub __handle_affected_packages{ + my %param = validate_with(params => \@_, + spec => {%common_options, + data => {type => ARRAYREF|HASHREF + }, + }, + allow_extra => 1, + ); + for my $data (make_list($param{data})) { + $param{affected_packages}{$data->{package}} = 1; + } +} + =head2 __handle_debug_transcript my ($debug,$transcript) = __handle_debug_transcript(%param); -Returns a debug and transcript IO::Scalar filehandle +Returns a debug and transcript filehandle =cut @@ -442,16 +606,14 @@ sub __handle_debug_transcript{ spec => {%common_options}, allow_extra => 1, ); - my $fake_scalar; - my $debug = IO::Scalar->new(exists $param{debug}?$param{debug}:\$fake_scalar); - my $transcript = IO::Scalar->new(exists $param{transcript}?$param{transcript}:\$fake_scalar); + my $debug = globify_scalar(exists $param{debug}?$param{debug}:undef); + my $transcript = globify_scalar(exists $param{transcript}?$param{transcript}:undef); return ($debug,$transcript); - } sub __return_append_to_log_options{ my %param = @_; - my $action = 'Unknown action'; + my $action = $param{action} if exists $param{action}; if (not exists $param{requester}) { $param{requester} = $config{control_internal_requester}; } @@ -459,31 +621,23 @@ sub __return_append_to_log_options{ $param{request_addr} = $config{control_internal_request_addr}; } if (not exists $param{message}) { - $action = $param{action} if exists $param{action}; - $param{message} = < 'mail/fake_control_message', + variables => {request_addr => $param{request_addr}, + requester => $param{requester}, + date => $date, + action => $action + }, + ); + } + if (not defined $action) { + carp "Undefined action!"; + $action = "unknown action"; } return (action => $action, - %param); + (map {exists $append_action_options{$_}?($_,$param{$_}):()} + keys %param), + ); }