File Coverage

blib/lib/App/Sqitch/Command/plan.pm
Criterion Covered Total %
statement 73 73 100.0
branch 27 28 96.4
condition 13 13 100.0
subroutine 15 15 100.0
pod 3 3 100.0
total 131 132 99.2


line stmt bran cond sub pod time code
1              
2             use 5.010;
3 3     3   1634 use strict;
  3         12  
4 3     3   18 use warnings;
  3         6  
  3         86  
5 3     3   17 use utf8;
  3         5  
  3         94  
6 3     3   15 use Locale::TextDomain qw(App-Sqitch);
  3         9  
  3         13  
7 3     3   70 use App::Sqitch::X qw(hurl);
  3         12  
  3         22  
8 3     3   583 use Moo;
  3         6  
  3         23  
9 3     3   1240 use Types::Standard qw(Str Int ArrayRef Bool);
  3         10  
  3         20  
10 3     3   1122 use Type::Utils qw(class_type);
  3         6  
  3         27  
11 3     3   2664 use App::Sqitch::ItemFormatter;
  3         5  
  3         19  
12 3     3   2917 use namespace::autoclean;
  3         10  
  3         90  
13 3     3   23 use Try::Tiny;
  3         6  
  3         35  
14 3     3   236 extends 'App::Sqitch::Command';
  3         6  
  3         3062  
15              
16             our $VERSION = 'v1.3.1'; # VERSION
17              
18             my %FORMATS;
19             $FORMATS{raw} = <<EOF;
20             %{:event}C%e %H%{reset}C%T
21             name %n
22             project %o
23             %{requires}a%{conflicts}aplanner %{name}p <%{email}p>
24             planned %{date:raw}p
25              
26             %{ }B
27             EOF
28              
29             $FORMATS{full} = <<EOF;
30             %{:event}C%L %h%{reset}C%T
31             %{name}_ %n
32             %{project}_ %o
33             %R%X%{planner}_ %p
34             %{planned}_ %{date}p
35              
36             %{ }B
37             EOF
38              
39             $FORMATS{long} = <<EOF;
40             %{:event}C%L %h%{reset}C%T
41             %{name}_ %n
42             %{project}_ %o
43             %{planner}_ %p
44              
45             %{ }B
46             EOF
47              
48             $FORMATS{medium} = <<EOF;
49             %{:event}C%L %h%{reset}C
50             %{name}_ %n
51             %{planner}_ %p
52             %{date}_ %{date}p
53              
54             %{ }B
55             EOF
56              
57             $FORMATS{short} = <<EOF;
58             %{:event}C%L %h%{reset}C
59             %{name}_ %n
60             %{planner}_ %p
61              
62             %{ }s
63             EOF
64              
65             $FORMATS{oneline} = '%{:event}C%h %l%{reset}C %n%{cyan}C%t%{reset}C';
66              
67             has target => (
68             is => 'ro',
69             isa => Str,
70             );
71              
72             has event => (
73             is => 'ro',
74             isa => Str,
75             );
76              
77             has change_pattern => (
78             is => 'ro',
79             isa => Str,
80             );
81              
82             has planner_pattern => (
83             is => 'ro',
84             isa => Str,
85             );
86              
87             has max_count => (
88             is => 'ro',
89             isa => Int,
90             );
91              
92             has skip => (
93             is => 'ro',
94             isa => Int,
95             );
96              
97             has reverse => (
98             is => 'ro',
99             isa => Bool,
100             default => 0,
101             );
102              
103             has headers => (
104             is => 'ro',
105             isa => Bool,
106             default => 1,
107             );
108              
109             has format => (
110             is => 'ro',
111             isa => Str,
112             default => $FORMATS{medium},
113             );
114              
115             has formatter => (
116             is => 'ro',
117             isa => class_type('App::Sqitch::ItemFormatter'),
118             lazy => 1,
119             default => sub { App::Sqitch::ItemFormatter->new },
120             );
121              
122             return qw(
123             event=s
124 2     2 1 7059 target|t=s
125             change-pattern|change=s
126             planner-pattern|planner=s
127             format|f=s
128             date-format|date=s
129             max-count|n=i
130             skip=i
131             reverse!
132             color=s
133             no-color
134             abbrev=i
135             oneline
136             headers!
137             );
138             }
139              
140             my ( $class, $config, $opt ) = @_;
141              
142             # Set base values if --oneline.
143 30     30 1 39614 if ($opt->{oneline}) {
144             $opt->{format} ||= 'oneline';
145             $opt->{abbrev} //= 6;
146 30 100       89 }
147 2   100     36  
148 2   100     12 # Determine and validate the date format.
149             my $date_format = delete $opt->{date_format} || $config->get(
150             key => 'plan.date_format'
151             );
152 30   100     126 if ($date_format) {
153             require App::Sqitch::DateTime;
154             App::Sqitch::DateTime->validate_as_string_format($date_format);
155 30 100       7281 } else {
156 2         787 $date_format = 'iso';
157 2         11 }
158              
159 28         52 # Make sure the plan format is valid.
160             if (my $format = $opt->{format}
161             || $config->get(key => 'plan.format')
162             ) {
163 28 100 100     94 if ($format =~ s/^format://) {
164             $opt->{format} = $format;
165             } else {
166 16 100       159 $opt->{format} = $FORMATS{$format} or hurl plan => __x(
167 1         12 'Unknown plan format "{format}"',
168             format => $format
169 15 100       70 );
170             }
171             }
172              
173             # Determine how to handle ANSI colors.
174             my $color = delete $opt->{no_color} ? 'never'
175             : delete $opt->{color} || $config->get(key => 'plan.color');
176              
177             $opt->{formatter} = App::Sqitch::ItemFormatter->new(
178 26 100 100     1176 ( $date_format ? ( date_format => $date_format ) : () ),
179             ( $color ? ( color => $color ) : () ),
180             ( $opt->{abbrev} ? ( abbrev => delete $opt->{abbrev} ) : () ),
181             );
182              
183 26 50       2773 return $class->SUPER::configure( $config, $opt );
    100          
    100          
184             }
185              
186 26         4666 my $self = shift;
187             my ($targets) = $self->parse_args(
188             target => $self->target,
189             args => \@_,
190 8     8 1 16107 );
191 8         42  
192             # Warn on multiple targets.
193             my $target = shift @{ $targets };
194             $self->warn(__x(
195             'Too many targets specified; using {target}',
196             target => $target->name,
197 7         10 )) if @{ $targets };
  7         14  
198             my $plan = $target->plan;
199              
200             # Exit with status 1 on no changes, probably not expected.
201 7 100       8 hurl {
  7         18  
202 7         236 ident => 'plan',
203             exitval => 1,
204             message => __x(
205 7 100       585 'No changes in {file}',
206             file => $plan->file,
207             ),
208             } unless $plan->count;
209              
210             # Search the changes.
211             my $iter = $plan->search_changes(
212             operation => $self->event,
213             name => $self->change_pattern,
214             planner => $self->planner_pattern,
215 6 100       58 limit => $self->max_count,
216             offset => $self->skip,
217             direction => $self->reverse ? 'DESC' : 'ASC',
218             );
219              
220             # Send the results.
221             my $formatter = $self->formatter;
222             my $format = $self->format;
223             if ($self->headers) {
224             $self->page( '# ', __x 'Project: {project}', project => $plan->project );
225 6         131 $self->page( '# ', __x 'File: {file}', file => $plan->file );
226 6         398 $self->page('');
227 6 100       17 }
228 5         74 while ( my $change = $iter->() ) {
229 5         720 $self->page( $formatter->format( $format, {
230 5         707 event => $change->is_deploy ? 'deploy' : 'revert',
231             project => $change->project,
232 6         120 change_id => $change->id,
233             change => $change->name,
234             note => $change->note,
235             tags => [ map { $_->format_name } $change->tags ],
236             requires => [ map { $_->as_string } $change->requires ],
237             conflicts => [ map { $_->as_string } $change->conflicts ],
238             planned_at => $change->timestamp,
239 1         9 planner_name => $change->planner_name,
240 6         17 planner_email => $change->planner_email,
241 11 100       1540 } ) );
  1         3  
242             }
243              
244             return $self;
245             }
246              
247             1;
248 5         867  
249              
250             =head1 Name
251              
252             App::Sqitch::Command::plan - List the changes in the plan
253              
254             =head1 Synopsis
255              
256             my $cmd = App::Sqitch::Command::plan->new(%params);
257             $cmd->execute;
258              
259             =head1 Description
260              
261             If you want to know how to use the C<plan> command, you probably want to be
262             reading C<sqitch-plan>. But if you really want to know how the C<plan> command
263             works, read on.
264              
265             =head1 Interface
266              
267             =head2 Attributes
268              
269             =head3 C<change_pattern>
270              
271             Regular expression to match against change names.
272              
273             =head3 C<planner_pattern>
274              
275             Regular expression to match against planner names.
276              
277             =head3 C<event>
278              
279             Event type buy which to filter entries to display.
280              
281             =head3 C<format>
282              
283             Display format template.
284              
285             =head3 C<max_count>
286              
287             Maximum number of entries to display.
288              
289             =head3 C<reverse>
290              
291             Reverse the usual order of the display of entries.
292              
293             =head3 C<headers>
294              
295             Output headers. Defaults to true.
296              
297             =head3 C<skip>
298              
299             Number of entries to skip before displaying entries.
300              
301             =head2 Instance Methods
302              
303             =head3 C<execute>
304              
305             $plan->execute;
306              
307             Executes the plan command. The plan will be searched and the results output.
308              
309             =head1 See Also
310              
311             =over
312              
313             =item L<sqitch-plan>
314              
315             Documentation for the C<plan> command to the Sqitch command-line client.
316              
317             =item L<sqitch>
318              
319             The Sqitch command-line client.
320              
321             =back
322              
323             =head1 Author
324              
325             David E. Wheeler <david@justatheory.com>
326              
327             =head1 License
328              
329             Copyright (c) 2012-2022 iovation Inc., David E. Wheeler
330              
331             Permission is hereby granted, free of charge, to any person obtaining a copy
332             of this software and associated documentation files (the "Software"), to deal
333             in the Software without restriction, including without limitation the rights
334             to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
335             copies of the Software, and to permit persons to whom the Software is
336             furnished to do so, subject to the following conditions:
337              
338             The above copyright notice and this permission notice shall be included in all
339             copies or substantial portions of the Software.
340              
341             THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
342             IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
343             FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
344             AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
345             LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
346             OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
347             SOFTWARE.
348              
349             =cut