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