verify tarballs
[p5sagit/Distar.git] / lib / Distar.pm
CommitLineData
42e08a83 1package Distar;
362ec4af 2use strict;
3use warnings FATAL => 'all';
42e08a83 4use base qw(Exporter);
ca3b3b8a 5use ExtUtils::MakeMaker ();
6use ExtUtils::MM ();
8aac77fc 7use File::Spec ();
42e08a83 8
f6286f60 9our $VERSION = '0.002000';
37e295d8 10$VERSION = eval $VERSION;
11
e076eedb 12my $MM_VER = eval $ExtUtils::MakeMaker::VERSION;
13
42e08a83 14our @EXPORT = qw(
be032607 15 author manifest_include readme_generator
42e08a83 16);
17
18sub import {
2852faf3 19 strict->import;
20 warnings->import(FATAL => 'all');
42e08a83 21 shift->export_to_level(1,@_);
22}
23
e076eedb 24sub author {
25 our $Author = shift;
26 $Author = [ $Author ]
27 if !ref $Author;
28}
42e08a83 29
30our @Manifest = (
31 'lib' => '.pm',
7b9318ce 32 'lib' => '.pod',
42e08a83 33 't' => '.t',
34 't/lib' => '.pm',
35 'xt' => '.t',
36 'xt/lib' => '.pm',
1d950b4a 37 '' => qr{[^/]*\.PL},
f1ef306e 38 '' => qr{Changes|MANIFEST|README|LICENSE|META\.yml},
42e08a83 39 'maint' => qr{[^.].*},
40);
41
42sub manifest_include {
43 push @Manifest, @_;
44}
45
6e83c113 46sub readme_generator {
f6286f60 47 die "readme_generator unsupported" if @_ && $_[0];
6e83c113 48}
49
42e08a83 50sub write_manifest_skip {
0fa73f76 51 my ($mm) = @_;
42e08a83 52 my @files = @Manifest;
53 my @parts;
54 while (my ($dir, $spec) = splice(@files, 0, 2)) {
55 my $re = ($dir ? $dir.'/' : '').
56 ((ref($spec) eq 'Regexp')
57 ? $spec
58 : !ref($spec)
59 ? ".*\Q${spec}\E"
a3e39afd 60 # print ref as well as stringification in case of overload ""
42e08a83 61 : die "spec must be string or regexp, was: ${spec} (${\ref $spec})");
62 push @parts, $re;
63 }
0fa73f76 64 my $dist_name = $mm->{DISTNAME};
65 my $include = join '|', map "${_}\$", @parts;
66 my $final = "^(?:\Q$dist_name\E-v?[0-9_.]+/|(?!$include))";
19916679 67 open my $skip, '>', 'MANIFEST.SKIP'
68 or die "can't open MANIFEST.SKIP: $!";
42e08a83 69 print $skip "${final}\n";
70 close $skip;
71}
72
ca3b3b8a 73{
74 package Distar::MM;
9af7ff2e 75 our @ISA = @MM::ISA;
76 @MM::ISA = (__PACKAGE__);
ca3b3b8a 77
78 sub new {
79 my ($class, $args) = @_;
4762e03a 80 my %test = %{$args->{test}||{}};
81 my $tests = $test{TESTS} || 't/*.t';
82 $tests !~ /\b\Q$_\E\b/ and $tests .= " $_"
83 for 'xt/*.t', 'xt/*/*.t';
84 $test{TESTS} = $tests;
ca3b3b8a 85 return $class->SUPER::new({
180e908e 86 LICENSE => 'perl_5',
53e1282f 87 MIN_PERL_VERSION => '5.006',
0cc25268 88 AUTHOR => ($MM_VER >= 6.5702 ? $Distar::Author : join(', ', @$Distar::Author)),
1f136c70 89 (exists $args->{ABSTRACT} ? () : (ABSTRACT_FROM => $args->{VERSION_FROM})),
4a4bb570 90 %$args,
4762e03a 91 test => \%test,
0a3fdb23 92 realclean => { FILES => (
93 ($args->{realclean}{FILES}||'')
94 . ' Distar/ MANIFEST.SKIP MANIFEST MANIFEST.bak'
95 ) },
ca3b3b8a 96 });
97 }
98
eddb1ce6 99 sub flush {
100 my $self = shift;
5371ff52 101 `git ls-files --error-unmatch MANIFEST.SKIP 2>&1`;
102 my $maniskip_tracked = !$?;
103
104 Distar::write_manifest_skip($self)
105 unless $maniskip_tracked;
eddb1ce6 106 $self->SUPER::flush(@_);
107 }
108
7efea54c 109 sub special_targets {
110 my $self = shift;
111 my $targets = $self->SUPER::special_targets(@_);
f57289e3 112 my $phony_targets = join ' ', qw(
113 preflight
7b418de8 114 check-version
5baee32c 115 check-manifest
b0780d0a 116 check-cpan-upload
f57289e3 117 releasetest
118 release
119 readmefile
120 distmanicheck
121 nextrelease
122 refresh
123 bump
124 bumpmajor
125 bumpminor
126 );
127 $targets =~ s/^(\.PHONY *:.*)/$1 $phony_targets/m;
7efea54c 128 $targets;
129 }
130
ee801c00 131 sub tarfile_target {
132 my $self = shift;
133 my $out = $self->SUPER::tarfile_target(@_);
134 my $verify = <<'END_FRAG';
135 $(ABSPERLRUN) $(HELPERS)/verify-tarball $(DISTVNAME).tar $(DISTVNAME)/MANIFEST --tar="$(TAR)"
136END_FRAG
137 $out =~ s{(\$\(TAR\).*\n)}{$1$verify};
138 $out;
139 }
140
ca3b3b8a 141 sub dist_test {
142 my $self = shift;
8ab5ea70 143
66a2ecde 144 my $include = '';
145 if (open my $fh, '<', 'maint/Makefile.include') {
146 $include = "\n# --- Makefile.include:\n\n" . do { local $/; <$fh> };
147 $include =~ s/\n?\z/\n/;
148 }
b0401762 149
66a2ecde 150 my @bump_targets =
151 grep { $include !~ /^bump$_(?: +\w+)*:/m } ('', 'minor', 'major');
81d518f6 152
8aac77fc 153 my $distar = File::Spec->catdir(
154 File::Spec->catpath((File::Spec->splitpath(__FILE__))[0,1], ''),
155 File::Spec->updir,
156 );
157 my $helpers = File::Spec->catdir($distar, 'helpers');
158
66a2ecde 159 my %vars = (
8aac77fc 160 DISTAR => $self->quote_literal($distar),
161 HELPERS => $self->quote_literal($helpers),
2c75254f 162 REMAKE => join(' ', '$(PERLRUN)', '-I$(DISTAR)/lib', '-mDistar', 'Makefile.PL', map { $self->quote_literal($_) } @ARGV),
190976f8 163 BRANCH => $self->{BRANCH} ||= 'master',
9b920c5c 164 CHANGELOG => $self->{CHANGELOG} ||= 'Changes',
b0780d0a 165 DEV_NULL_STDOUT => ($self->{DEV_NULL} ? '>'.File::Spec->devnull : ''),
263b723e 166 FAKE_RELEASE => '',
f0bbeff7 167 DISTTEST_MAKEFILE_PARAMS => '',
66a2ecde 168 );
169
f0bbeff7 170 my $dist_test = $self->SUPER::dist_test(@_);
171 $dist_test =~ s/(\bMakefile\.PL\b)/$1 \$(DISTTEST_MAKEFILE_PARAMS)/;
172
66a2ecde 173 join('',
f0bbeff7 174 $dist_test,
66a2ecde 175 "\n\n# --- Distar section:\n\n",
176 (map "$_ = $vars{$_}\n", sort keys %vars),
177 <<'END',
81d518f6 178
b0780d0a 179preflight: check-version check-manifest check-cpan-upload
8aac77fc 180 $(ABSPERLRUN) $(HELPERS)/preflight $(VERSION) --changelog=$(CHANGELOG) --branch=$(BRANCH)
7b418de8 181check-version:
8aac77fc 182 $(ABSPERLRUN) $(HELPERS)/check-version $(VERSION) $(TO_INST_PM) $(EXE_FILES)
5baee32c 183check-manifest:
8aac77fc 184 $(ABSPERLRUN) $(HELPERS)/check-manifest
b0780d0a 185check-cpan-upload:
186 $(NOECHO) cpan-upload -h $(DEV_NULL_STDOUT)
3e632431 187releasetest:
f0bbeff7 188 $(MAKE) disttest RELEASE_TESTING=1 DISTTEST_MAKEFILE_PARAMS="PREREQ_FATAL=1" PASTHRU="$(PASTHRU) TEST_FILES=\"$(TEST_FILES)\""
49beea48 189release: preflight
190 $(MAKE) releasetest
42e08a83 191 git commit -a -m "Release commit for $(VERSION)"
401ece0b 192 git tag v$(VERSION) -m "release v$(VERSION)"
49beea48 193 $(RM_RF) $(DISTVNAME)
194 $(MAKE) $(DISTVNAME).tar$(SUFFIX)
263b723e 195 $(NOECHO) $(MAKE) pushrelease FAKE_RELEASE=$(FAKE_RELEASE)
196pushrelease ::
197 $(NOECHO) $(NOOP)
198pushrelease$(FAKE_RELEASE) ::
65a1f7d9 199 cpan-upload $(DISTVNAME).tar$(SUFFIX)
2c636792 200 git push origin v$(VERSION) HEAD
5154970c 201distdir: readmefile
75f8311c 202readmefile: create_distdir
5c5deb0a 203 $(NOECHO) $(TEST_F) $(DISTVNAME)/README || $(MAKE) $(DISTVNAME)/README
f6286f60 204$(DISTVNAME)/README: $(VERSION_FROM)
205 $(NOECHO) $(MKPATH) $(DISTVNAME)
206 pod2text $(VERSION_FROM) >$(DISTVNAME)/README
8aac77fc 207 $(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) README
7934d553 208distsignature: readmefile
de048fa8 209disttest: distmanicheck
792c9e91 210distmanicheck: create_distdir
211 cd $(DISTVNAME) && $(ABSPERLRUN) "-MExtUtils::Manifest=manicheck" -e "exit manicheck"
e7a78651 212nextrelease:
8aac77fc 213 $(ABSPERLRUN) $(HELPERS)/add-changelog-heading --git $(VERSION) $(CHANGELOG)
0edb27b8 214refresh:
41e593ee 215 cd $(DISTAR) && git pull || $(TRUE)
e9f66489 216 $(RM_F) $(FIRST_MAKEFILE)
0edb27b8 217 $(REMAKE)
8ab5ea70 218END
66a2ecde 219 map(sprintf(<<'END', "bump$_", ($_ || '$(V)')), @bump_targets),
220%s:
8aac77fc 221 $(ABSPERLRUN) $(HELPERS)/bump-version --git $(VERSION) %s
66a2ecde 222 $(RM_F) $(FIRST_MAKEFILE)
223 $(REMAKE)
42e08a83 224END
66a2ecde 225 $include,
226 "\n",
227 );
42e08a83 228 }
229}
230
42e08a83 2311;
edb4539a 232__END__
233
234=head1 NAME
235
236Distar - Additions to ExtUtils::MakeMaker for dist authors
237
238=head1 SYNOPSIS
239
240F<Makefile.PL>:
241
242 use ExtUtils::MakeMaker;
83e12da3 243 (do './maint/Makefile.PL.include' or die $@) unless -f 'META.yml';
edb4539a 244
245 WriteMakefile(...);
246
247F<maint/Makefile.PL.include>:
248
249 BEGIN { -e 'Distar' or system("git clone git://git.shadowcat.co.uk/p5sagit/Distar.git") }
250 use lib 'Distar/lib';
251 use Distar 0.001;
252
253 author 'A. U. Thor <author@cpan.org>';
254
255 manifest_include t => 'test-helper.pl';
256 manifest_include corpus => '.txt';
257
258make commmands:
259
260 $ perl Makefile.PL
261 $ make bump # bump version
262 $ make bump V=2.000000 # bump to specific version
263 $ make bumpminor # bump minor version component
264 $ make bumpmajor # bump major version component
265 $ make nextrelease # add version heading to Changes file
266 $ make releasetest # build dist and test (with xt/ and RELEASE_TESTING=1)
267 $ make preflight # check that repo and file state is release ready
83e12da3 268 $ make release # check releasetest and preflight, commits and tags,
269 # builds and uploads to CPAN, and pushes commits and
270 # tag
271 $ make release FAKE_RELEASE=1
272 # builds a release INCLUDING committing and tagging,
273 # but does not upload to cpan or push anything to git
edb4539a 274
275=head1 DESCRIPTION
276
277L<ExtUtils::MakeMaker> works well enough as development tool for
278builting and testing, but using it to release is annoying and error prone.
279Distar adds just enough to L<ExtUtils::MakeMaker> for it to be a usable dist
280author tool. This includes extra commands for releasing and safety checks, and
281automatic generation of some files. It doesn't require any non-core modules and
282is compatible with old versions of perl.
283
284=head1 FUNCTIONS
285
286=head2 author( $author )
287
288Set the author to include in generated META files. Can be a single entry, or
289an arrayref.
290
291=head2 manifest_include( $dir, $pattern )
292
293Add a pattern to include files in the MANIFEST file, and thus in the generated
294dist files.
295
296The pattern can be either a regex, or a path suffix. It will be applied to the
297full path past the directory specified.
298
299The default files that are always included are: F<.pm> and F<.pod> files in
300F<lib>, F<.t> files in F<t> and F<xt>, F<.pm> files in F<t/lib> and F<xt/lib>,
301F<Changes>, F<MANIFEST>, F<README>, F<LICENSE>, F<META.yml>, and F<.PL> files in
302the dist root, and all files in F<maint>.
303
304=head1 AUTOGENERATED FILES
305
306=over 4
307
308=item F<MANIFEST.SKIP>
309
310The F<MANIFEST.SKIP> will be automatically generated to exclude any files not
311explicitly allowed via C<manifest_include> or the included defaults. It will be
312created (or updated) at C<perl Makefile.PL> time.
313
314=item F<README>
315
316The F<README> file will be generated at dist generation time, inside the built
317dist. It will be generated using C<pod2text> on the main module.
318
319If a F<README> file exists in the repo, it will be used directly instead of
320generating the file.
321
322=back
323
324=head1 MAKE COMMMANDS
325
326=head2 test
327
328test will be adjusted to include F<xt/> tests by default. This will only apply
329for authors, not users installing from CPAN.
330
331=head2 release
332
333Releases the dist. Before releasing, checks will be done on the dist using the
334C<preflight> and C<releasetest> commands.
335
336Releasing will generate a dist tarball and upload it to CPAN using cpan-upload.
337It will also create a git tag for the release, and push the tag and branch.
338
83e12da3 339=head3 FAKE_RELEASE
263b723e 340
341If release is run with FAKE_RELEASE=1 set, it will skip uploading to CPAN and
342pushing to git. A release commit will still be created and tagged locally.
343
edb4539a 344=head2 preflight
345
346Performs a number of checks on the files and repository, ensuring it is in a
347sane state to do a release. The checks are:
348
349=over 4
350
351=item * All version numbers match
352
353=item * The F<MANIFEST> file is up to date
354
355=item * The branch is correct
356
357=item * There is no existing tag for the version
358
359=item * There are no unmerged upstream changes
360
361=item * There are no outstanding local changes
362
363=item * There is an appropriate staged Changes heading
364
365=item * cpan-upload is available
366
367=back
368
369=head2 releasetest
370
371Test the dist preparing for a release. This generates a dist dir and runs the
372tests from inside it. This ensures all appropriate files are included inside
373the dist. C<RELEASE_TESTING> will be set in the environment.
374
375=head2 nextrelease
376
377Adds an appropriate changelog heading for the release, and prompts to stage the
378change.
379
380=head2 bump
381
382Bumps the version number. This will try to preserve the length and format of
83e12da3 383the version number. The least significant digit will be incremented. Versions
384with underscores will preserve the underscore in the same position.
edb4539a 385
386Optionally accepts a C<V> option to set the version to a specific value.
387
388The version changes will automatically be committed. Unstaged modifications to
389the files will be left untouched.
390
83e12da3 391=head3 V
392
393The V option will be passed along to the version bumping script. It can accept
394a space separated list of options, including an explicit version number.
395
396Options:
397
398=over 4
399
400=item --force
401
402Updates version numbers even if they do not match the current expected version
403number.
404
405=item --stable
406
407Attempts to convert the updated version to a stable version, removing any
408underscore.
409
410=item --alpha
411
412Attempts to convert the updated version to an alpha version, adding an
413underscore in an appropriate place.
414
415=back
416
edb4539a 417=head2 bumpminor
418
419Like bump, but increments the minor segment of the version. This will treat
420numeric versions as x.yyyzzz format, incrementing the yyy segment.
421
422=head2 bumpmajor
423
424Like bumpminor, but bumping the major segment.
425
426=head2 refresh
427
428Updates Distar and re-runs C<perl Makefile.PL>
429
430=head1 SUPPORT
431
432IRC: #web-simple on irc.perl.org
433
434Git repository: L<git://git.shadowcat.co.uk/p5sagit/Distar>
435
436Git browser: L<http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=p5sagit/Distar.git;a=summary>
437
438=head1 AUTHOR
439
440mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>
441
442=head1 CONTRIBUTORS
443
444haarg - Graham Knop (cpan:HAARG) <haarg@cpan.org>
445
83e12da3 446ether - Karen Etheridge (cpan:ETHER) <ether@cpan.org>
edb4539a 447
448frew - Arthur Axel "fREW" Schmidt (cpan:FREW) <frioux@gmail.com>
449
450Mithaldu - Christian Walde (cpan:MITHALDU) <walde.christian@googlemail.com>
451
452=head1 COPYRIGHT
453
454Copyright (c) 2011-2015 the Distar L</AUTHOR> and L</CONTRIBUTORS>
455as listed above.
456
457=head1 LICENSE
458
459This library is free software and may be distributed under the same terms
460as perl itself. See L<http://dev.perl.org/licenses/>.
461
462=cut