24 days of Perl code from RJBS! Feed

Giving the Gift of Code

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;
# ABSTRACT: our top-secret project for playing bowling against WOPR

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