Last week I wrote about developing a Perl module enabling me to output log entries to macOS’ unified logging system. This weekend’s adventure involved porting that module’s manual processes. These processes included dependency management, documentation syncing, version bumping, and release. The goal was to make everything more automated and repeatable.
And maybe even… monstrous.
I was already familiar with the Dist::Zilla (DZil to its friends) suite of tools and plugins. I also knew that some Perl developers view it as a huge barrier to entry. This perception affects their willingness to contribute to others’ projects.
So even though Log-Any-Adapter-MacOS-OSLog was a small module of interest to a limited coding audience (macOS Perl users), I thought it wise to have both a main branch and separate build/main branch for those programmers who wanted to work as though things had barely changed:
- Entire source code with full POD-formatted documentation;
- A Makefile.PL script to generate a portable building, testing, and installing Makefile;
- Plus, every Perl distribution should supply the typical README, MANIFEST, LICENSE, and CONTRIBUTING documentation. This is essential if it’s meant for public consumption.
A small distribution would also give me a model I can scale up for larger projects. At the very least, it was another learning opportunity for me.
Boy, was it.
Why Dist::Zilla?
Because I know it can automate away the boilerplate code and repetition of information that’s unfortunately necessary in a modern Perl module distribution:
- the version numbers in every module and script
- the README that often includes the same introductory text as the main module’s documentation
- the naming, order, and content of POD sections (via DZil’s sister suite, Pod::Weaver), some of which repeat distribution metadata like author, version, support, copyright, license, and so on
If you need further detail, Dan Book’s Dist::Zilla::Starter is, as its name suggests. an excellent and remarkably thorough guide to the how and why of Dist::Zilla. It even covers the basic structure of CPAN distributions and the history of Perl module authoring tools.
Why not something else?
Because just as in the story of Goldilocks and the three bears, everything else I looked at seemed wanting in some way:

- Module::Build/Module::Build::Tiny: Although minimal, pure-Perl, and easy to install, Module::Build proper still requires shipping extra boilerplate and duplicate metadata. ::Tiny shaves that down further but drops whole swaths of functionality. As an example, you can’t specify at setup time that users need a specific operating system. It’s a deal-breaker for a module that requires macOS 10.12 Sierra or newer.
- Minilla: Opinionated convention over configuration, but I don’t share its opinions. Overriding directory layout, test structure, and README/license handling would mean fighting Minilla’s defaults, DZil-config style, without DZil’s plugin ecosystem.
- ShipIt: Simple, one-file configuration. But too simple: it’s mostly release automation and not authoring automation. Everything I wanted to tool away is still manual.
- No or minimal toolchain: That was how I started this module, with ExtUtils::MakeMaker. Totally manual, with every contributor, including yours truly, needing to remember all the moving parts by hand.
Off to see the lizard!
My DZil dist.ini configuration file started off simply enough:
name = Log-Any-Adapter-MacOS-OSLog
author = Mark Gardner <[email protected]>
license = Perl_5
copyright_holder = Mark Gardner
version = 0.0.5 ; bump as appropriate
[MetaResources]
repository.type = git
bugtracker.web = https://codeberg.org/mjgardner/perl-Log-Any-Adapter-MacOS-OSLog/issues
[Repository]
web = https://codeberg.org/mjgardner/perl-Log-Any-Adapter-MacOS-OSLog
That [MetaResources] is the first evidence of using plugins. It provides a nice tidy way to specify the metadata. Automated tools can use this information to index, examine, package, or install Perl distributions.
So far so good. But then the monster attacked.
[@Filter]
-bundle = @Basic
-remove = GatherDir
-remove = MakeMaker
-remove = Readme
; License plugin still active here
[GatherDir]
include_dotfiles = 1
exclude_filename = .gitignore
exclude_filename = .build
[ReadmeAnyFromPod]
type = markdown
filename = README.md
location = build
[CopyFilesFromBuild]
copy = README.md
; various MakeMaker::*, Git::*, Pod::Weaver, etc. plugins
I was getting errors from the dzil --build command as it attempted to add the same file multiple times. These were generated files committed to the main version control branch and a build/main branch for non-DZil contributors.
This brought me to a dead stop. I went down a rabbit-hole examining built files from rsync(1) with [Run::AfterBuild]., comparing them to the branch I had set up.
Plus my .perlcriticrc file was disappearing from the build artifacts despite that instruction to [GatherDir] to include dotfiles.
LICENSE to kill (files)
Eventually I traced the LICENSE file duplication to dueling DZil plugins. The aforementioned [GatherDir] was dutifully copying the file from my working root even as the [License] plugin was generating it. I didn’t want to lose the latter source of whatever license I happened to be using to distribute this code.
And .perlcriticrc? It turns out the [PruneCruft] plugin doesn’t listen to its friend [GatherDir] and was hoovering it away.
In the end, I had to expand my manually-configured plugin roster a little to keep them from fighting over what files came from where:
[@Filter]
-bundle = @Basic
-remove = GatherDir
-remove = PruneCruft
-remove = MakeMaker
-remove = Readme
[PruneCruft]
except = .perlcriticrc
[GatherDir]
include_dotfiles = 1
exclude_filename = .gitignore
exclude_filename = .build
exclude_filename = LICENSE
[ReadmeAnyFromPod]
type = markdown
filename = README.md
[CopyFilesFromBuild]
copy = README.md
copy = LICENSE
With these tweaks to Log-Any-Adapter-MacOS-OSLog’s dist.ini:
[License]generates its file,[CopyFilesFromBuild]copies it along with the README into the root for my commit,- And
[GatherDir]doesn’t stomp on it like so many Tokyo city blocks.
The build/main branch for non-DZil contributors would contain exactly what they and I expected. It would be a match of the DZil working copy plus artifacts, git cloneable with no surprises.
Lessons learned, and what’s next?
I wanted to serve two styles of development and had signed myself up for a complicated authoring process. I now have more knowledge of which plugin runs in each phase of the build. This allows me to decide between generated and version-controlled files.
CPAN has a rich variety of personal Dist::Zilla::PluginBundle::s factoring individual authors’ preferences into a single place. Those authors don’t have to copy-and-paste DZil configurations around. It’s past time for me to mint one of my own bundles for more consistent and well-understood Perl distributions. Then I can start spinning up new projects without revisiting the same pain.
This weekend has brought on a mental shift. I moved from fighting DZil’s defaults to making it work my way. I also found satisfaction in a predictable, minimal-effort Perl release pipeline.
The monster was just a guy in a rubber suit all along.





