Using Dist::Zilla

How I use Dist::Zilla to package and release my Perl modules

I don’t often publish new Perl modules, but when I do, it’s typically with Dist::Zilla. Once configured, dzil allows me to focus on the development of my module, and not on how to release it.

Like most, in the early days of using dzil, I cargo culted someone else’s working config. Nowadays, I still cargo cult working configs, but I understand better how and why they work. In this post, I’ll walk through a working configuration for one of my modules and explain how it allows me to make a release happen.

I gloss over some bits, and am a little hand-wavey in spots. If ever in doubt, please read the relevant perldoc for the given module or function. I have tried my best to include all relevant links.

Getting Started

One of the best things about Dist::Zilla is the power and control you have over the packaging and release process.

UNLIMITED POWER!!!

Unlike Palpatine, this power does not come from a dark place (unless you are Yanick).

I recently published an update to one of my modules, Dancer2::Plugin::Minion. As part of this release, I had to fix some earlier build problems. This gave me a chance to start with a fairly basic dist.ini:

name    = Dancer2-Plugin-Minion
version = 1.0.0
author  = Jason A. Crome <cpan@jason.cromedome.dev>
license = Perl_5
copyright_holder = Jason A. Crome
copyright_year = 2024
main_module = lib/Dancer2/Plugin/Minion.pm

[MetaResources]
bugtracker.web  = https://github.com/cromedome/Dancer2-Plugin-Minion/issues
repository.url  = git@github.com:cromedome/Dancer2-Plugin-Minion.git
repository.web  = https://github.com/cromedome/Dancer2-Plugin-Minion
repository.type = git
x_IRC           = irc://irc.perl.org/#dancer

[@Filter]
-bundle = @Basic
-remove = License

[Prereqs::FromCPANfile]

[PkgVersion]

[NextRelease]
filename = Changes

[@Git]
allow_dirty = Changes
allow_dirty = dist.ini
allow_dirty = README.md
add_files_in = Changes
add_files_in = dist.ini
add_files_in = README.md

[PruneFiles]
match = dist.ini

Your dist.ini is the core configuration for how Dist::Zilla should build and package your module’s distribution. You can create one via a minting profile (more on these later), by copying one that works from another distribution, or start from scratch. For purposes of this blog post, let’s start with my configuration above.

Understanding dist.ini

dist.ini is intended to be easy to understand. There’s just a few basic rules:

  • Sections are denoted with []
  • Section names starting with @ are plugin bundles (search Dist::Zilla::PluginBundle on MetaCPAN)
  • Section names starting with = does not do name expansion like @ does (I have never needed this)
  • Other section names enable plugins with the provided name (for example, PkgVersion enables Dist::Zilla::Plugin::PkgVersion)
  • Values in each section are configuration to the plugin or bundle named by the section title (generally, key/value format)

The topmost unnamed section contains configuration required by Dist::Zilla. You must provide name, version, abstract, and license (author is optional). Alternatively, at the top of the primary module in your distribution, you can specify:

# ABSTRACT: Do awesome things

The MetaResources section is used when your module is indexed by CPAN.

The plugin bundle [@Filter] deserves a special mention. It loads other plugin bundles, but with specified plugins removed. Since the Dancer2::Plugin::Minion repo provides its own license file, I don’t need Dist::Zilla::Plugin::License to add one for me. The Filter bundle allows for this; -bundle = @Basic loads the Basic bundle, except for the license plugin, because we remove (“filter”) it with -remove = License (you can do this for multiple plugins). Easy and powerful!

[PrereqsFromCPANFile] tells Dist::Zilla to look in your distribution’s cpanfile for a list of dependencies (these, in turn, get added to META.json when your distribution is built). You can also specify them in the [Prereqs] section of your dist.ini (not shown).

[PkgVersion] automatically takes the version specified in your dist.ini and adds a line to each module to set it’s $VERSION. Complementing this is the [NextRelease] plugin, which automatically fills in version information in your distributions Changes file.

Finally, [PruneFiles] ensures that listed files are not included in your distribution.

Now that we have our config set up, let’s build our distribution!

Building Your Distribution

Running dzil build from the command line will perform any build steps needed by your distribution. This can include building any XS code in a module, generating module Pod with Pod::Weaver, builds a Makefile.PL, etc.

You spent some bucks on all those processor cores, and you want to use them, right? Dist::Zilla makes this easy! Just specify -jX on the command line, where X is the number of cores you’d like to devote to the build process. Dist::Zilla will do its best to use all of the cores you allow it to use when running your distribution’s build step.

The machine I am writing this on has 16 cores, so my typical build step is dzil build -j16.

Testing

Running your test suite is as easy as building: dzil test (what, you expected something fancier here?).

Like dzil build, you can use multiple cores to run your test suite: dzil test -j8. Keep in mind that some test suites do not run well in parallel mode. In my own experience, this has rarely been a problem.

Finally, Release Time!

Did you leave UploadToCPAN enabled in your dist.ini? You’re almost there! Just type dzil release and your release tarball will be uploaded to PAUSE. Boom!

The UploadToCPAN plugin expects you will have credentials stored in either your Dist::Zilla config (~/.dzil/config.ini) or ~/.pause (personally I use the latter). If you are storing user and password information in your home directory, make sure to set permissions on this file to 0600.

You don’t want to use dzil to upload to CPAN? That’s ok - dzil build gave you a tarball that you can upload via the PAUSE web interface or whatever other tool you’d typically use.

We also added the Git plugin bundle to our dist.ini above (with [@Git]). Default behavior includes:

  • Making sure your repo is in a clean state
  • Commits any files updated at build time
  • Tags your released version
  • Push the above to your project’s repo

The options specified above allow the Changes, dist.ini, and README.md to be dirty when dzil release is run. Why dist.ini? If you are using the PkgVersion plugin, you’ll specify the version number being released in your config, and that will be propagated to any modules in your distribution, as well as your Changes file.

The add_files_in option will ensure that any specified files will be included in the release commit. See the docs for the Git commit plugin for more information.

Other Topics

dzil will run any steps that have yet to be performed prior to running the step you asked for. As an example, if you run dzil test, but you haven’t run dzil build, Dist::Zilla will build your distribution before trying to run your tests. Want to run before you walk? Type dzil release and watch it build, test, and upload your dist to PAUSE, commit dirty files to git, and add a release tag to your repo. If any step fails, the process will halt, allowing you to resolve whatever issue cropped up.

Dist::Zilla also has a concept known as minting profiles. Minting profiles allow you customize the way a new module/distribution is created. At Best Practical, we have a minting profile for creating new RT extensions, with configuration options for extensions that run database migrations.

For more information on minting profiles, check out the Dist::Zilla documentation.

Recap

The high-level steps for using Dist::Zilla to build and release a distribution are:

  • Set up your dist.ini
  • dzil build -jX
  • dzil test -jX
  • dzil release

Final Thoughts

This post has only scratched the surface of what you can do with Dist::Zilla. For more information, check out the following resources:

Dist::Zilla provides you nearly unlimited power for building and releasing a Perl module. This power can add complexity; make sure to find the right balance of these ingredients for your own projects.

Happy minting!

comments powered by Disqus