File Coverage

blib/lib/Dist/Zilla/Plugin/MetaProvides/Update.pm
Criterion Covered Total %
statement 18 39 46.1
branch 1 18 5.5
condition n/a
subroutine 6 6 100.0
pod 0 1 0.0
total 25 64 39.0


line stmt bran cond sub pod time code
1 1     1   203458 use strict;
  1         2  
  1         28  
2 1     1   5 use warnings;
  1         3  
  1         53  
3             package Dist::Zilla::Plugin::MetaProvides::Update;
4             # vim: set ts=8 sts=4 sw=4 tw=115 et :
5             # ABSTRACT: A plugin to fix "provides" metadata after [RewriteVersion] modified $VERSION declarations
6              
7             our $VERSION = '0.001';
8              
9 1     1   6 use Moose;
  1         2  
  1         5  
10             with 'Dist::Zilla::Role::FileMunger';
11 1     1   5653 use Moose::Util 'find_meta';
  1         2  
  1         7  
12 1     1   211 use namespace::autoclean;
  1         2  
  1         9  
13              
14             sub munge_files
15             {
16 1     1 0 1094591 my $self = shift;
17              
18             # re-calculate 'provides' metadata and copy it back to distmeta.
19              
20 1         39 my $zilla = $self->zilla;
21 1 50       18 return if not find_meta($zilla)->find_attribute_by_name('distmeta')->has_value($zilla);
22 0           my $distmeta = $zilla->distmeta;
23              
24             # if there is no provides metadata, then there is nothing to do (yet). Maybe we ran too soon?
25 0 0         return if not $distmeta->{provides};
26              
27             # find the plugin that does Dist::Zilla::Role::MetaProvider::Provider
28             # die if we can't find one -- *something* populated $distmeta->{provides}!
29              
30 0           my @provides_plugins = grep { $_->does('Dist::Zilla::Role::MetaProvider::Provider') } @{$zilla->plugins};
  0            
  0            
31 0 0         $self->log_fatal('failed to find any Dist::Zilla::Role::MetaProvider::Provider plugins -- what populated provides?!') if not @provides_plugins;
32              
33 0           foreach my $new_metadata (map { $_->metadata } @provides_plugins)
  0            
34             {
35 0           foreach my $module (keys %{$new_metadata->{provides}})
  0            
36             {
37             $self->log_fatal('could not find provides entry for %s in original distmeta; did plugin add it in the wrong phase?')
38 0 0         if not exists $distmeta->{provides}{$module};
39              
40 0 0         if ($distmeta->{provides}{$module}{file} ne $new_metadata->{provides}{$module}{file})
41             {
42             $self->log([ 'filename for module %s has changed (%s to %s) -- provides plugin is running too late',
43 0           $module, $distmeta->{provides}{$module}{file}, $new_metadata->{provides}{$module}{file} ]);
44 0           $distmeta->{provides}{$module}{file} = $new_metadata->{provides}{$module}{file};
45             }
46              
47             # if version has disappeared, die
48 0 0         if (not exists $new_metadata->{provides}{$module}{version})
49             {
50             $self->log_fatal([ 'metaprovides version for %s has disappeared!', $module ])
51 0 0         if exists $distmeta->{provides}{$module}{version};
52             }
53             # if version has been added or changed, warn and update.
54             else
55             {
56             # new version exists.
57             # if old did not exist, add it and log.
58 0 0         if (not exists $distmeta->{provides}{$module}{version})
    0          
59             {
60             $self->log([ '$VERSION for %s has been added (as %s): fixing provides metadata',
61 0           $module, $new_metadata->{provides}{$module}{version} ]);
62 0           $distmeta->{provides}{$module}{version} = $new_metadata->{provides}{$module}{version};
63             }
64             # if old did exist, check equality.
65             elsif ($distmeta->{provides}{$module}{version} ne $new_metadata->{provides}{$module}{version})
66             {
67             $self->log([ '$VERSION for %s has been changed from %s to %s: fixing provides metadata',
68 0           $module, $distmeta->{provides}{$module}{version}, $new_metadata->{provides}{$module}{version} ]);
69 0           $distmeta->{provides}{$module}{version} = $new_metadata->{provides}{$module}{version};
70             }
71             }
72             }
73             }
74             }
75              
76             __PACKAGE__->meta->make_immutable;
77              
78             __END__
79              
80             =pod
81              
82             =encoding UTF-8
83              
84             =for stopwords FileMunging MetaProvider OurPkgVersion PkgVersion PodWeaver RewriteVersion
85              
86             =head1 NAME
87              
88             Dist::Zilla::Plugin::MetaProvides::Update - A plugin to fix "provides" metadata after [RewriteVersion] modified $VERSION declarations
89              
90             =head1 VERSION
91              
92             version 0.001
93              
94             =head1 SYNOPSIS
95              
96             In your F<dist.ini> (or a plugin bundle that effectively does the same thing):
97              
98             [MetaProvides::*] ; e.g. ::Class, ::Package, ::FromFile
99             ...
100             [RewriteVersion]
101             [MetaProvides::Update]
102              
103             =head1 DESCRIPTION
104              
105             =for Pod::Coverage munge_files
106              
107             This plugin is a hack and hopefully will soon be made redundant. It is bundled along with a (the?)
108             bundle that needs it, but can also be used with other bundles if such a need is identified.
109              
110             This plugin is meant to be run after all other file mungers, but most particularly after
111             L<[RewriteVersion]|Dist::Zilla::Plugin::RewriteVersion>. Because plugin bundles contain many plugins,
112             it can be difficult or impossible to arrange the order of plugins and bundles in F<dist.ini> such that
113             all ordering constraints are correctly satisfied.
114              
115             The specific ordering problem that this plugin is correcting for is this:
116              
117             =over 4
118              
119             =item *
120              
121             a plugin runs in the FileMunging phase that requires metadata (in my case, I typically see this with L<[PodWeaver]|Dist::Zilla::Plugin::PodWeaver>)
122              
123             =item *
124              
125             this prompts MetaProvider plugins to run, one of which populates L<"provides" metadata|CPAN::Meta::Spec/provides>
126              
127             =item *
128              
129             all F<.pm> files in the distribution are scanned and their C<$VERSION> declarations are extracted
130              
131             =item *
132              
133             a subsequent FileMunging plugin adds or mutates these C<$VERSION> declarations
134              
135             =item *
136              
137             now the "provides" metadata is incorrect.
138              
139             =back
140              
141             Incorrect "provides" metadata is a big deal, because this metadata is treated as authoritative by PAUSE and can
142             result in incorrect package indexing.
143              
144             There are many C<$VERSION>-mutating plugins, such as:
145              
146             =over 4
147              
148             =item *
149              
150             L<[PkgVersion]|Dist::Zilla::Plugin::PkgVersion>
151              
152             =item *
153              
154             L<[OurPkgVersion]|Dist::Zilla::Plugin::OurPkgVersion>
155              
156             =item *
157              
158             L<[RewriteVersion]|Dist::Zilla::Plugin::RewriteVersion> (and its derivative, L<[RewriteVersion::Transitional]|Dist::Zilla::Plugin::RewriteVersion::Transitional>)
159              
160             =back
161              
162             Careful ordering of plugins can be used to avoid this issue: as long as the plugin that populates "provides"
163             metadata appears in the configuration B<after> the plugin that mutates C<$VERSION>, everything works correctly. In
164             L<my author bundle|Dist::Zilla::PluginBundle::Author::ETHER>, I would prefer to list
165             L<[RewriteVersion::Transitional]|Dist::Zilla::Plugin::RewriteVersion::Transitional> at the very beginning of the
166             plugin list, to ensure module files are munged before any other plugins inspect them.
167             However, correct ordering may no longer be possible if plugins are added from sub-bundles. I ran
168             into this exact scenario when writing L<[@Git::VersionManager]|Dist::Zilla::PluginBundle::Git::VersionManager> --
169             all the plugins (except for L<[RewriteVersion::Transitional]|Dist::Zilla::Plugin::RewriteVersion::Transitional>)
170             are after-release plugins, and need to run after other after-release plugins that the user may be using, so this
171             likely results in the placement of the "provides" metadata-populating plugin as before these plugins.
172              
173             My hacky (and hopefully temporary) solution is this plugin which runs after the C<$VERSION> declaration is mutated,
174             and hunts for the previous "provides" metadata-populating plugin and re-runs it, to update the metadata. Ideally,
175             that plugin should do this itself as late as possible (such as in the Prereq Source phase), after all file munging is
176             complete.
177              
178             =head1 SEE ALSO
179              
180             =over 4
181              
182             =item *
183              
184             L<MetaProvides issue #8: warn when $VERSIONs are extracted from files too soon|https://github.com/kentnl/Dist-Zilla-Plugin-MetaProvides/issues/8>
185              
186             =item *
187              
188             L<[@Git::VersionManager]|Dist::Zilla::PluginBundle::Git::VersionManager>
189              
190             =item *
191              
192             L<Dist::Zilla::Role::MetaProvider::Provider>
193              
194             =item *
195              
196             L<[MetaProvides::Package]|Dist::Zilla::Plugin::MetaProvides::Package>
197              
198             =back
199              
200             =head1 SUPPORT
201              
202             Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=Dist-Zilla-PluginBundle-Git-VersionManager>
203             (or L<bug-Dist-Zilla-PluginBundle-Git-VersionManager@rt.cpan.org|mailto:bug-Dist-Zilla-PluginBundle-Git-VersionManager@rt.cpan.org>).
204              
205             There is also a mailing list available for users of this distribution, at
206             L<http://dzil.org/#mailing-list>.
207              
208             There is also an irc channel available for users of this distribution, at
209             L<C<#distzilla> on C<irc.perl.org>|irc://irc.perl.org/#distzilla>.
210              
211             I am also usually active on irc, as 'ether' at C<irc.perl.org>.
212              
213             =head1 AUTHOR
214              
215             Karen Etheridge <ether@cpan.org>
216              
217             =head1 COPYRIGHT AND LICENCE
218              
219             This software is copyright (c) 2017 by Karen Etheridge.
220              
221             This is free software; you can redistribute it and/or modify it under
222             the same terms as the Perl 5 programming language system itself.
223              
224             =cut