#!/usr/bin/perl # # Author: Florian Pritz # # Licensed under GPLv3 # use warnings; no warnings 'once'; use strict; use diagnostics; use JSON; use WWW::Mechanize; use Data::Dumper; use Text::Wrap; use Getopt::Long; use Pod::Usage; my $configfile = "$ENV{HOME}/.signoffsrc"; { package Settings; # define default values # these can be overridden from the config file our $domain = "https://www.archlinux.org"; our $pacman_db = "/var/lib/pacman"; chomp (our $system_arch = `uname -m`); our $display_disabled = 0; our $only_pending = 0; our $hide_signed_off = 0; our $display_comments = 1; our @target_repos = qw(core unknown); unless (my $return = do $configfile) { die "couldn't parse $configfile: $@" if $@; die "couldn't do $configfile: $!" unless defined $return; } } sub make_links { my $text = shift; $text =~ s|FS#([0-9]+)||; return $text; } Getopt::Long::Configure ("bundling"); GetOptions( "help" => sub {pod2usage(0);}, "h" => sub {pod2usage(2);}, "comments|c!" => \$Settings::display_comments ); # check if all vars are configured for my $varname (qw(username password)) { unless (${$Settings::{$varname}}) { print STDERR "$varname not defined in config file \"$configfile\"\n"; exit 1; } } # sanity checks if (! -d $Settings::pacman_db) { print STDERR "\$pacman_db set to non-directory \"$Settings::pacman_db\": $!\n"; exit 1; } $Text::Wrap::columns = 80; # array to hold information of installed packages my @installed_groups; my $mech = WWW::Mechanize->new(cookie_jar => {}); #$mech->proxy(['https', 'http', 'ftp'], 'http://localhost:8080'); # log in $mech->get("$Settings::domain/login/"); $mech->submit_form( form_id => "dev-login-form", fields => { username => $Settings::username, password => $Settings::password } ); if ($mech->content() =~ m/Please enter a correct username and password/) { print STDERR "Login credentials are incorrect.\n"; exit 1; } $mech->get("$Settings::domain/packages/signoffs/json"); my $json = JSON::decode_json($mech->content()); my $id = 0; for my $signoff_groups ($json->{signoff_groups}) { for my $signoff_group (@{$signoff_groups}) { # skip packages for different arch next unless ($signoff_group->{arch} eq $Settings::system_arch || $signoff_group->{arch} eq "any"); my $target_repo = lc $signoff_group->{target_repo}; # skip package that are not going to repos listed in the config if (!grep $_ eq $target_repo, @Settings::target_repos) { next; } if (!$Settings::display_disabled) { # skip disabled packages next if !$signoff_group->{enabled}; } if ($Settings::only_pending) { # skip packages with enough signoffs next if $signoff_group->{approved}; } # loop over packages, print some information and push it to @installed_groups # if the package is installed for my $pkgname (@{$signoff_group->{pkgnames}}) { # if package is installed save to @installed_groups, else skip it next unless -e "$Settings::pacman_db/local/$pkgname-$signoff_group->{version}"; $installed_groups[$id] = $signoff_group; print "\e[1;33m$id\e[0m - \e[0;33m$pkgname $signoff_group->{version}\e[0m\n"; if ($signoff_group->{known_bad}) { print " \e[1;31mKnown bad!\e[0m\n"; } if ($pkgname ne $signoff_group->{pkgbase}) { print " \e[0;31mPkgbase: $signoff_group->{pkgbase}\e[0m\n"; } unless ($signoff_group->{required} == 2) { print " Required signoffs: $signoff_group->{required}\n"; } my $i = 0; my $signoff_count = @{$signoff_group->{signoffs}}; print " Signoffs: "; if ($signoff_group->{enabled}) { for my $signoff (@{$signoff_group->{signoffs}}) { # if it's our signoff, save that info for later so we can revoke it if ($signoff->{user} eq $Settings::username && !$signoff->{revoked}) { $signoff_group->{can_revoke} = 1; print "\e[0;32m$signoff->{user}\e[0m"; } else { print $signoff->{user}; print " (revoked)" if $signoff->{revoked}; } print ", " if $i+1 < $signoff_count; $i++; } } else { print ""; } print "\n"; if ($signoff_group->{comments} ne "" && $Settings::display_comments) { print " \e[0;35mComment:\e[0m\n"; print wrap(" ", " ", make_links($signoff_group->{comments})); print "\n"; } $id++; } } } print "\n"; print STDERR"\e[0;34mPlease enter IDs of packages to sign off/revoke: \e[0m"; my $input = ; # sign off or revoke for my $id (split(" ", $input)) { unless ($id =~ /^\d+$/ && exists ($installed_groups[$id])) { print STDERR "Invalid ID \"$id\". Skipping...\n"; } my $pkg = $installed_groups[$id]; print "processing pkgbase $pkg->{pkgbase} ... \n"; my $url = "$Settings::domain/packages/$pkg->{repo}/$pkg->{arch}/$pkg->{pkgbase}/signoff/"; my $action = "signoff"; if ($pkg->{can_revoke}) { $action = "revoke"; $url .= "/revoke"; } eval { $mech->get($url); }; if ($@) { print STDERR "Warning: failed to process $action: $@\n"; } } __END__ =head1 SYNOPSIS signoffs.pl [options] Options: --help, -h display help message --comments, -c display comments (can be negated) =head1 OPTIONS =over 8 =item B<--help>, B<-h> Print a help message. =item B<--comments>, B<-c> Display comments. This option can be negated with --no: --no-comments =back =head1 EXAMPLE CONFIG Save this as ~/.signoffsrc # your login credentials $username = "whatever"; $password = "secret"; # if you want to use a script: # chomp ($password = `getpw whatever`); # Everthing below this line is optional # should we display packages that's can't be signed off? $display_disabled = 1; # only display packages that don't have enough signoffs $only_pending = 0; # hide packages you have signed off already $hide_signed_off = 0; # only display packages that will move to these repos @target_repos = qw(core unknown); # you probably don't have to change those $domain = "https://www.archlinux.org"; #chomp ($system_arch = `uname -m`); $pacman_db = "/var/lib/pacman"; =cut