1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
|
#!/usr/bin/perl
use strict;
use warnings;
use Getopt::Long;
use Pod::Usage qw( pod2usage );
use Path::Tiny;
use HTTP::Tiny;
use IO::Uncompress::Gunzip qw(gunzip $GunzipError);
use Debian::Control;
use Dpkg::Control::HashCore;
use Debian::PkgPerl::Util;
use 5.010;
# 0) setup
my %opt;
GetOptions( \%opt, 'help|h', 'man|m', ) || pod2usage(2);
pod2usage(1) if $opt{help};
pod2usage( -exitval => 0, -verbose => 2 ) if $opt{man};
my $dpt_packages = $ENV{'DPT_PACKAGES'};
die "Required configuration variable DPT_PACKAGES is not set
in ~/.dpt.conf or ~/.config/dpt.conf or in your environment.\n"
unless $dpt_packages;
$dpt_packages =~ s/~/$ENV{'HOME'}/;
die "No directory called '$dpt_packages' found: $!" unless -d $dpt_packages;
# 1) local repos
my $gitpkgs = path($dpt_packages)->visit(
sub {
my ( $dir, $state ) = @_;
my $changelog = $dir->child('debian/changelog');
return unless -f $changelog;
my ($head) = $changelog->lines( { count => 1, chomp => 1, } );
# "zonemaster-gui (1.0.7-2) UNRELEASED; urgency=medium
my ( $pkg, $ver, $dist, $urgency ) = split /\s/, $head;
$ver =~ s|\((.+)\)|$1|;
$dist =~ s|;||;
$urgency =~ s|urgency=||;
my $control = $dir->child('debian/control');
return unless -f $control;
my $c = Debian::Control->new();
$c->read("$control");
my $uploaders = $c->source->Uploaders;
$uploaders =~ s/\n//;
$uploaders =~ s/\s*//;
$uploaders //= '';
$state->{$pkg} = {
ver => $ver,
dist => $dist,
urgency => $urgency,
uploaders => $uploaders,
};
},
{ recurse => 0, follow_symlinks => 0, } # defaults
);
# 2) Sources
my @sources;
foreach my $component (qw/main contrib non-free non-free-firmware/) {
my $sourcesgz = Debian::PkgPerl::Util->download_and_cache_file(
"https://deb.debian.org/debian/dists/unstable/$component/source/Sources.gz",
"Sources.$component.gz",
6 * 60 * 60
);
use IO::Uncompress::Gunzip qw(gunzip $GunzipError);
( my $sources = $sourcesgz ) =~ s/\.gz$//;
my $status = gunzip "$sourcesgz" => "$sources"
or die "gunzip failed: $GunzipError\n";
push @sources, $sources;
}
my @srcpkgs
= qx|grep-dctrl --no-field-names --show-field Package --field Maintainer --pattern pkg-perl-maintainers\@lists.alioth.debian.org @sources|;
unlink @sources; # rm uncompressed files
# 3) removals
my $removalsfile = Debian::PkgPerl::Util->download_and_cache_file(
'https://ftp-master.debian.org/removals-full.822',
'removals-full.822',
24 * 60 * 60
);
my $removalfh = path($removalsfile)->openr;
my $removal_deb822;
while (
defined(
$removal_deb822
= Dpkg::Control::HashCore->new( allow_duplicate => 1 )
)
and ( $removal_deb822->parse( $removalfh, $removalsfile ) )
)
{
next unless $removal_deb822->{Sources}; # e.g. binaries only in auto-craft
my $reason = $removal_deb822->{Reason};
next if $reason =~ /\[auto-cruft\]/; # e.g. "[auto-cruft] NVIU"
next if $reason =~ /NVIU/; # e.g. "[rene] NVIU"
my $date = $removal_deb822->{Date};
my @sources = grep {/\S/} split /^/, $removal_deb822->{Sources};
chomp @sources;
foreach (@sources) {
my ( $source, $version ) = split /_/, $_;
next unless defined $gitpkgs->{$source};
my $comment = "RM: date: $date, version: $version, reason: $reason";
$gitpkgs->{$source}->{comment} .= $comment;
}
}
# 4) NEW queue
my $newfile
= Debian::PkgPerl::Util->download_and_cache_file(
'https://ftp-master.debian.org/new.822',
'new.822', 24 * 60 * 60 );
my $newfh = path($newfile)->openr;
my $new_deb822;
while (
defined(
$new_deb822 = Dpkg::Control::HashCore->new( allow_duplicate => 1 )
)
and ( $new_deb822->parse( $newfh, $newfile ) )
)
{
next unless $new_deb822->{Source};
my $source = $new_deb822->{Source};
my $version = $new_deb822->{Version};
my $age = $new_deb822->{Age};
next unless defined $gitpkgs->{$source};
my $comment = "NEW: version: $version, age: $age";
$gitpkgs->{$source}->{comment} .= $comment;
}
# 5) output
my $title = "Packages in Git but not in Sources (as pkg-perl): ";
say "\n$title";
my $format = "%-45s %-15s %-15s %-25s %-s\n";
printf $format, 'Source package', 'Version', 'Distribution', 'Uploaders', '';
say '-' x 101;
my $count;
foreach my $p ( sort keys %{$gitpkgs} ) {
next if grep ( /^$p$/, @srcpkgs );
printf $format, $p, $gitpkgs->{$p}->{ver}, $gitpkgs->{$p}->{dist},
$gitpkgs->{$p}->{uploaders},
( $gitpkgs->{$p}->{comment}
? '| ' . $gitpkgs->{$p}->{comment}
: '' );
$count++;
}
say '=' x 101;
say "Count: $count";
exit;
__END__
=head1 NAME
dpt-never-uploaded - list packages in Git which are not in the archive
=head1 SYNOPSIS
B<dpt never-uploaded> I<[--help|-h]> I<[--man|-m]>
=head1 DESCRIPTION
B<dpt never-uploaded> compares the local Git repositories of packages with
the contents of the Sources files in the archive where the Debian Perl Group
is the maintainer, and outputs a list of packages not found in unstable.
These are candidates for either uploading or removing or updating the
maintainer field, unless they are in the NEW queue or only in experimental.
=head1 OPTIONS
=over
=item B<--help|-h>
Show this help.
=item B<--man|-m>
Show full manpage.
=back
=head1 CONFIGURATION
B<dpt never-uploaded> uses the C<DPT_PACKAGES> environment variable.
See L<dpt-config(5)> for details.
=head1 COPYRIGHT AND LICENSE
Copyright 2023-2024, gregor herrmann E<lt>gregoa@debian.orgE<gt>
Released under the same terms as Perl itself, i.e. Artistic or GPL-1+.
|