File Coverage

blib/lib/Gentoo/App/Pram.pm
Criterion Covered Total %
statement 48 138 34.7
branch 0 46 0.0
condition 0 11 0.0
subroutine 16 25 64.0
pod 3 9 33.3
total 67 229 29.2


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2             package Gentoo::App::Pram;
3              
4             our $VERSION = '0.100000';
5              
6 1     1   480 use warnings;
  1         2  
  1         33  
7 1     1   4 use strict;
  1         1  
  1         20  
8              
9 1     1   425 use Term::ANSIColor qw/colored/;
  1         7101  
  1         608  
10 1     1   12 use File::Basename qw/basename/;
  1         2  
  1         49  
11 1     1   374 use File::Which qw/which/;
  1         954  
  1         46  
12 1     1   354 use Encode qw/decode/;
  1         11126  
  1         82  
13 1     1   493 use File::Temp;
  1         13854  
  1         68  
14 1     1   448 use HTTP::Tiny;
  1         37126  
  1         50  
15              
16 1     1   8 use constant E_ERROR => colored('ERROR', 'red');
  1         2  
  1         4  
17 1     1   113 use constant E_NO => colored('NO', 'red');
  1         3  
  1         3  
18 1     1   73 use constant E_YES => colored('YES', 'green');
  1         2  
  1         5  
19 1     1   72 use constant E_OK => colored('OK', 'green');
  1         2  
  1         3  
20 1     1   70 use constant E_MERGE => colored('MERGE', 'blue');
  1         2  
  1         3  
21              
22 1     1   134 use constant CLOSES_GITHUB => qr#\ACloses: https?://github\.com#;
  1         3  
  1         41  
23              
24 1     1   570 use Getopt::Long;
  1         8758  
  1         4  
25 1     1   568 use Pod::Usage;
  1         31608  
  1         1318  
26              
27             sub new {
28 0     0 0   my ($class, @args) = @_;
29 0 0         return bless { ref $args[0] ? %{ $args[0] } : @args }, $class;
  0            
30             }
31              
32             sub new_with_opts {
33 0     0 0   my ($class) = @_;
34 0           my @opts = (
35             'repository|r=s',
36             'closes|c=s',
37             'editor|e=s',
38             'signoff|s',
39             'bug|b=s',
40             'help|h',
41             'man|m'
42             );
43 0           my %opts;
44 0 0         if (!GetOptions(\%opts, @opts)) {
45 0           print "\n";
46 0           pod2usage(-verbose => 1)
47             }
48 0           $opts{pr_number} = shift @ARGV;
49 0           return $class->new(\%opts);
50             }
51              
52             sub run {
53 0     0 0   my ($self) = @_;
54              
55 0           my $pr_number = $self->{pr_number};
56 0           my $closes = $self->{closes};
57 0           my $bug = $self->{bug};
58              
59 0           $| = 1;
60              
61 0 0         $self->{help} and pod2usage(-verbose => 1);
62 0 0         $self->{man} and pod2usage(-verbose => 2);
63              
64 0 0 0       $bug and $closes and pod2usage(
65             -message => E_ERROR . qq#! --bug and --closes options are mutually exclusive!\n#,
66             -verbose => 1
67             );
68              
69 0           run_checks($pr_number, "You must specify a Pull Request number!");
70 0 0         $bug and run_checks($bug, "You must specify a bug number when using --bug!");
71 0 0         $closes and run_checks($closes, "You must specify a bug number when using --closes!");
72              
73             # Defaults to 'gentoo/gentoo' because we're worth it.
74 0   0       my $repo_name = $self->{repository} || 'gentoo/gentoo';
75 0   0       my $editor = $self->{editor} || $ENV{EDITOR} || 'less';
76              
77 0           my $git_command = which('git') . ' am --keep-cr -S';
78 0 0         $self->{signoff} and $git_command = "$git_command -s";
79              
80 0           my $patch_url = "https://patch-diff.githubusercontent.com/raw/$repo_name/pull/$pr_number.patch";
81 0           $self->{pr_url} = "https://github.com/$repo_name/pull/$pr_number";
82            
83             # Go!
84 0           $self->apply_patch(
85             $editor,
86             $git_command,
87             $self->modify_patch(
88             $self->fetch_patch($patch_url)
89             )
90             );
91             }
92              
93             sub run_checks {
94 0 0   0 0   @_ == 2 || die qq#Usage: run_checks(obj, error_msg)#;
95 0           my ($obj, $error_msg) = @_;
96              
97 0 0         $obj || pod2usage(
98             -message => E_ERROR . qq#! $error_msg\n#,
99             -verbose => 1
100             );
101              
102 0 0         $obj =~ /^\d+$/ || pod2usage(
103             -message => E_ERROR . qq#! "$obj" is NOT a number!\n#,
104             -verbose => 1
105             );
106             }
107              
108             sub my_sleep {
109 0     0 0   select(undef, undef, undef, 0.50);
110             }
111              
112             sub fetch_patch {
113 0 0   0 1   @_ == 2 || die qq#Usage: fetch_patch(patch_url)\n#;
114 0           my ($self, $patch_url) = @_;
115              
116 0           print "Fetching $patch_url ... ";
117              
118 0           my $response = HTTP::Tiny->new->get($patch_url);
119 0           my $status = $response->{status};
120            
121 0 0         $status != 200 and die "\n" . E_ERROR . qq#! Unreachable URL! Got HTTP status $status!\n#;
122            
123 0           my $patch = $response->{content};
124 0           chomp $patch;
125              
126 0           print E_OK . "!\n";
127            
128 0           return decode('UTF-8', $patch);
129             }
130              
131             sub add_header {
132 0 0   0 0   @_ == 3 || die qq#Usage: add_header(patch, header, msg)\n#;
133 0           my ($patch, $header, $msg) = @_;
134              
135 0           print qq#$msg#;
136 0           my_sleep();
137 0           my $confirm = E_ERROR;
138 0           my $is_sub = $patch =~ s#---#$header#;
139 0 0         $is_sub and $confirm = E_OK;
140 0           print "$confirm!\n";
141 0           my_sleep();
142 0           return $patch;
143             }
144              
145             sub modify_patch {
146 0 0   0 1   @_ == 2 || die qq#Usage: modify_patch(patch)\n#;
147 0           my ($self, $patch) = @_;
148              
149 0 0         if (not $patch =~ CLOSES_GITHUB) {
150 0           my $pr_url = $self->{pr_url};
151 0           $patch = add_header(
152             $patch,
153             qq#Closes: $pr_url\n---#,
154             qq#Adding Github "Closes:" header ... #
155             );
156             }
157              
158 0 0         if ($self->{bug}) {
159 0           my $bug = $self->{bug};
160 0           $patch = add_header(
161             $patch,
162             qq#Bug: https://bugs.gentoo.org/$bug\n---#,
163             qq#Adding Gentoo "Bug:" header with bug $bug ... #
164             );
165             }
166              
167 0 0         if ($self->{closes}) {
168 0           my $closes = $self->{closes};
169 0           $patch = add_header(
170             $patch,
171             qq#Closes: https://bugs.gentoo.org/$closes\n---#,
172             qq#Adding Gentoo "Closes:" header with bug $closes ... #
173             );
174             }
175              
176 0           return $patch;
177             }
178              
179             sub apply_patch {
180 0 0   0 1   @_ == 4 || die qq#Usage: apply_patch(editor, git_command, patch)\n#;
181 0           my ($self, $editor, $git_command, $patch) = @_;
182              
183 0           my $patch_location = File::Temp->new() . '.patch';
184 0   0       open my $fh, '>:encoding(UTF-8)', $patch_location || die E_ERROR . qq#! Can't write to $patch_location: $!!\n#;
185 0           print $fh $patch;
186 0           close $fh;
187              
188 0           print "Opening $patch_location with $editor ... ";
189 0           my_sleep();
190 0           my $exit = system $editor => $patch_location;
191 0 0         $exit eq 0 || die E_ERROR . qq#! Could not open $patch_location: $!!\n#;
192 0           print E_OK . "!\n";
193            
194 0           print E_MERGE . "? Do you want to apply this patch and merge this PR? [y/n] ";
195              
196 0           chomp(my $answer = );
197              
198 0 0         if ($answer =~ /^[Yy]$/) {
199 0           $git_command = "$git_command $patch_location";
200 0           print E_YES . "!\n";
201 0           print "Launching '$git_command' ... ";
202 0           $exit = system join ' ', $git_command;
203 0 0         $exit eq 0 || die E_ERROR . qq#! Error when launching '$git_command': $!!\n#;
204 0           print E_OK . "!\n";
205             } else {
206 0           print E_NO . "!\nBailing out.\n";
207             }
208            
209 0           print "Removing $patch_location ... ";
210 0   0       unlink $patch_location || die E_ERROR . qq#! Couldn't remove '$patch_location'!\n#;
211 0           print E_OK . "!\n";
212             }
213              
214             1;
215              
216             __END__