2 # Extract version information from an existing non-versioned database by
3 # guesswork, based on Version: pseudo-headers and closing mails that look
4 # like Debian changelogs. The latter in particular is somewhat heuristic.
12 Usage: $0 db-directory versions-directory
21 print "Processing $log ...\n";
23 open LOG, "< $log" or die "Can't open $log: $!";
24 my @records = read_log_records(*LOG);
27 my (@found_versions, @fixed_versions);
28 my (%found_versions, %fixed_versions);
30 for my $record (@records) {
31 if ($record->{type} eq 'html') {
32 # Reassigns zap the found and fixed version list. Reopens will
33 # zap the fixed list too in the full deployment, but doing that
34 # here causes problems in case of accidental reopens and
36 if ($record->{text} =~ /assigned/) {
45 next unless $record->{type} eq 'autocheck' or
46 $record->{type} eq 'incoming-recv';
47 my $decoded = Debbugs::MIME::parse($record->{text});
48 next unless defined $decoded;
50 # Was it sent to -done or -close?
52 my $firstreceived = $decoded->{header}[0];
53 if ($firstreceived =~ /\(at [^)]*-(?:done|close)\)/) {
57 # Get Version: pseudo-headers.
59 for ($i = 0; $i < @{$decoded->{body}}; ++$i) {
60 last if $decoded->{body}[$i] !~ /^(\S+):\s*(.*)/;
61 my ($fn, $fv) = (lc $1, $2);
62 next if $fn ne 'version';
63 next if $fv !~ /^(\d[^,\s]*(?:[,\s]+|$))+/;
65 for my $v (split /[,\s]+/, $fv) {
66 push @fixed_versions, $v
67 unless exists $fixed_versions{$v};
68 $fixed_versions{$v} = 1;
69 @found_versions = grep { $_ ne $v } @found_versions;
70 delete $found_versions{$v};
73 for my $v (split /[,\s]+/, $fv) {
74 push @found_versions, $v
75 unless exists $found_versions{$v};
76 $found_versions{$v} = 1;
77 @fixed_versions = grep { $_ ne $v } @fixed_versions;
78 delete $fixed_versions{$v};
84 # Look for Debian changelogs.
85 for (; $i < @{$decoded->{body}}; ++$i) {
86 if ($decoded->{body}[$i] =~
87 /\S+ \(([^)]+)\) \S+; urgency=\S+/i) {
89 push @fixed_versions, $v
90 unless exists $fixed_versions{$v};
91 $fixed_versions{$v} = 1;
92 @found_versions = grep { $_ ne $v } @found_versions;
93 delete $found_versions{$v};
100 return (\@found_versions, \@fixed_versions);
105 my ($target, $source) = @_;
106 my %seen = map { $_ => 1 } @$target;
107 for my $v (@$source) {
108 next if exists $seen{$v};
114 my ($db, $verdb) = @ARGV[0, 1];
115 opendir DB, $db or die "Can't opendir $db: $!";
117 mkdir $verdb or die "Can't mkdir $verdb: $!";
120 while (defined(my $dir = readdir DB)) {
121 next if $dir =~ /^\.\.?$/ or not -d "$db/$dir";
122 opendir HASH, "$db/$dir" or die "Can't opendir $db/$dir: $!";
124 while (defined(my $file = readdir HASH)) {
125 next unless $file =~ /\.log$/;
126 next if -z "$db/$dir/$file";
127 (my $bug = $file) =~ s/\..*//;
131 next if -e "$verdb/$bughash/$bug.versions" and
132 (stat "$verdb/$bughash/$bug.versions")[9] >=
133 (stat "$db/$dir/$file")[9];
135 open STATUS, "$db/$dir/$bug.status" or next;
136 <STATUS> for 1 .. 6; # done is field 7
137 chomp (my $done = <STATUS>);
138 <STATUS>; # mergedwith is field 9
139 chomp (my $mergedwith = <STATUS>);
142 my ($found_versions, $fixed_versions) = getbuginfo("$db/$dir/$file");
144 if (length $mergedwith) {
145 for my $merge (split ' ', $mergedwith) {
148 my ($mfound, $mfixed) =
149 getbuginfo("$db/$mergehash/$merge.log");
150 mergeinto($found_versions, $mfound);
151 mergeinto($fixed_versions, $mfixed);
155 @$fixed_versions = () unless length $done;
157 for my $out ($bug, (split ' ', $mergedwith)) {
161 unless (-d "$verdb/$outhash") {
162 mkdir "$verdb/$outhash" or die "Can't mkdir $verdb/$outhash: $!";
165 open VERSIONS, "> $verdb/$outhash/$out.versions"
166 or die "Can't open $verdb/$outhash/$out.versions: $!";
167 print VERSIONS "Found-in: @$found_versions\n";
168 print VERSIONS "Fixed-in: @$fixed_versions\n";