update docs
[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
ca3b3b8a 131 sub dist_test {
132 my $self = shift;
8ab5ea70 133
66a2ecde 134 my $include = '';
135 if (open my $fh, '<', 'maint/Makefile.include') {
136 $include = "\n# --- Makefile.include:\n\n" . do { local $/; <$fh> };
137 $include =~ s/\n?\z/\n/;
138 }
b0401762 139
66a2ecde 140 my @bump_targets =
141 grep { $include !~ /^bump$_(?: +\w+)*:/m } ('', 'minor', 'major');
81d518f6 142
8aac77fc 143 my $distar = File::Spec->catdir(
144 File::Spec->catpath((File::Spec->splitpath(__FILE__))[0,1], ''),
145 File::Spec->updir,
146 );
147 my $helpers = File::Spec->catdir($distar, 'helpers');
148
66a2ecde 149 my %vars = (
8aac77fc 150 DISTAR => $self->quote_literal($distar),
151 HELPERS => $self->quote_literal($helpers),
2c75254f 152 REMAKE => join(' ', '$(PERLRUN)', '-I$(DISTAR)/lib', '-mDistar', 'Makefile.PL', map { $self->quote_literal($_) } @ARGV),
190976f8 153 BRANCH => $self->{BRANCH} ||= 'master',
9b920c5c 154 CHANGELOG => $self->{CHANGELOG} ||= 'Changes',
b0780d0a 155 DEV_NULL_STDOUT => ($self->{DEV_NULL} ? '>'.File::Spec->devnull : ''),
263b723e 156 FAKE_RELEASE => '',
66a2ecde 157 );
158
159 join('',
160 $self->SUPER::dist_test(@_),
161 "\n\n# --- Distar section:\n\n",
162 (map "$_ = $vars{$_}\n", sort keys %vars),
163 <<'END',
81d518f6 164
b0780d0a 165preflight: check-version check-manifest check-cpan-upload
8aac77fc 166 $(ABSPERLRUN) $(HELPERS)/preflight $(VERSION) --changelog=$(CHANGELOG) --branch=$(BRANCH)
7b418de8 167check-version:
8aac77fc 168 $(ABSPERLRUN) $(HELPERS)/check-version $(VERSION) $(TO_INST_PM) $(EXE_FILES)
5baee32c 169check-manifest:
8aac77fc 170 $(ABSPERLRUN) $(HELPERS)/check-manifest
b0780d0a 171check-cpan-upload:
172 $(NOECHO) cpan-upload -h $(DEV_NULL_STDOUT)
3e632431 173releasetest:
ba5a4b50 174 $(MAKE) disttest RELEASE_TESTING=1 PASTHRU="$(PASTHRU) TEST_FILES=\"$(TEST_FILES)\""
49beea48 175release: preflight
176 $(MAKE) releasetest
42e08a83 177 git commit -a -m "Release commit for $(VERSION)"
401ece0b 178 git tag v$(VERSION) -m "release v$(VERSION)"
49beea48 179 $(RM_RF) $(DISTVNAME)
180 $(MAKE) $(DISTVNAME).tar$(SUFFIX)
263b723e 181 $(NOECHO) $(MAKE) pushrelease FAKE_RELEASE=$(FAKE_RELEASE)
182pushrelease ::
183 $(NOECHO) $(NOOP)
184pushrelease$(FAKE_RELEASE) ::
65a1f7d9 185 cpan-upload $(DISTVNAME).tar$(SUFFIX)
2c636792 186 git push origin v$(VERSION) HEAD
5154970c 187distdir: readmefile
75f8311c 188readmefile: create_distdir
5c5deb0a 189 $(NOECHO) $(TEST_F) $(DISTVNAME)/README || $(MAKE) $(DISTVNAME)/README
f6286f60 190$(DISTVNAME)/README: $(VERSION_FROM)
191 $(NOECHO) $(MKPATH) $(DISTVNAME)
192 pod2text $(VERSION_FROM) >$(DISTVNAME)/README
8aac77fc 193 $(NOECHO) $(ABSPERLRUN) $(HELPERS)/add-to-manifest -d $(DISTVNAME) README
7934d553 194distsignature: readmefile
de048fa8 195disttest: distmanicheck
792c9e91 196distmanicheck: create_distdir
197 cd $(DISTVNAME) && $(ABSPERLRUN) "-MExtUtils::Manifest=manicheck" -e "exit manicheck"
e7a78651 198nextrelease:
8aac77fc 199 $(ABSPERLRUN) $(HELPERS)/add-changelog-heading --git $(VERSION) $(CHANGELOG)
0edb27b8 200refresh:
41e593ee 201 cd $(DISTAR) && git pull || $(TRUE)
e9f66489 202 $(RM_F) $(FIRST_MAKEFILE)
0edb27b8 203 $(REMAKE)
8ab5ea70 204END
66a2ecde 205 map(sprintf(<<'END', "bump$_", ($_ || '$(V)')), @bump_targets),
206%s:
8aac77fc 207 $(ABSPERLRUN) $(HELPERS)/bump-version --git $(VERSION) %s
66a2ecde 208 $(RM_F) $(FIRST_MAKEFILE)
209 $(REMAKE)
42e08a83 210END
66a2ecde 211 $include,
212 "\n",
213 );
42e08a83 214 }
215}
216
42e08a83 2171;
edb4539a 218__END__
219
220=head1 NAME
221
222Distar - Additions to ExtUtils::MakeMaker for dist authors
223
224=head1 SYNOPSIS
225
226F<Makefile.PL>:
227
228 use ExtUtils::MakeMaker;
83e12da3 229 (do './maint/Makefile.PL.include' or die $@) unless -f 'META.yml';
edb4539a 230
231 WriteMakefile(...);
232
233F<maint/Makefile.PL.include>:
234
235 BEGIN { -e 'Distar' or system("git clone git://git.shadowcat.co.uk/p5sagit/Distar.git") }
236 use lib 'Distar/lib';
237 use Distar 0.001;
238
239 author 'A. U. Thor <author@cpan.org>';
240
241 manifest_include t => 'test-helper.pl';
242 manifest_include corpus => '.txt';
243
244make commmands:
245
246 $ perl Makefile.PL
247 $ make bump # bump version
248 $ make bump V=2.000000 # bump to specific version
249 $ make bumpminor # bump minor version component
250 $ make bumpmajor # bump major version component
251 $ make nextrelease # add version heading to Changes file
252 $ make releasetest # build dist and test (with xt/ and RELEASE_TESTING=1)
253 $ make preflight # check that repo and file state is release ready
83e12da3 254 $ make release # check releasetest and preflight, commits and tags,
255 # builds and uploads to CPAN, and pushes commits and
256 # tag
257 $ make release FAKE_RELEASE=1
258 # builds a release INCLUDING committing and tagging,
259 # but does not upload to cpan or push anything to git
edb4539a 260
261=head1 DESCRIPTION
262
263L<ExtUtils::MakeMaker> works well enough as development tool for
264builting and testing, but using it to release is annoying and error prone.
265Distar adds just enough to L<ExtUtils::MakeMaker> for it to be a usable dist
266author tool. This includes extra commands for releasing and safety checks, and
267automatic generation of some files. It doesn't require any non-core modules and
268is compatible with old versions of perl.
269
270=head1 FUNCTIONS
271
272=head2 author( $author )
273
274Set the author to include in generated META files. Can be a single entry, or
275an arrayref.
276
277=head2 manifest_include( $dir, $pattern )
278
279Add a pattern to include files in the MANIFEST file, and thus in the generated
280dist files.
281
282The pattern can be either a regex, or a path suffix. It will be applied to the
283full path past the directory specified.
284
285The default files that are always included are: F<.pm> and F<.pod> files in
286F<lib>, F<.t> files in F<t> and F<xt>, F<.pm> files in F<t/lib> and F<xt/lib>,
287F<Changes>, F<MANIFEST>, F<README>, F<LICENSE>, F<META.yml>, and F<.PL> files in
288the dist root, and all files in F<maint>.
289
290=head1 AUTOGENERATED FILES
291
292=over 4
293
294=item F<MANIFEST.SKIP>
295
296The F<MANIFEST.SKIP> will be automatically generated to exclude any files not
297explicitly allowed via C<manifest_include> or the included defaults. It will be
298created (or updated) at C<perl Makefile.PL> time.
299
300=item F<README>
301
302The F<README> file will be generated at dist generation time, inside the built
303dist. It will be generated using C<pod2text> on the main module.
304
305If a F<README> file exists in the repo, it will be used directly instead of
306generating the file.
307
308=back
309
310=head1 MAKE COMMMANDS
311
312=head2 test
313
314test will be adjusted to include F<xt/> tests by default. This will only apply
315for authors, not users installing from CPAN.
316
317=head2 release
318
319Releases the dist. Before releasing, checks will be done on the dist using the
320C<preflight> and C<releasetest> commands.
321
322Releasing will generate a dist tarball and upload it to CPAN using cpan-upload.
323It will also create a git tag for the release, and push the tag and branch.
324
83e12da3 325=head3 FAKE_RELEASE
263b723e 326
327If release is run with FAKE_RELEASE=1 set, it will skip uploading to CPAN and
328pushing to git. A release commit will still be created and tagged locally.
329
edb4539a 330=head2 preflight
331
332Performs a number of checks on the files and repository, ensuring it is in a
333sane state to do a release. The checks are:
334
335=over 4
336
337=item * All version numbers match
338
339=item * The F<MANIFEST> file is up to date
340
341=item * The branch is correct
342
343=item * There is no existing tag for the version
344
345=item * There are no unmerged upstream changes
346
347=item * There are no outstanding local changes
348
349=item * There is an appropriate staged Changes heading
350
351=item * cpan-upload is available
352
353=back
354
355=head2 releasetest
356
357Test the dist preparing for a release. This generates a dist dir and runs the
358tests from inside it. This ensures all appropriate files are included inside
359the dist. C<RELEASE_TESTING> will be set in the environment.
360
361=head2 nextrelease
362
363Adds an appropriate changelog heading for the release, and prompts to stage the
364change.
365
366=head2 bump
367
368Bumps the version number. This will try to preserve the length and format of
83e12da3 369the version number. The least significant digit will be incremented. Versions
370with underscores will preserve the underscore in the same position.
edb4539a 371
372Optionally accepts a C<V> option to set the version to a specific value.
373
374The version changes will automatically be committed. Unstaged modifications to
375the files will be left untouched.
376
83e12da3 377=head3 V
378
379The V option will be passed along to the version bumping script. It can accept
380a space separated list of options, including an explicit version number.
381
382Options:
383
384=over 4
385
386=item --force
387
388Updates version numbers even if they do not match the current expected version
389number.
390
391=item --stable
392
393Attempts to convert the updated version to a stable version, removing any
394underscore.
395
396=item --alpha
397
398Attempts to convert the updated version to an alpha version, adding an
399underscore in an appropriate place.
400
401=back
402
edb4539a 403=head2 bumpminor
404
405Like bump, but increments the minor segment of the version. This will treat
406numeric versions as x.yyyzzz format, incrementing the yyy segment.
407
408=head2 bumpmajor
409
410Like bumpminor, but bumping the major segment.
411
412=head2 refresh
413
414Updates Distar and re-runs C<perl Makefile.PL>
415
416=head1 SUPPORT
417
418IRC: #web-simple on irc.perl.org
419
420Git repository: L<git://git.shadowcat.co.uk/p5sagit/Distar>
421
422Git browser: L<http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=p5sagit/Distar.git;a=summary>
423
424=head1 AUTHOR
425
426mst - Matt S. Trout (cpan:MSTROUT) <mst@shadowcat.co.uk>
427
428=head1 CONTRIBUTORS
429
430haarg - Graham Knop (cpan:HAARG) <haarg@cpan.org>
431
83e12da3 432ether - Karen Etheridge (cpan:ETHER) <ether@cpan.org>
edb4539a 433
434frew - Arthur Axel "fREW" Schmidt (cpan:FREW) <frioux@gmail.com>
435
436Mithaldu - Christian Walde (cpan:MITHALDU) <walde.christian@googlemail.com>
437
438=head1 COPYRIGHT
439
440Copyright (c) 2011-2015 the Distar L</AUTHOR> and L</CONTRIBUTORS>
441as listed above.
442
443=head1 LICENSE
444
445This library is free software and may be distributed under the same terms
446as perl itself. See L<http://dev.perl.org/licenses/>.
447
448=cut