File Coverage

blib/lib/Ukigumo/Client.pm
Criterion Covered Total %
statement 78 162 48.1
branch 0 10 0.0
condition 0 12 0.0
subroutine 26 34 76.4
pod 6 6 100.0
total 110 224 49.1


line stmt bran cond sub pod time code
1             package Ukigumo::Client;
2 4     4   29278 use strict;
  4         7  
  4         128  
3 4     4   18 use warnings;
  4         9  
  4         183  
4 4     4   109 use 5.008001;
  4         17  
  4         236  
5             our $VERSION = '0.36';
6              
7 4     4   22 use Carp ();
  4         7  
  4         88  
8 4     4   3962 use Capture::Tiny;
  4         149033  
  4         241  
9 4     4   3235 use Encode::Locale;
  4         71234  
  4         196  
10 4     4   32 use Encode;
  4         11  
  4         293  
11 4     4   23 use File::Spec;
  4         8  
  4         110  
12 4     4   19 use File::Path qw(mkpath);
  4         8  
  4         157  
13 4     4   4422 use LWP::UserAgent;
  4         233251  
  4         175  
14 4     4   5896 use English '-no_match_vars';
  4         13175  
  4         26  
15 4     4   6060 use HTTP::Request::Common qw(POST);
  4         10907  
  4         305  
16 4     4   4967 use JSON qw(decode_json);
  4         58948  
  4         27  
17 4     4   686 use File::Temp;
  4         8  
  4         463  
18 4     4   342795 use File::HomeDir;
  4         26731  
  4         376  
19 4     4   32 use Cwd;
  4         9  
  4         237  
20 4     4   3139 use Scope::Guard;
  4         1802  
  4         162  
21              
22 4     4   3131 use Ukigumo::Constants;
  4         5307  
  4         277  
23 4     4   2244 use Ukigumo::Client::CommandRunner;
  4         16  
  4         142  
24 4     4   2467 use Ukigumo::Client::Executor::Command;
  4         10  
  4         109  
25 4     4   1922 use Ukigumo::Client::Logger;
  4         12  
  4         137  
26 4     4   2373 use Ukigumo::Client::YamlConfig;
  4         20  
  4         180  
27 4     4   3587 use Ukigumo::Helper qw(normalize_path);
  4         2284  
  4         572  
28 4     4   28 use Ukigumo::Logger;
  4         8  
  4         85  
29              
30 4     4   19 use Mouse;
  4         10  
  4         32  
31              
32             has 'workdir' => (
33             is => 'ro',
34             isa => 'Str',
35             required => 1,
36             lazy => 1,
37             default => sub {
38             my $self = shift;
39             File::Spec->catdir( File::HomeDir->my_home, '.ukigumo', 'work')
40             },
41             );
42             has 'project' => (
43             is => 'rw',
44             isa => 'Str',
45             default => sub {
46             my $self = shift;
47             my $proj = $self->repository;
48             $proj =~ s/\.git$//;
49             $proj =~ s!.+\/!!;
50             $proj || '-';
51             },
52             lazy => 1,
53             );
54             has 'logfh' => (
55             is => 'ro',
56             default => sub { File::Temp->new(UNLINK => 1) }
57             );
58             has 'server_url' => (
59             is => 'ro',
60             isa => 'Str',
61             required => 1,
62             );
63             has 'user_agent' => (
64             is => 'ro',
65             required => 1,
66             lazy => 1,
67             default => sub {
68             my $ua = LWP::UserAgent->new(
69             agent => "ukigumo-client/$Ukigumo::Client::VERSION" );
70             $ua->env_proxy;
71             $ua;
72             },
73             );
74              
75             has quiet => (
76             is => 'ro',
77             isa => 'Bool',
78             default => 0,
79             );
80              
81             # components
82             has 'vc' => (
83             is => 'ro',
84             required => 1,
85             handles => [qw(get_revision branch repository)],
86             );
87             has 'executor' => (
88             is => 'ro',
89             required => 1,
90             );
91             has 'notifiers' => (
92             is => 'rw',
93             default => sub { +[ ] },
94             );
95              
96             has 'compare_url' => (
97             is => 'ro',
98             isa => 'Str',
99             default => '',
100             );
101             has 'repository_owner' => (
102             is => 'ro',
103             isa => 'Str',
104             default => '',
105             );
106             has 'repository_name' => (
107             is => 'ro',
108             isa => 'Str',
109             default => '',
110             );
111              
112             # for VC log
113             has vc_log => (
114             is => 'ro',
115             isa => 'Str',
116             lazy => 1,
117             default => sub {
118             my $self = shift;
119             chomp(my $orig_revision = $self->orig_revision);
120             chomp(my $current_revision = $self->current_revision);
121             join '', $self->vc->get_log($orig_revision, $current_revision);
122             },
123             );
124             has current_revision => (
125             is => 'ro',
126             isa => 'Str',
127             lazy => 1,
128             default => sub {
129             my $self = shift;
130             $self->vc->get_revision();
131             },
132             );
133             has orig_revision => (
134             is => 'ro',
135             isa => 'Str',
136             lazy => 1,
137             default => sub {
138             my $self = shift;
139             $self->vc->get_revision();
140             },
141             );
142              
143             has 'elapsed_time_sec' => (
144             is => 'rw',
145             isa => 'Maybe[Int]',
146             default => 0,
147             );
148              
149             has 'logger' => (
150             is => 'ro',
151             isa => 'Ukigumo::Client::Logger',
152             lazy => 1,
153             default => sub {
154             my $self = shift;
155             Ukigumo::Client::Logger->new(
156             logfh => $self->logfh,
157             branch => $self->branch,
158             quiet => $self->quiet,
159             );
160             },
161             );
162              
163 4     4   3606 no Mouse;
  4         10  
  4         23  
164              
165             sub push_notifier {
166 0     0 1   my $self = shift;
167 0           push @{$self->notifiers}, @_;
  0            
168             }
169              
170             sub run {
171 0     0 1   my $self = shift;
172              
173             # Back to original directory, after work.
174 0           my $orig_cwd = Cwd::getcwd();
175             my $guard = Scope::Guard->new(
176 0     0     sub { chdir $orig_cwd }
177 0           );
178              
179 0           my $workdir = File::Spec->catdir( $self->workdir, normalize_path($self->project), normalize_path($self->branch) );
180              
181 0           $self->logger->infof("ukigumo-client $VERSION");
182 0           $self->logger->infof("start testing : " . $self->vc->description());
183 0           $self->logger->infof("working directory : " . $workdir);
184              
185             {
186 0           mkpath($workdir);
  0            
187 0 0         unless (chdir($workdir)) {
188 0           $self->reflect_result(STATUS_FAIL);
189 0           die "Cannot chdir(@{[ $workdir ]}): $!";
  0            
190             }
191              
192 0           $self->logger->infof('run vc : ' . ref $self->vc);
193 0           chomp(my $orig_revision = $self->orig_revision);
194              
195 0           $self->vc->update($self, $workdir);
196 0           chomp(my $current_revision = $self->current_revision);
197              
198 0 0 0       if ($self->vc->skip_if_unmodified && $orig_revision eq $current_revision) {
199 0           $self->logger->infof('skip testing');
200 0           return;
201             }
202              
203 0           my $conf = Ukigumo::Client::YamlConfig->new(c => $self);
204              
205 0           local %ENV = %ENV;
206 0           $conf->apply_environment_variables;
207              
208 0   0       $self->project($conf->project_name || $self->project);
209              
210 0           $self->push_notifier(@{$conf->notifiers});
  0            
211              
212 0           my $repository_owner = $self->repository_owner;
213 0           my $repository_name = $self->repository_name;
214              
215 0           for my $notify (grep { ref $_ eq NOTIFIER_GITHUBSTATUSES } @{$self->notifiers}) {
  0            
  0            
216 0           $notify->send($self, STATUS_PENDING, '', '', $current_revision, $repository_owner, $repository_name);
217             }
218              
219 0           my $command_runner = Ukigumo::Client::CommandRunner->new(c => $self, config => $conf);
220              
221 0           $command_runner->run('before_install');
222 0           $command_runner->run('install');
223 0           $command_runner->run('before_script');
224              
225 0 0         my $executor = defined($conf->script) ? Ukigumo::Client::Executor::Command->new(command => $conf->script)
226             : $self->executor;
227              
228 0           $self->logger->infof('run executor : ' . ref $executor);
229 0           my $status = $executor->run($self);
230 0           $self->logger->infof('finished testing : ' . $status);
231              
232 0           $command_runner->run('after_script');
233              
234 0           $self->reflect_result($status);
235             }
236              
237 0           $self->logger->infof("end testing");
238             }
239              
240             sub report_timeout {
241 0     0 1   my ($self, $log_filename) = @_;
242              
243 0           $self->elapsed_time_sec(undef);
244 0           $self->reflect_result(STATUS_TIMEOUT, $log_filename);
245             }
246              
247             sub reflect_result {
248 0     0 1   my ($self, $status, $log_filename) = @_;
249              
250 0           my ($report_url, $last_status) = $self->send_to_server($status, $log_filename);
251              
252 0           $self->logger->infof("sending notification: @{[ $self->branch ]}, $status");
  0            
253              
254 0           my $repository_owner = $self->repository_owner;
255 0           my $repository_name = $self->repository_name;
256              
257 0           for my $notify (@{$self->notifiers}) {
  0            
258 0           $notify->send($self, $status, $last_status, $report_url, $self->current_revision, $repository_owner, $repository_name);
259             }
260             }
261              
262             sub send_to_server {
263 0     0 1   my ($self, $status, $log_filename) = @_;
264              
265 0           my $server_url = $self->server_url;
266 0           $server_url =~ s!/$!!g;
267              
268 0           $self->logger->infof("sending result to server at $server_url (status: $status)");
269              
270 0           my $ua = $self->user_agent();
271              
272             # flush log file before send it
273 0           $self->logfh->flush();
274              
275 0   0       my $req =
276             POST $server_url . '/api/v1/report/add',
277             Content_Type => 'form-data',
278             Content => [
279             project => $self->project,
280             branch => $self->branch,
281             repo => $self->repository,
282             revision => substr($self->current_revision, 0, 10),
283             status => $status,
284             vc_log => $self->vc_log,
285             body => [$log_filename || $self->logfh->filename],
286             compare_url => $self->compare_url,
287             elapsed_time_sec => $self->elapsed_time_sec,
288             ];
289 0           my $res = $ua->request($req);
290 0 0         $res->is_success or die "Cannot send a report to @{[ $self->server_url ]}/api/v1/report/add:\n" . $res->as_string;
  0            
291 0   0       my $dat = eval { decode_json($res->decoded_content) } || $res->decoded_content . " : $@";
292 0           $self->logger->infof("report url: $dat->{report}->{url}");
293 0 0         my $report_url = $dat->{report}->{url} or die "Cannot get report url";
294 0           return ($report_url, $dat->{report}->{last_status});
295             }
296              
297             sub tee {
298 0     0 1   my ($self, $command) = @_;
299 0           $self->logger->infof("command: $command");
300             my ($out) = Capture::Tiny::tee_merged {
301 0     0     ( $EUID, $EGID ) = ( $UID, $GID );
302 0           my $begin_time = time;
303 0           system $command;
304 0           $self->elapsed_time_sec($self->elapsed_time_sec + time - $begin_time);
305 0           };
306 0           $out = Encode::encode("console_in", Encode::decode("console_out", $out));
307              
308 0           print {$self->logfh} $out;
  0            
309 0           return $?;
310             }
311              
312             1;
313             __END__