diff --git a/maintainers/scripts/nixpkgs-lint.nix b/maintainers/scripts/nixpkgs-lint.nix new file mode 100644 index 00000000000..5eb7649d58d --- /dev/null +++ b/maintainers/scripts/nixpkgs-lint.nix @@ -0,0 +1,22 @@ +{ stdenv, makeWrapper, perl, perlPackages }: + +stdenv.mkDerivation { + name = "nixpkgs-lint-1"; + + buildInputs = [ makeWrapper perl perlPackages.XMLSimple ]; + + unpackPhase = "true"; + buildPhase = "true"; + + installPhase = + '' + mkdir -p $out/bin + cp ${./nixpkgs-lint.pl} $out/bin/nixpkgs-lint + wrapProgram $out/bin/nixpkgs-lint --set PERL5LIB $PERL5LIB + ''; + + meta = { + maintainers = [ stdenv.lib.maintainers.eelco ]; + description = "A utility for Nixpkgs contributors to check Nixpkgs for common errors"; + }; +} diff --git a/maintainers/scripts/nixpkgs-lint.pl b/maintainers/scripts/nixpkgs-lint.pl new file mode 100755 index 00000000000..0b1519ef15c --- /dev/null +++ b/maintainers/scripts/nixpkgs-lint.pl @@ -0,0 +1,115 @@ +#! /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"; diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index bffffad649d..300c4bc23fe 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -206,6 +206,8 @@ let nix-generate-from-cpan = callPackage ../../maintainers/scripts/nix-generate-from-cpan.nix { }; + nixpkgs-lint = callPackage ../../maintainers/scripts/nixpkgs-lint.nix { }; + ### STANDARD ENVIRONMENT