File Coverage

blib/lib/Dist/Zilla/App/Command/bakeini.pm
Criterion Covered Total %
statement 39 41 95.1
branch 7 12 58.3
condition n/a
subroutine 8 8 100.0
pod n/a
total 54 61 88.5


line stmt bran cond sub pod time code
1 5     5   659739 use 5.006;
  5         19  
  5         150  
2 5     5   20 use strict;
  5         6  
  5         131  
3 5     5   22 use warnings;
  5         9  
  5         269  
4              
5             package Dist::Zilla::App::Command::bakeini;
6              
7             our $VERSION = '0.002005';
8              
9             # ABSTRACT: bake dist.ini to not need the bundles.
10              
11             our $AUTHORITY = 'cpan:KENTNL'; # AUTHORITY
12              
13 5     5   391 use Dist::Zilla::App '-command';
  5         35384  
  5         32  
14              
15             ## no critic (NamingConventions::ProhibitAmbiguousNames)
16 1     1   44588 sub abstract { return 'bake dist.ini from dist.ini.meta' }
17              
18             sub opt_spec {
19             ## no critic (RegularExpressions::ProhibitFixedStringMatches,RegularExpressions::RequireLineBoundaryMatching)
20              
21             return (
22 3     3   118640 [ 'root=s' => 'the root of the dist; defaults to .' ],
23             [
24             'comments=s' => 'include all, authordeps or none comments; defaults to all',
25             {
26             default => 'all',
27             regex => qr/\A(?:all|authordeps|none)\z/sx,
28             },
29             ],
30             );
31             }
32              
33             sub validate_args {
34 3     3   1682 my ( undef, $opt, undef ) = @_;
35 3         27 require Path::Tiny;
36              
37 3         8 my $root = $opt->root;
38 3 50       31 $root = Path::Tiny::path($root) if defined $root;
39 3 50       21 $root = Path::Tiny::cwd() if not defined $root;
40              
41 3 100       141 return if $root->child('dist.ini.meta')->is_file;
42 1         122 require Carp;
43 1         36 Carp::croak("dist.ini.meta not found in $root");
44             }
45              
46             sub execute {
47 2     2   151 my ( undef, $opt, undef ) = @_;
48 2         9 require Path::Tiny;
49              
50 2         7 my $root = $opt->root;
51 2 50       9 $root = Path::Tiny::path($root) if defined $root;
52 2 50       7 $root = Path::Tiny::cwd() if not defined $root;
53              
54 2         37 my $file = $root->child('dist.ini.meta');
55              
56 2         1063 require Dist::Zilla::Util::ExpandINI;
57 2         20946 Dist::Zilla::Util::ExpandINI->VERSION('0.003000');
58 2         16 my $state = Dist::Zilla::Util::ExpandINI->new( comments => $opt->comments );
59 2         2725 $state->_load_file($file);
60 2         17233 $state->_expand();
61 2         123116 my $out = $root->child('dist.ini')->openw_utf8;
62 2         2606 my $return = print {$out} "; This file is generated from dist.ini.meta by dzil bakeini.\n",
  2         15  
63             "; Edit that file or the bundles contained within for long-term changes.\n";
64              
65 2 50       8 if ( not $return ) {
66 0         0 require Carp;
67 0         0 Carp::croak("Error writing to dist.ini! $? $! $@");
68             }
69 2         11 $state->_store_handle($out);
70 2         11736 return;
71             }
72              
73             1;
74              
75             __END__
76              
77             =pod
78              
79             =encoding UTF-8
80              
81             =head1 NAME
82              
83             Dist::Zilla::App::Command::bakeini - bake dist.ini to not need the bundles.
84              
85             =head1 VERSION
86              
87             version 0.002005
88              
89             =head1 SYNOPSIS
90              
91             cp dist.ini dist.ini.meta
92             dzil bakeini
93              
94             less dist.ini # no more bundles :D
95              
96             =head1 DESCRIPTION
97              
98             C<bakeini> is an C<App::Command> module for C<Dist::Zilla> that enables one to have two versions
99             of their C<dist.ini>, one which contains their bundle, and the other which is generated from the
100             first in a static and portable way, without requiring the bundle to be present.
101              
102             This allows contributors and test targets to have a mostly "static" configuration that is less
103             prone to randomly breaking your distributions every time you change something significant in your bundle.
104              
105             It also allows contributors to only need the dependencies they B<really> need, not the super-set
106             of dependencies your bundle probably implies.
107              
108             And at the same time, you still have the flexibility and power you normally have with a centralized
109             configuration stored in a bundle, which you can roll out on demand, instead of having the roll out
110             automatically propagate every time the bundle gets updated.
111              
112             =head1 DISCUSSION
113              
114             =head2 The Quibbles
115              
116             There's several long standing point of contention surrounding the use of bundles.
117              
118             A few poignant ones that bother me are:
119              
120             =over 4
121              
122             =item * Bundles change over time and configuration parameters can change in validity
123              
124             For example, I might add a requirement in a later incarnation of a bundle that a given parameter be specified. But that creates
125             a confusing backwards compatibility problem for people who merely want to check out and build the code.
126              
127             =item * Some contributors tend not to like dealing with bundles due to bundle complexity
128              
129             Bundles often declare far more dependencies than contributors B<need> to build one specific distribution, and the bundle
130             obscures the visibility of what plugins are being used.
131              
132             This also manifests as a difficulty to work around problems produced by bundles such as bundles C<use>-ing broken modules,
133             which is not straight forward to iron out with the C<@Filter> bundle.
134              
135             C<@Filter> is also complicated for end users who are not familiar with C<dzil> to use, and C<@Filter> also lacks abilities to
136             re-order plugins if that is necessary to avoid a bug.
137              
138             Additionally, routing configuration to a single plugin within a bundle can be confusing with messy syntax, especially if the
139             bundle doesn't C<do> C<ConfigSlicer> or something like that.
140              
141             And the effort of learning and using those tools is high if all you want to do is I<temporarily> change a build setting for the
142             point of local use or local testing.
143              
144             =back
145              
146             =head2 The Benefits and Method
147              
148             So this command attempts to avoid these problems by separating the bundle from its configuration until configuration is wanted
149             updated.
150              
151             This means C<Dist::Zilla> based distributions B<DON'T> have their build configuration radically changed simply because somebody
152             upgraded a bundle, and the configuration is I<MORE> local to the distribution instead of being more global.
153              
154             This means bundle specific configuration demands B<ONLY> need to be satisfied during the baking process, but B<NOT> every
155             subsequent build, and are thus B<NOT> prone to causing a sea of unusable C<dist.ini>s if a bundle gets changed.
156              
157             =head2 The Downsides
158              
159             The biggest known downside of this approach at present is with much more advanced bundle usage.
160              
161             Because the bundle itself is being taken out of the loop, that means C<dist.ini> will B<NOT> be able to automatically have new
162             plugins added to it in response to changes in the tree. C<dzil bakeini> will have to be run subsequently to take tree changes
163             into consideration and emit updated configuration.
164              
165             And because the bundle itself is being taken out of the loop, that means C<ENV> based controls in bundles will be bound at the
166             time of calling C<dzil bakeini>, which means if you're like C<@ETHER> and have an "Airplane mode", then:
167              
168             AIRPLANE=1 dzil build
169              
170             Won't work on a baked C<dist.ini>, and you will instead need:
171              
172             AIRPLANE=1 dzil bakeini && dzil build
173              
174             Though, that could be beneficial too depending on how you use it.
175              
176             # Get on the plane
177             AIRPLANE=1 dzil bakeini
178              
179             # dzil runs everything in airplane mode now
180             dzil build
181              
182             # Get off the plane
183             dzil bakeini
184              
185             # dzil runs normally
186             dzil build
187              
188             =head1 TIPS AND TRICKS
189              
190             =head2 C<bakeini> dependent behavior in a bundle
191              
192             If you want to codify some unique behavior to how your bundle performs under C<dzil bakeini>, ( for instance, to change the C<prereqs> advertised as being C<develop.requires> )
193              
194             Here, L<< C<::Util::CurrentCmd>|Dist::Zilla::Util::CurrentCmd >> comes in handy:
195              
196             use Dist::Zilla::Util::CurrentCmd qw(current_cmd);
197              
198             my @config;
199             ...
200             if ( 'bakeini' eq ( current_cmd() || '' ) ) {
201             push @config, [ 'baked dist prereqs', 'Dist::Zilla::Plugin::Prereqs', { 'Foo::Bar' => 2 }];
202             } else {
203             ...
204             }
205              
206             =head1 PARAMETERS
207              
208             =head2 C<--comments>
209              
210             C<--comments> allows to control which comments are copied into the target C<dist.ini>
211              
212             =head3 C<all>
213              
214             B<DEFAULT> Inject all comments regardless
215              
216             =head3 C<authordeps>
217              
218             Inject all comments that are C<Dist::Zilla> C<AuthorDeps>
219              
220             =head3 C<none>
221              
222             Inject no comments.
223              
224             =head1 AUTHOR
225              
226             Kent Fredric <kentnl@cpan.org>
227              
228             =head1 COPYRIGHT AND LICENSE
229              
230             This software is copyright (c) 2015 by Kent Fredric <kentfredric@gmail.com>.
231              
232             This is free software; you can redistribute it and/or modify it under
233             the same terms as the Perl 5 programming language system itself.
234              
235             =cut