X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Debbugs%2FStatus.pm;h=73a1dbc108f9d7af6d844a0120ed06e5d35c2de3;hb=90aed5cbeb0a7cf6bfbbe4ed7ba9e2268dd50f6b;hp=5fb5413dcdb3fb1a1e3323ec29a9b45d77da6a4c;hpb=580822d029b7914604e00b23974bd1baa8410bed;p=debbugs.git diff --git a/Debbugs/Status.pm b/Debbugs/Status.pm index 5fb5413..73a1dbc 100644 --- a/Debbugs/Status.pm +++ b/Debbugs/Status.pm @@ -159,17 +159,24 @@ sub read_bug{ die "One of bug or summary must be passed to read_bug" if not exists $param{bug} and not exists $param{summary}; my $status; + my $log; + my $location; if (not defined $param{summary}) { - my ($lref, $location) = @param{qw(bug location)}; + my $lref; + ($lref,$location) = @param{qw(bug location)}; if (not defined $location) { $location = getbuglocation($lref,'summary'); return undef if not defined $location; } $status = getbugcomponent($lref, 'summary', $location); + $log = getbugcomponent($lref, 'log' , $location); return undef unless defined $status; } else { $status = $param{summary}; + $log = $status; + $log =~ s/\.summary$/.log/; + ($location) = $status =~ m/(db-h|db|archive)/; } my $status_fh = new IO::File $status, 'r' or warn "Unable to open $status for reading: $!" and return undef; @@ -216,6 +223,9 @@ sub read_bug{ $data{$field} = decode_rfc1522($data{$field}); } } + # Add log last modified time + $data{log_modified} = (stat($log))[9]; + $data{location} = $location; return \%data; } @@ -519,24 +529,20 @@ sub removefixedversions { my $version = shift; my $isbinary = shift; return unless defined $version; - undef $package if $package =~ m[(?:\s|/)]; - my $source = $package; - - if (defined $package and $isbinary) { - my @srcinfo = binarytosource($package, $version, undef); - if (@srcinfo) { - # We know the source package(s). Use a fully-qualified version. - removefixedversions($data, $_->[0], $_->[1], '') foreach @srcinfo; - return; - } - # Otherwise, an unqualified version will have to do. - undef $source; - } foreach my $ver (split /[,\s]+/, $version) { - my $sver = defined($source) ? "$source/$ver" : ''; - @{$data->{fixed_versions}} = - grep { $_ ne $ver and $_ ne $sver } @{$data->{fixed_versions}}; + if ($ver =~ m{/}) { + # fully qualified version + @{$data->{fixed_versions}} = + grep {$_ ne $ver} + @{$data->{fixed_versions}}; + } + else { + # non qualified version; delete all matchers + @{$data->{fixed_versions}} = + grep {$_ !~ m[(?:^|/)\Q$ver\E$]} + @{$data->{fixed_versions}}; + } } } @@ -610,17 +616,34 @@ sub bug_archiveable{ my $status = $param{status}; if (not exists $param{status} or not defined $status) { $status = read_bug(bug=>$param{bug}); - return undef if not defined $status; + if (not defined $status) { + print STDERR "Cannot archive $param{bug} because it does not exist\n" if $DEBUG; + return undef; + } } # Bugs can be archived if they are # 1. Closed - return $cannot_archive if not defined $status->{done} or not length $status->{done}; + if (not defined $status->{done} or not length $status->{done}) { + print STDERR "Cannot archive $param{bug} because it is not done\n" if $DEBUG; + return $cannot_archive + } # If we just are checking if the bug can be archived, we'll not even bother # checking the versioning information if the bug has been -done for less than 28 days. + my $log_file = getbugcomponent($param{bug},'log'); + if (not defined $log_file) { + print STDERR "Cannot archive $param{bug} because the log doesn't exist\n" if $DEBUG; + return $cannot_archive; + } + my $max_log_age = max(map {$config{remove_age} - -M $_} + $log_file, map {my $log = getbugcomponent($_,'log'); + defined $log ? ($log) : (); + } + split / /, $status->{mergedwith} + ); if (not $param{days_until} and not $param{ignore_time} - and $config{remove_age} > - -M getbugcomponent($param{bug},'log') + and $max_log_age > 0 ) { + print STDERR "Cannot archive $param{bug} because of time\n" if $DEBUG; return $cannot_archive; } # At this point, we have to get the versioning information for this bug. @@ -628,6 +651,10 @@ sub bug_archiveable{ # tags set, we assume a default set, otherwise we use the tags the bug # has set. + # In cases where we are assuming a default set, if the severity + # is strong, we use the strong severity default; otherwise, we + # use the normal default. + # There must be fixed_versions for us to look at the versioning # information my $min_fixed_time = time; @@ -637,11 +664,20 @@ sub bug_archiveable{ @dist_tags{@{$config{removal_distribution_tags}}} = (1) x @{$config{removal_distribution_tags}}; my %dists; - @dists{@{$config{removal_default_distribution_tags}}} = - (1) x @{$config{removal_default_distribution_tags}}; for my $tag (split ' ', ($status->{tags}||'')) { - next unless $dist_tags{$tag}; - $dists{$tag} = 1; + next unless exists $config{distribution_aliases}{$tag}; + next unless $dist_tags{$config{distribution_aliases}{$tag}}; + $dists{$config{distribution_aliases}{$tag}} = 1; + } + if (not keys %dists) { + if (isstrongseverity($status->{severity})) { + @dists{@{$config{removal_strong_severity_default_distribution_tags}}} = + (1) x @{$config{removal_strong_severity_default_distribution_tags}}; + } + else { + @dists{@{$config{removal_default_distribution_tags}}} = + (1) x @{$config{removal_default_distribution_tags}}; + } } my %source_versions; my @sourceversions = get_versions(package => $status->{package}, @@ -658,6 +694,7 @@ sub bug_archiveable{ version_cache => $version_cache, package => $status->{package}, )) { + print STDERR "Cannot archive $param{bug} because it's found\n" if $DEBUG; return $cannot_archive; } # Since the bug has at least been fixed in the architectures @@ -688,15 +725,16 @@ sub bug_archiveable{ last if $buggy eq 'found'; $min_fixed_time = min($time_versions{$version},$min_fixed_time); } - $min_archive_days = max($min_archive_days,ceil((time - $min_fixed_time)/(60*60*24))); + $min_archive_days = max($min_archive_days,ceil($config{remove_age} - (time - $min_fixed_time)/(60*60*24))); } # If $param{ignore_time}, then we should ignore time. if ($param{ignore_time}) { return $param{days_until}?0:1; } # 6. at least 28 days have passed since the last action has occured or the bug was closed - my $age = ceil($config{remove_age} - -M getbugcomponent($param{bug},'log')); + my $age = ceil($max_log_age); if ($age > 0 or $min_archive_days > 0) { + print STDERR "Cannot archive $param{bug} because not enough days have passed\n" if $DEBUG; return $param{days_until}?max($age,$min_archive_days):0; } else { @@ -729,12 +767,17 @@ currently not correctly implemented. =item arch -- optional architecture(s) to check package status at -=item usertags -- optional hashref of usertags +=item bugusertags -- optional hashref of bugusertags =item sourceversion -- optional arrayref of source/version; overrides dist, arch, and version. [The entries in this array must be in the "source/version" format.] Eventually this can be used to for caching. +=item indicatesource -- if true, indicate which source packages this +bug could belong to. Defaults to false. [Note that eventually we will +properly allow bugs that only affect a source package, and this will +become always on.] + =back Note: Currently the version information is cached; this needs to be @@ -765,12 +808,15 @@ sub get_bug_status { arch => {type => SCALAR|ARRAYREF, optional => 1, }, - usertags => {type => HASHREF, - optional => 1, - }, + bugusertags => {type => HASHREF, + optional => 1, + }, sourceversions => {type => ARRAYREF, optional => 1, }, + indicatesource => {type => BOOLEAN, + default => 0, + }, }, ); my %status; @@ -787,20 +833,26 @@ sub get_bug_status { } else { my $location = getbuglocation($param{bug}, 'summary'); - return {} if not length $location; + return {} if not defined $location or not length $location; %status = %{ readbug( $param{bug}, $location ) }; } $status{id} = $param{bug}; - if (defined $param{usertags}{$param{bug}}) { + if (defined $param{bugusertags}{$param{bug}}) { $status{keywords} = "" unless defined $status{keywords}; $status{keywords} .= " " unless $status{keywords} eq ""; - $status{keywords} .= join(" ", @{$param{usertags}{$param{bug}}}); + $status{keywords} .= join(" ", @{$param{bugusertags}{$param{bug}}}); } $status{tags} = $status{keywords}; my %tags = map { $_ => 1 } split ' ', $status{tags}; $status{"package"} =~ s/\s*$//; + if ($param{indicatesource} and $status{package} ne '') { + $status{source} = join(', ',binarytosource($status{package})); + } + else { + $status{source} = 'unknown'; + } $status{"package"} = 'unknown' if ($status{"package"} eq ''); $status{"severity"} = 'normal' if ($status{"severity"} eq ''); @@ -894,32 +946,42 @@ sub bug_presence { } my @sourceversions; + my $pseudo_desc = getpseudodesc(); if (not exists $param{sourceversions}) { my %sourceversions; - if (defined $param{version}) { + # pseudopackages do not have source versions by definition. + if (exists $pseudo_desc->{$status{package}}) { + # do nothing. + } + elsif (defined $param{version}) { foreach my $arch (make_list($param{arch})) { - my @temp = makesourceversions($status{package}, - $arch, - make_list($param{version}) - ); - @sourceversions{@temp} = (1) x @temp; + for my $package (split /\s*,\s*/, $status{package}) { + my @temp = makesourceversions($package, + $arch, + make_list($param{version}) + ); + @sourceversions{@temp} = (1) x @temp; + } } } elsif (defined $param{dist}) { foreach my $arch (make_list($param{arch})) { my @versions; - foreach my $dist (make_list($param{dist})) { - push @versions, getversions($status{package}, $dist, $arch); + for my $package (split /\s*,\s*/, $status{package}) { + foreach my $dist (make_list($param{dist})) { + push @versions, getversions($package, $dist, $arch); + } + my @temp = makesourceversions($package, + $arch, + @versions + ); + @sourceversions{@temp} = (1) x @temp; } - my @temp = makesourceversions($status{package}, - $arch, - @versions - ); - @sourceversions{@temp} = (1) x @temp; } } # TODO: This should probably be handled further out for efficiency and # for more ease of distinguishing between pkg= and src= queries. + # DLA: src= queries should just pass arch=source, and they'll be happy. @sourceversions = keys %sourceversions; } else { @@ -935,7 +997,8 @@ sub bug_presence { version_cache => $version_cache, ); } - elsif (defined $param{dist}) { + elsif (defined $param{dist} and + not exists $pseudo_desc->{$status{package}}) { return 'absent'; } if (length($status{done}) and @@ -991,18 +1054,20 @@ sub max_buggy{ # Resolve bugginess states (we might be looking at multiple # architectures, say). Found wins, then fixed, then absent. my $maxbuggy = 'absent'; - for my $version (@{$param{sourceversions}}) { - my $buggy = buggy(bug => $param{bug}, - version => $version, - found => $param{found}, - fixed => $param{fixed}, - version_cache => $param{version_cache}, - package => $param{package}, - ); - if ($buggy eq 'found') { - return 'found'; - } elsif ($buggy eq 'fixed') { - $maxbuggy = 'fixed'; + for my $package (split /\s*,\s*/, $param{package}) { + for my $version (@{$param{sourceversions}}) { + my $buggy = buggy(bug => $param{bug}, + version => $version, + found => $param{found}, + fixed => $param{fixed}, + version_cache => $param{version_cache}, + package => $package, + ); + if ($buggy eq 'found') { + return 'found'; + } elsif ($buggy eq 'fixed') { + $maxbuggy = 'fixed'; + } } } return $maxbuggy; @@ -1060,23 +1125,31 @@ sub buggy { ); } if ($param{version} !~ m{/}) { - $param{version} = makesourceversions($param{package},undef, - $param{version} - ); + my ($version) = makesourceversions($param{package},undef, + $param{version} + ); + $param{version} = $version if defined $version; } # Figure out which source packages we need my %sources; @sources{map {m{(.+)/}; $1} @found} = (1) x @found; @sources{map {m{(.+)/}; $1} @fixed} = (1) x @fixed; - @sources{map {m{(.+)/}; $1} $param{version}} = 1; + @sources{map {m{(.+)/}; $1} $param{version}} = 1 if + $param{version} =~ m{/}; my $version; if (not defined $param{version_cache} or not exists $param{version_cache}{join(',',sort keys %sources)}) { $version = Debbugs::Versions->new(\&Debbugs::Versions::Dpkg::vercmp); foreach my $source (keys %sources) { my $srchash = substr $source, 0, 1; - my $version_fh = new IO::File "$config{version_packages_dir}/$srchash/$source", 'r' or - warn "Unable to open $config{version_packages_dir}/$srchash/$source: $!" and next; + my $version_fh = IO::File->new("$config{version_packages_dir}/$srchash/$source", 'r'); + if (not defined $version_fh) { + # We only want to warn if it's a package which actually has a maintainer + my $maints = getmaintainers(); + next if not exists $maints->{$source}; + warn "Bug $param{bug}: unable to open $config{version_packages_dir}/$srchash/$source: $!"; + next; + } $version->load($version_fh); } if (defined $param{version_cache}) { @@ -1091,7 +1164,8 @@ sub buggy { sub isstrongseverity { my $severity = shift; - $severity = $config{default_severity} if $severity eq ''; + $severity = $config{default_severity} if + not defined $severity or $severity eq ''; return grep { $_ eq $severity } @{$config{strong_severities}}; } @@ -1101,77 +1175,95 @@ sub isstrongseverity { =cut sub update_realtime { - my ($file, $bug, $new) = @_; + my ($file, %bugs) = @_; # update realtime index.db - open(IDXDB, "<$file") or die "Couldn't open $file"; - open(IDXNEW, ">$file.new"); + return () unless keys %bugs; + my $idx_old = IO::File->new($file,'r') + or die "Couldn't open ${file}: $!"; + my $idx_new = IO::File->new($file.'.new','w') + or die "Couldn't open ${file}.new: $!"; + my $min_bug = min(keys %bugs); my $line; my @line; - while($line = ) { - @line = split /\s/, $line; - last if ($line[1] >= $bug); - print IDXNEW $line; - $line = ""; + my %changed_bugs; + while($line = <$idx_old>) { + @line = split /\s/, $line; + # Two cases; replacing existing line or adding new line + if (exists $bugs{$line[1]}) { + my $new = $bugs{$line[1]}; + delete $bugs{$line[1]}; + $min_bug = min(keys %bugs); + if ($new eq "NOCHANGE") { + print {$idx_new} $line; + $changed_bugs{$line[1]} = $line; + } elsif ($new eq "REMOVE") { + $changed_bugs{$line[1]} = $line; + } else { + print {$idx_new} $new; + $changed_bugs{$line[1]} = $line; + } + } + else { + while ($line[1] > $min_bug) { + print {$idx_new} $bugs{$min_bug}; + delete $bugs{$min_bug}; + last unless keys %bugs; + $min_bug = min(keys %bugs); + } + print {$idx_new} $line; + } + last unless keys %bugs; } + print {$idx_new} map {$bugs{$_}} sort keys %bugs; - if ($new eq "NOCHANGE") { - print IDXNEW $line if ($line ne "" and $line[1] == $bug); - } elsif ($new eq "REMOVE") { - 0; - } else { - print IDXNEW $new; - } - if (defined $line and $line ne "" and @line and $line[1] > $bug) { - print IDXNEW $line; - $line = ""; - } - - print IDXNEW while(); + print {$idx_new} <$idx_old>; - close(IDXNEW); - close(IDXDB); + close($idx_new); + close($idx_old); rename("$file.new", $file); - return $line; + return %changed_bugs; } sub bughook_archive { - my $ref = shift; + my @refs = @_; &filelock("debbugs.trace.lock"); - &appendfile("debbugs.trace","archive $ref\n"); - my $line = update_realtime( - "$config{spool_dir}/index.db.realtime", - $ref, - "REMOVE"); + &appendfile("debbugs.trace","archive ".join(',',@refs)."\n"); + my %bugs = update_realtime("$config{spool_dir}/index.db.realtime", + map{($_,'REMOVE')} @refs); update_realtime("$config{spool_dir}/index.archive.realtime", - $ref, $line); + %bugs); &unfilelock; } sub bughook { - my ( $type, $ref, $data ) = @_; + my ( $type, %bugs_temp ) = @_; &filelock("debbugs.trace.lock"); - &appendfile("debbugs.trace","$type $ref\n",makestatus($data, 1)); - - my $whendone = "open"; - my $severity = $config{default_severity}; - (my $pkglist = $data->{package}) =~ s/[,\s]+/,/g; - $pkglist =~ s/^,+//; - $pkglist =~ s/,+$//; - $whendone = "forwarded" if defined $data->{forwarded} and length $data->{forwarded}; - $whendone = "done" if defined $data->{done} and length $data->{done}; - $severity = $data->{severity} if length $data->{severity}; - - my $k = sprintf "%s %d %d %s [%s] %s %s\n", - $pkglist, $ref, $data->{date}, $whendone, - $data->{originator}, $severity, $data->{keywords}; - - update_realtime("$config{spool_dir}/index.db.realtime", $ref, $k); + my %bugs; + for my $bug (keys %bugs_temp) { + my $data = $bugs_temp{$bug}; + &appendfile("debbugs.trace","$type $bug\n",makestatus($data, 1)); + + my $whendone = "open"; + my $severity = $config{default_severity}; + (my $pkglist = $data->{package}) =~ s/[,\s]+/,/g; + $pkglist =~ s/^,+//; + $pkglist =~ s/,+$//; + $whendone = "forwarded" if defined $data->{forwarded} and length $data->{forwarded}; + $whendone = "done" if defined $data->{done} and length $data->{done}; + $severity = $data->{severity} if length $data->{severity}; + + my $k = sprintf "%s %d %d %s [%s] %s %s\n", + $pkglist, $bug, $data->{date}, $whendone, + $data->{originator}, $severity, $data->{keywords}; + $bugs{$bug} = $k; + } + update_realtime("$config{spool_dir}/index.db.realtime", %bugs); &unfilelock; }