#! /usr/bin/perl -w # Extract version information from an existing non-versioned database by # guesswork, based on Version: pseudo-headers and closing mails that look # like Debian changelogs. The latter in particular is somewhat heuristic. # <@aj> Hackin' on the BTS, Feelin' like it'll take forever; Oh you better # hold it's hand, when it dies on names so clever. These are the best # bugs of our life. It's up to archive-slash-69, man we were killin' # time, we were young and resltess, we needed to unwind. I guess # nothin' can last forever - forever, no... my $config_path = '/etc/debbugs'; my $lib_path = '/usr/lib/debbugs'; require "$config_path/config"; require "$lib_path/errorlib"; use Debbugs::Log; use Debbugs::MIME; if (@ARGV != 1) { print <{type} eq 'html') { # Reassigns zap the found and fixed version list. Reopens will # zap the fixed list too in the full deployment, but doing that # here causes problems in case of accidental reopens and # recloses. if ($record->{text} =~ /assigned/) { @found_versions = (); %found_versions = (); @fixed_versions = (); %fixed_versions = (); } next; } next unless $record->{type} eq 'autocheck' or $record->{type} eq 'incoming-recv'; my $decoded = Debbugs::MIME::parse($record->{text}); next unless defined $decoded; # Was it sent to -done or -close? my $closing = 0; my $firstreceived = $decoded->{header}[0]; if ($firstreceived =~ /\(at [^)]*-(?:done|close)\)/) { $closing = 1; } # Get Version: pseudo-headers. my $i; my ($source, $sourcever, $ver); for ($i = 0; $i < @{$decoded->{body}}; ++$i) { last if $decoded->{body}[$i] !~ /^(\S+):\s*(.*)/; my ($fn, $fv) = (lc $1, $2); if ($fn eq 'source') { $source = $fv; } elsif ($fn eq 'source-version' and $fv =~ /^(\d[^,\s]*(?:[,\s]+|$))+/) { $sourcever = $fv; } elsif ($fn eq 'version' and $fv =~ /^(\d[^,\s]*(?:[,\s]+|$))+/) { # Deal with reportbug brain-damage. next if $fv =~ /^unavailable/i; $fv =~ s/;.*//; $fv =~ s/ *\(.*\)//; # Strip off other random junk at the end of a version. $fv =~ s/ +[A-Za-z].*//; $ver = $fv; } } my @parsedvers; if (defined $ver) { push @parsedvers, split /[,\s]+/, $ver; } elsif (defined $source and defined $sourcever) { push @parsedvers, map "$source/$_", split /[,\s]+/, $sourcever; } if ($closing) { for my $v (@parsedvers) { push @fixed_versions, $v unless exists $fixed_versions{$v}; $fixed_versions{$v} = 1; @found_versions = grep { $_ ne $v } @found_versions; delete $found_versions{$v}; } } else { for my $v (@parsedvers) { push @found_versions, $v unless exists $found_versions{$v}; $found_versions{$v} = 1; @fixed_versions = grep { $_ ne $v } @fixed_versions; delete $fixed_versions{$v}; } } if ($closing) { # Look for Debian changelogs. for (; $i < @{$decoded->{body}}; ++$i) { if ($decoded->{body}[$i] =~ /(\w[-+0-9a-z.]+) \(([^\(\) \t]+)\) \S+; urgency=\S+/i) { my ($p, $v) = ($1, $2); push @fixed_versions, "$p/$v" unless exists $fixed_versions{"$p/$v"}; $fixed_versions{"$p/$v"} = 1; @found_versions = grep { $_ ne "$p/$v" } @found_versions; delete $found_versions{"$p/$v"}; last; } } } } return (\@found_versions, \@fixed_versions); } sub mergeinto ($$) { my ($target, $source) = @_; my %seen = map { $_ => 1 } @$target; for my $v (@$source) { next if exists $seen{$v}; push @$target, $v; $seen{$v} = 1; } } chdir $gSpoolDir or die "Can't chdir $gSpoolDir: $!"; my $db = $ARGV[0]; opendir DB, $db or die "Can't opendir $db: $!"; while (defined(my $dir = readdir DB)) { next if $dir =~ /^\.\.?$/ or not -d "$db/$dir"; opendir HASH, "$db/$dir" or die "Can't opendir $db/$dir: $!"; while (defined(my $file = readdir HASH)) { next unless $file =~ /\.log$/; next if -z "$db/$dir/$file"; (my $bug = $file) =~ s/\..*//; $bug =~ /(..)$/; my $bughash = $1; print "Processing $bug ...\n" if $ENV{DEBBUGS_VERBOSE}; my ($locks, $status) = lockreadbugmerge($bug, $db); unless (defined $status) { unlockreadbugmerge($locks); next; } if (@{$status->{found_versions}} or @{$status->{fixed_versions}}) { unlockreadbugmerge($locks); next; } my @merges = (); # Only process the lowest of each set of merged bugs. if (length $status->{mergedwith}) { @merges = sort { $a <=> $b } split ' ', $status->{mergedwith}; if ($merges[0] < $bug) { unlockreadbugmerge($locks); next; } } my ($found_versions, $fixed_versions) = getbuginfo("$db/$dir/$file"); if (length $status->{mergedwith}) { for my $merge (@merges) { $merge =~ /(..)$/; my $mergehash = $1; my ($mfound, $mfixed) = getbuginfo("$db/$mergehash/$merge.log"); mergeinto($found_versions, $mfound); mergeinto($fixed_versions, $mfixed); } } @$fixed_versions = () unless length $status->{done}; for my $out ($bug, @merges) { if ($out != $bug) { filelock("lock/$out"); } my $outstatus = readbug($out, $db); $outstatus->{found_versions} = [@$found_versions]; $outstatus->{fixed_versions} = [@$fixed_versions]; writebug($out, $outstatus, $db, 2, 'disable bughook'); if ($out != $bug) { unfilelock(); } } unlockreadbugmerge($locks); } closedir HASH; } closedir DB;