Confessions of a Dist::Zilla newbie
I've been spending a lot of time writing Perl again, after ten years or so in management, and I'm throughly enjoying myself. I recently wanted a module to interface with a third party service, and was surprised to discover there wasn't already a CPAN module for it. My initial needs were very simple, so I decided this would be a good opportunity to find out the modern way to create a module for release to CPAN. This post is based on notes I took as I worked through this.
So, what's the modern way to do Makefile.PL? Tricky to find out, but I've seen a bunch of stuff about Dist::Zilla, and it seems to have a gazillion plugins. Right, Dist::Zilla (DZ) it is. Start off with the tutorial:
http://dzil.org/tutorial/new-dist.html.
My module is going to be Mail::SendGrid, an interface to the APIs provided by SendGrid.com, a commercial mail sending service. I skimmed the various perl docs recommended for perl module authors as well. Having installed DZ, the first thing I'm told to do is:
% dzil setup
You're prompted for some information; it creates a .dzil directory with profile.ini in it.
The modern way also seems to include github. I already had a github account; once you've got one you need to give git the credentials to access your github account:
git config --global github.user LoginName git config --global github.token GitHubToken
The LoginName is your github username. The GitHubToken isn't your password; to get it:
- go to github, click on account settings in the top right corner.
- click on Account Admin.
- you'll see your API Token. Note this changes when you change your password.
Next, edit ˜/.dzil/profile.ini and add the lines:
[GitHub::Create] public = 1
I think that's so that when you create a new DZ distribution, it will create it on github. But that didn't seem to work for me. What did I miss / get wrong?
Ok, now I'm ready to start my distribution. Just run the following:
% dzil new Mail::Sendgrid
It creates a directory Mail-Sendgrid, which should has two files, including a dist.ini. If your dist.ini was empty, maybe you missed the "dzil setup" step, like I did the first time :-).
Looking at dist.ini:
name = Mail-SendGrid author = Neil Bowerslicense = Perl_5 copyright_holder = Neil Bowers copyright_year = 2011 version = 0.001 [@Basic]
Hmm, perlmodstyle says the most common CPAN version numbering scheme looks like:
1.00, 1.10, 1.11, 1.20, 1.30, 1.31, 1.32
So I changed my dist.ini file to set the version to 0.01. I must remember to submit an issue to the DZ team, or the perlmodstyle team, and say they need to talk!
That [@Basic] pulls a bunch of default stuff in. The tutorial says it's crucial that I understand what's in there. Sure. I just want to release my module; I'll read it sometime later. I could have just copied one of my old school Makefile.PL files by now.
I write a quick first version of the module. Looking at the generated Mail/SendGrid.pm it has:
use strict; use warnings; package Mail::SendGrid;
I've always written:
package Mail::SendGrid; use strict; use warnings;
Is there a difference?
I write some basic tests, and check they work:
% cd Mail-SendGrid % ls -1F dist.int lib/ t/ % prove -lr t ... All tests successful.
So, back to the Dist::Zilla tutorial. Next I need to add pre-reqs:
[Prereqs] Mouse = 0.94 ; version I've tested with HTTP::Tiny = 0.013 ; version I've tested with XML::Simple = 2.18 ; version I've tested with
I've got to give the minimum version of modules required, but I don't really know, I just know that the most recent version works fine. I could read through the changes and guess the oldest version that was probably ok. But "probably" isn't any good. So I'll just put the version number that I have.
I'm using HTTP::Tiny to talk to SendGrid.com, and the underlying URI is https. The doc for HTTP::Tiny says:
Direct https connections are supported only if IO::Socket::SSL is installed.
So, do I need to declare that as a pre-requisite? Let's have a look at the HTTP::Tiny distribution: it doesn't declare IO::Socket::SSL as a pre-req, so I better:
IO::Socket::SSL = 1.44 ; version I've tested with
Ah, I read a bit more of the DZ tutorial, and see that I can use [AutoPrereqs], and it will work out the pre-reqs by looking at my code. But I need to put the required version of modules in my use statements, which is a good thing to do anyway. So now I have:
[AutoPrereqs] [Prereqs] IO::Socket::SSL = 1.44 ; version I've tested with
I still explicitly call out IO::Socket::SSL, because that's an implicit prerequisite.
Next, I write some basic documentation. Seems like DZ can help me here too. I need to add another line to dist.ini:
[PodWeaver]
I also had to install Pod::Weaver and Dist::Zilla::Plugin::PodWeaver.
How do I build a tarball for release?
% dzil build
Ok, that worked. But now I want to put this on github. I go to github.com and create a new repository called Mail-SendGrid. And then I want to tie my local working copy to github:
% cd Mail-SendGrid % git init Initialized empty Git repository in /Users/neilb/src/Mail-SendGrid/.git/ % git add * % git commit -m "first commit" ... a bunch of stuff from git ... % git remote add origin git@github.com:neilbowers/Mail-SendGrid.git % git push -u origin master ... bunch of output ...
But I want DZ to handle this sort of stuff for me. There seem to be (at least) two DZ plugins related to git/github, and after reading their doc, I'm not entirely sure what I need to do. So I add the following to dist.ini, at the end:
[@GitHub] repo = Mail-SendGrid cpan = 1
Install Dist::Zilla::Plugin::PkgVersion and then add the following to dist.ini after the @Basic line:
[PkgVersion]
This inserts a VERSION declaration line in all module and script files in the dist, taking the version number from dist.ini. Ok, seems like some of these DZ plugins are going to make my life a bit easier.
Now I want to be able to release the module, so:
[TestRelease] [ConfirmRelease] [UploadToCPAN]
Time to bite the bullet and release it
% dzil release
Oops, it tried uploading to PAUSE twice. Scanning the copious output, it looks like @Basic already covers the release stuff. Heh, that bit about it being crucial to read, might not have been all bluster. A quick scan of the doc shows that it does indeed cover those three things, so I can delete the three lines from my dist.ini.
Now my dist.ini looks like:
name = Mail-SendGrid author = Neil Bowerslicense = Perl_5 copyright_holder = Neil Bowers copyright_year = 2011 version = 0.01 [@Basic] [PkgVersion] [AutoPrereqs] [Prereqs] IO::Socket::SSL = 1.44 ; version I've tested with [PodWeaver] [@GitHub] repo = Mail-SendGrid cpan = 1
Looking at github, and the output log, nothing got uploaded to github. I subsequently did two more releases to CPAN, but still had to manually update github. What am I missing?
Great post! I've just started using Dist::Zilla myself, and I've successfully pushed a couple releases to CPAN with it. I couldn't get the git stuff working either, but I didn't try very hard. Emacs magit-mode makes it really easy to stage, commit and push changes in just a few keystrokes, so I did not have sufficient motivation to look into it. :)
The @Github plugin is only meant to create the repo on the website when it doesn't exist and for munging the metadata of the dist. For actual releasing you want to use https://metacpan.org/module/Dist::Zilla::Plugin::Git
I am by no means a D::Z expert, but I have used it in several modules. The benefits I take from it are AutoPrereqs (scans and finds prereqs) and PkgVersion (inserts version code into .pm). However these don't really help on a big project or even a small one with "use if available" type dependencies.
I also like ReadmeAnyFromPod which takes my POD and turns it into the README.pod which github uses.
D::Z is nice for the simplicity of copy and pasting your dist.ini and having it be mostly correct (esp. with AutoPrereqs), but in the end I still only use D::Z for smaller projects.
https://github.com/jberger/PDL-Util/blob/master/dist.ini
Welcome back!!
Dzil has a steeper learning curve as I myself learnt a few months ago but it helps you to keep your repository clean if you use the right plugins you don't have to keep makefiles, READMEs etc. in Repos, just the lib files, tests and dist.ini :)
What do you use/do for larger projects Joel, and what's the straw that breaks DZ's back? Is there some problem that you hit, and then you switch away from DZ, or do you know from the start that a project's not appropriate for DZ?