Dist::Zilla - 2009-12-11

I Hate Wrapping

I like to give people things that they like. It's nice for someone to open a present and find just the thing they wanted -- or better, something that they didn't know they wanted, but that they're elated to receive. It's a nice feeling. I just don't like wrapping presents. I have to pick wrapping paper, measure it, cut it, tape it, and try not to make it look like a five year-old did it. I generally fail at all of these tasks and hand people presents that are balled up in tape and crumpled paper.

It's the same feeling I get when I release code to the CPAN: I'm excited that people might find it useful, even if they hadn't realized they'd wanted something like that -- but I'm annoyed at the idea of writing up all the stupid boilerplate: generic hunks of Pod, build files, the same tests I use everywhere else, license documents. It's just a pain, and I still end up with a big red "You forgot the README!" light flashing at me on CPANTS. At least, I used to.

First, to fight this problem, I started using Module::Starter, and I contributed a number of changes to make it easier to extend. I didn't get it very easy to extend though, just slightly more than it had been. In the end, too, it really only got you started, as the name suggested. Every time you wanted to make a new release or add a file or change something you'd put in all the files, you realized that Module::Starter stopped being useful well before your first release.

I tried to use Module::Install to deal with this problem, and it helped a little. It could add a few files that I'd need generated like license documents or tests. It just didn't do enough -- and it was such a pain to extend, being built with weird fundamental design decisions atop the ancient and insane ExtUtils::MakeMaker.

My Own Private Gift Counter

Finally I decided that all I really wanted was a way to make make install way, way more powerful and hookable. I made a little flowchart of the steps I wanted to perform and then build a little skeleton of code to hang those steps on, and that skeleton was Dist::Zilla. Dist::Zilla, as the name suggests, is an absolute monster. It only needs to be run by maintainers of CPAN distribution, not installers, so it doesn't shy away from demanding all sorts of hefty prerequisites.

Here's how I use Dist::Zilla, more or less, although there's more than one way to do it:

~$ cd code ~/code$ dzil new My-Project will create new dist My-Project in /Users/rjbs/code/My-Project ~/code$ cd My-Project ~/code/My-Project$ vi dist.ini

Most of my dist.ini, the master configuration file, is set up for me already -- it's only about five lines -- but I edit it a bit to make it look like this:



1:

2:

3:

4:

5:

6:

7:

8:



name = My-Project

author = Ricardo Signes <rjbs@cpan.org>

license = Perl_5

copyright_holder = Ricardo Signes



[@RJBS]

[@Git]

[AutoPrereq]



Now I'm ready to start coding. I'll make a t/my-tests.t and lib/My/Project.pm. My module might look something like this:



1:

2:

3:

4:

5:

6:

7:

8:

9:

10:

11:

12:

13:

14:

15:

16:

17:

18:

19:



use strict ;

package My::Project ;



use Games::Bowling::Scorecard ;

use Games::War::Nuclear::Thermonuclear::Global ;

use Path::Resolver 2.012 ;



=method play_a_game



$project->play_a_game($num_of_players);



This method starts a game. It's a strange game.



=cut



sub play_a_game { ... }



1 ;



You can load up your Changes, too, just adding the details of your release so the file looks like:

Revision history for {{$dist->name}} {{$NEXT}} first release doesn't handle zero players tends to destroy civilization

That's it.

Now, when you run dzil release , this happens:

abort if you're not all committed to git

determine all prereqs by finding "use" and similar statements

write out a README, LICENSE, Makefile.PL, MANIFEST, META.yml and META.json

decide on the next release's version number

defined $VERSION in all your packages

add a NAME, VERSION, LICENSE, and AUTHORS section to all the Pod

turn all =method commands into grouped "normal" Pod

update the changelog with the new version and the current date/time

build a tarball and upload it to PAUSE (submit it for CPAN index)

commit the changes to the changelog file

tag the release

push the changes and tags to the remote git repository

I've actually left out a few steps that are very useful but harder to explain. All of those steps happen for each release, so you can reconfigure them all each time without having to update a lot of embedded boilerplate code -- the kind you'd end up with, with other distribution-creation systems.

Finally, all those steps are configurable. I described the configuration that I showed, but you could use fewer plugins, or more plugins, or just different plugins. If you want to maintain your own list of prereqs, you can. If you want to write your own MANIFEST, you can do that -- and there's even a plugin to sanity-check it for you when you try to build a release.

By making it so easy to wrap up and deliver your code, Dist::Zilla lets you just enjoy giving the gift itself -- so you'll be eager to do it all the more often!

See Also