File Coverage

blib/lib/App/PAUSE/cleanup.pm
Criterion Covered Total %
statement 46 139 33.0
branch 8 38 21.0
condition 0 6 0.0
subroutine 10 15 66.6
pod 0 3 0.0
total 64 201 31.8


line stmt bran cond sub pod time code
1             package App::PAUSE::cleanup;
2             BEGIN {
3 1     1   91121 $App::PAUSE::cleanup::VERSION = '0.0012';
4             }
5             # ABSTRACT: Manage (delete/undelete) your PAUSE files
6              
7 1     1   8 use strict;
  1         2  
  1         69  
8 1     1   5 use warnings;
  1         1  
  1         62  
9              
10 1     1   781 use Getopt::Usaginator <<_END_;
  1         42797  
  1         8  
11              
12             Usage: pause-cleanup
13              
14             --username Your PAUSE username
15             --password The password for the above
16             Instead of supplying your identity on the
17             commandline, you can setup \$HOME/.pause like so:
18              
19             user
20             password
21              
22             -d, --dump Dump the list of files to STDOUT
23            
24             -h, -?, --help This help
25              
26             _END_
27 1     1   1541 use Getopt::Long qw/ GetOptions /;
  1         15674  
  1         6  
28              
29              
30 1     1   1192 use Term::EditorEdit;
  1         92091  
  1         38  
31 1     1   967 use Config::Identity::PAUSE;
  1         64709  
  1         11  
32 1     1   1517 use WWW::Mechanize;
  1         343974  
  1         21  
33              
34             my $agent = WWW::Mechanize->new;
35              
36             sub run {
37 0     0 0 0 my $self = shift;
38 0         0 my @arguments = @_;
39              
40 0         0 my ( $help, $username, $password, $dump );
41             {
42 0         0 local @ARGV = @arguments;
  0         0  
43 0         0 GetOptions(
44             'username=s' => \$username,
45             'password=s' => \$password,
46             'dump|d' => \$dump,
47             'help|h|?' => \$help,
48             );
49             }
50              
51 0 0       0 usage 0 if $help;
52              
53 0         0 my %identity = Config::Identity::PAUSE->load;
54 0 0       0 $username = $identity{user} unless defined $username;
55 0 0       0 $password = $identity{password} unless defined $password;
56              
57 0 0 0     0 usage '! Missing username and/or password' unless
58             defined $username && defined $password;
59              
60 0         0 $agent->credentials( "pause.perl.org:443", "PAUSE", $username, $password );
61              
62 0         0 print "> Logging in as $username\n";
63            
64 0         0 my $response = $agent->get( 'https://pause.perl.org/pause/authenquery?ACTION=delete_files' );
65 0         0 my @filelist =
66             map {
67             # Package-Pkg-0.0016.tar.gz
68 0 0       0 m{/>\s*([\S]+)\s+(\d+)\s+(.*)\s*
69 0         0 my $tar_gz = $1;
70 0         0 my $size = $2;
71 0         0 my $scheduled = $3;
72 0         0 ( my $package = $tar_gz ) =~ s/-([\d\._]+)\.tar\.gz$//;
73 0         0 my $version = $1;
74 0         0 my $package_version = "$package-$version";
75 0 0       0 $scheduled = $scheduled =~ m/Scheduled for deletion/ ? 1 : 0;
76 0         0 { package => $package, package_version => $package_version,
77             version => $version, tar_gz => $tar_gz, size => $size,
78             scheduled => $scheduled };
79             }
80 0         0 grep { m/pause99_delete_files_FILE/ && m/\.tar\.gz/ }
81             split m/\n/, $response->decoded_content;
82              
83 0 0       0 if ( $dump ) {
84 0         0 print join "\n", map { $_->{package_version} } @filelist;
  0         0  
85 0         0 print "\n";
86 0         0 return;
87             }
88              
89 0         0 my %package;
90 0         0 for my $file (@filelist) {
91 0         0 push @{ $package{$file->{package}} }, $file;
  0         0  
92             }
93              
94 0         0 my @document;
95 0         0 push @document, <<_END_;
96             # Logged in as $username
97             #
98             # Any line not beginning with 'delete', 'undelete', or 'keep' is ignored
99             # To take action on a release, remove the leading '#'
100             #
101             # delete Delete the .meta, .readme, and .tar.gz associated
102             # with the release
103             #
104             # undelete Undelete the .meta, .readme, and .tar.gz (remove
105             # from scheduled deletion
106             #
107             # keep Ignore the release
108             #
109             # By default, the latest version of each release is commented 'keep'
110             # Older versions are commented 'delete' (or 'undelete')
111             _END_
112              
113 0         0 for my $name (sort keys %package) {
114 0         0 my @filelist = @{ $package{$name} };
  0         0  
115 0 0       0 @filelist = sort { $a->{scheduled} cmp $b->{scheduled} or
  0         0  
116             $b->{tar_gz} cmp $a->{tar_gz} } @filelist;
117              
118 0         0 push @document, "$name:";
119              
120 0         0 my @latest = $self->extract_latest( \@filelist );
121              
122 0         0 for my $latest ( @latest ) {
123 0 0       0 if ( $latest->{scheduled} )
124 0         0 { push @document, "# undelete $latest->{package_version}" }
125 0         0 else { push @document, "# keep $latest->{package_version}" }
126             }
127              
128 0 0       0 push @document,
129             ( map {
130 0         0 my $operation = $_->{scheduled} ? "undelete" : "delete";
131 0         0 "# $operation $_->{package_version}"
132             } @filelist ),
133             '',
134             ;
135             }
136              
137 0         0 my $document = join "\n", @document;
138            
139             my $delete_undelete = Term::EditorEdit->edit( document => $document, process => sub {
140 0     0   0 my $edit = shift;
141 0         0 my ( @delete, @undelete );
142 0         0 my @content = split m/\n/, $edit->content;
143 0         0 for my $line ( @content ) {
144 0 0       0 next unless $line =~ m/^\s*(delete|undelete)\s*(\S+)/i;
145 0 0       0 if ( lc $1 eq 'delete' ) { push @delete, $2 }
  0         0  
146 0         0 else { push @undelete, $2 }
147             }
148 0         0 return { delete => \@delete, undelete => \@undelete };
149 0         0 } );
150              
151 0         0 my ( $delete, $undelete ) = @$delete_undelete{qw/ delete undelete /};
152              
153 0 0       0 if ( @$delete ) {
154 0         0 print "\n---\n";
155 0         0 print join "\n", '', ( map { " $_" } @$delete ), '', '';
  0         0  
156 0         0 print "> Really delete? If you wish to abort, hit ^C (CTRL-C) now!\n";
157 0         0 print "> Hit return to continue, or cancel with ^C\n";
158 0         0 my $nil = ;
159 0         0 my $count = scalar @$delete;
160 0         0 print "> Deleting $count\n";
161 0         0 $self->_delete( $delete );
162             }
163            
164 0 0       0 if ( @$undelete ) {
165 0         0 print "\n---\n";
166 0         0 print join "\n", '', ( map { " $_" } @$undelete ), '', '';
  0         0  
167 0         0 my $count = scalar @$undelete;
168 0         0 print "> Undeleting $count\n";
169 0         0 $self->_undelete( $undelete );
170             }
171              
172 0 0 0     0 unless ( @$delete || @$undelete ) {
173 0         0 print "> Nothing to do\n";
174             }
175             }
176              
177             sub _delete {
178 0     0   0 my $self = shift;
179 0         0 $self->_submit( 'SUBMIT_pause99_delete_files_delete', @_ );
180             }
181              
182             sub _undelete {
183 0     0   0 my $self = shift;
184 0         0 $self->_submit( 'SUBMIT_pause99_delete_files_undelete', @_ );
185             }
186              
187             sub extract_latest {
188 5     5 0 13007 my $self = shift;
189 5         9 my $filelist = shift;
190              
191 5         9 my @latest;
192             my @filelist;
193 0         0 my $found;
194              
195 5         11 for my $file ( @$filelist ) {
196 13 100       50 if ( $file->{version} =~ m/_/ ) {
    100          
197 5 100       16 if ( ! @latest ) { push @latest, $file }
  3         9  
198 2         5 else { push @filelist, $file }
199             }
200             elsif ( ! $found ) {
201 5         6 $found = 1;
202 5         10 push @latest, $file;
203             }
204             else {
205 3         9 push @filelist, $file;
206             }
207             }
208              
209 5         14 @$filelist = @filelist;
210 5         19 return @latest;
211             }
212              
213             sub expand_filelist {
214 1     1 0 19 my $self = shift;
215 1         2 my $filelist = shift; # Actually, package_version
216              
217 1         2 my @filelist;
218 1         4 for my $package_version (@$filelist) {
219 5         9 my $pv = $package_version;
220 5         22 my ( $version ) = $pv =~ m/-([\d\._]+)$/;
221 5 100       13 if ( $version =~ m/_/ )
222 2         9 { push @filelist, "$pv.tar.gz" }
223 3         5 else { push @filelist, map { ( "$_.meta", "$_.readme", "$_.tar.gz" ) } $pv }
  3         18  
224             }
225              
226 1         17 return @filelist;
227             }
228              
229             sub _submit {
230 0     0     my $self = shift;
231 0           my $button = shift;
232 0           my $filelist = shift; # Actually, package_version
233            
234 0           my @filelist = $self->expand_filelist( $filelist );
235 0           $agent->get( 'https://pause.perl.org/pause/authenquery?ACTION=delete_files' );
236 0           $agent->tick( 'pause99_delete_files_FILE' => $_ ) for @filelist;
237 0           $agent->click( $button );
238             }
239              
240             1;
241              
242             __END__