nixpkgs/maintainers/scripts/nixpkgs-lint.pl

116 lines
3.5 KiB
Perl
Raw Normal View History

#! /run/current-system/sw/bin/perl -w
use strict;
use List::Util qw(min);
use XML::Simple qw(:strict);
use Data::Dumper;
my $filter = "*";
my $xml = `nix-env -f . -qa '$filter' --xml --meta --drv-path`;
my $info = XMLin($xml, KeyAttr => { 'item' => '+attrPath', 'meta' => 'name' }, ForceArray => 1, SuppressEmpty => '' ) or die "cannot parse XML output";
#print Dumper($info);
my %pkgsByName;
foreach my $attr (sort keys %{$info->{item}}) {
my $pkg = $info->{item}->{$attr};
#print STDERR "attr = $attr, name = $pkg->{name}\n";
$pkgsByName{$pkg->{name}} //= [];
push @{$pkgsByName{$pkg->{name}}}, $pkg;
}
# Check meta information.
print "=== Package meta information ===\n\n";
my $nrMissingMaintainers = 0;
my $nrMissingDescriptions = 0;
my $nrBadDescriptions = 0;
my $nrMissingLicenses = 0;
foreach my $attr (sort keys %{$info->{item}}) {
my $pkg = $info->{item}->{$attr};
my $pkgName = $pkg->{name};
$pkgName =~ s/-[0-9].*//;
# Check the maintainers.
my @maintainers;
my $x = $pkg->{meta}->{maintainers};
if (defined $x && $x->{type} eq "strings") {
@maintainers = map { $_->{value} } @{$x->{string}};
} elsif (defined $x->{value}) {
@maintainers = ($x->{value});
}
if (scalar @maintainers == 0) {
print "$attr: Lacks a maintainer\n";
$nrMissingMaintainers++;
}
# Check the license.
if (!defined $pkg->{meta}->{license}) {
print "$attr: Lacks a license\n";
$nrMissingLicenses++;
}
# Check the description.
my $description = $pkg->{meta}->{description}->{value};
if (!$description) {
print "$attr: Lacks a description\n";
$nrMissingDescriptions++;
} else {
my $bad = 0;
if ($description =~ /^\s/) {
print "$attr: Description starts with whitespace\n";
$bad = 1;
}
if ($description =~ /\s$/) {
print "$attr: Description ends with whitespace\n";
$bad = 1;
}
if ($description =~ /\.$/) {
print "$attr: Description ends with a period\n";
$bad = 1;
}
if (index(lc($description), lc($attr)) != -1) {
print "$attr: Description contains package name\n";
$bad = 1;
}
$nrBadDescriptions++ if $bad;
}
}
print "\n";
# Find packages that have the same name.
print "=== Package name collisions ===\n\n";
my $nrCollisions = 0;
foreach my $name (sort keys %pkgsByName) {
my @pkgs = @{$pkgsByName{$name}};
# Filter attributes that are aliases of each other (e.g. yield the
# same derivation path).
my %drvsSeen;
@pkgs = grep { my $x = $drvsSeen{$_->{drvPath}}; $drvsSeen{$_->{drvPath}} = 1; !defined $x } @pkgs;
# Filter packages that have a lower priority.
my $highest = min (map { $_->{priority} // 0 } @pkgs);
@pkgs = grep { ($_->{priority} // 0) == $highest } @pkgs;
next if scalar @pkgs == 1;
$nrCollisions++;
print "The following attributes evaluate to a package named $name:\n";
print " ", join(", ", map { $_->{attrPath} } @pkgs), "\n\n";
}
print "=== Bottom line ===\n";
print "Number of packages: ", scalar(keys %{$info->{item}}), "\n";
print "Number of missing maintainers: $nrMissingMaintainers\n";
print "Number of missing licenses: $nrMissingLicenses\n";
print "Number of missing descriptions: $nrMissingDescriptions\n";
print "Number of bad descriptions: $nrBadDescriptions\n";
print "Number of name collisions: $nrCollisions\n";