X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=Debbugs%2FStatus.pm;h=72b040f545a2879759d704eaa052227ee4da2c6d;hb=99883e942647482486ad6bf56acdbce4c5c928da;hp=d8a1f05ac2a6f14b626b6635993111b5be1ee09f;hpb=f7cdbf935ade36f9f5c1216e8291de0217452abe;p=debbugs.git diff --git a/Debbugs/Status.pm b/Debbugs/Status.pm index d8a1f05..72b040f 100644 --- a/Debbugs/Status.pm +++ b/Debbugs/Status.pm @@ -193,7 +193,10 @@ sub read_bug{ } # Version 3 is the latest format version currently supported. - return undef if $version > 3; + if ($version > 3) { + warn "Unsupported status version '$version'"; + return undef; + } my %namemap = reverse %fields; for my $line (@lines) { @@ -226,6 +229,7 @@ sub read_bug{ # Add log last modified time $data{log_modified} = (stat($log))[9]; $data{location} = $location; + $data{bug_num} = $param{bug}; return \%data; } @@ -369,17 +373,17 @@ sub writebug { for my $version (keys %outputs) { next if defined $minversion and $version < $minversion; my $status = getbugcomponent($ref, $outputs{$version}, $location); - &quit("can't find location for $ref") unless defined $status; - open(S,"> $status.new") || &quit("opening $status.new: $!"); + die "can't find location for $ref" unless defined $status; + open(S,"> $status.new") || die "opening $status.new: $!"; print(S makestatus($data, $version)) || - &quit("writing $status.new: $!"); - close(S) || &quit("closing $status.new: $!"); + die "writing $status.new: $!"; + close(S) || die "closing $status.new: $!"; if (-e $status) { $change = 'change'; } else { $change = 'new'; } - rename("$status.new",$status) || &quit("installing new $status: $!"); + rename("$status.new",$status) || die "installing new $status: $!"; } # $disablebughook is a bit of a hack to let format migration scripts use @@ -529,24 +533,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}}; + } } } @@ -620,17 +620,44 @@ 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 + } + # Check to make sure that the bug has none of the unremovable tags set + if (@{$config{removal_unremovable_tags}}) { + for my $tag (split ' ', ($status->{tags}||'')) { + if (grep {$tag eq $_} @{$config{removal_unremovable_tags}}) { + print STDERR "Cannot archive $param{bug} because it has an unremovable tag '$tag'\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. @@ -638,6 +665,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; @@ -647,11 +678,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}, @@ -668,6 +708,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 @@ -698,15 +739,19 @@ 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 there are no versions in the archive at all, then + # we can archive if enough days have passed + if @sourceversions; } # 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 { @@ -739,12 +784,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 @@ -775,12 +825,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; @@ -797,20 +850,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 ''); @@ -820,7 +879,8 @@ sub get_bug_status { $status{"pending"} = 'fixed' if ($tags{fixed}); - my $presence = bug_presence(map{(exists $param{$_})?($_,$param{$_}):()} + my $presence = bug_presence(status => \%status, + map{(exists $param{$_})?($_,$param{$_}):()} qw(bug sourceversions arch dist version found fixed package) ); if (defined $presence) { @@ -904,32 +964,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 { @@ -945,7 +1015,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 @@ -1001,18 +1072,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; @@ -1070,23 +1143,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}) { @@ -1101,7 +1182,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}}; } @@ -1115,43 +1197,50 @@ sub update_realtime { # 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; my %changed_bugs; - while($line = ) { + while($line = <$idx_old>) { @line = split /\s/, $line; - last unless (keys %bugs) > 0; # 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 IDXNEW $line; + print {$idx_new} $line; $changed_bugs{$line[1]} = $line; } elsif ($new eq "REMOVE") { $changed_bugs{$line[1]} = $line; } else { - print IDXNEW $new; + print {$idx_new} $new; $changed_bugs{$line[1]} = $line; } } - elsif ($line[1] > $min_bug) { - print IDXNEW $bugs{$min_bug}; - delete $bugs{$min_bug}; - $min_bug = min(keys %bugs); - $changed_bugs{$line[1]} = ''; + 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; - print IDXNEW while(); + print {$idx_new} <$idx_old>; - close(IDXNEW); - close(IDXDB); + close($idx_new); + close($idx_old); rename("$file.new", $file);