Imported from Getty/langertha (
.claude/skills/getty-perl-release-author-getty/SKILL.md). Install upstream withnpx skills add Getty/langertha --skill getty-perl-release-author-getty. Copyright stays with the author.
When working with [@Author::GETTY] plugin bundle:
Required Metadata
name = Distribution-Name
author = Name <email>
license = Perl_5
copyright_holder = Copyright Owner
The LICENSE file
The bundle expects LICENSE to be a committed file in the repository. It
removes [License] from @Basic and adds [LicenseFile], which aborts the
build when that file is missing or no longer matches the distribution's
license, copyright_holder and copyright_year:
[@Author::GETTY/LicenseFile] no LICENSE in the distribution:
run 'dzil genlicense' and commit the file
Write it once, then track it:
dzil genlicense
git add LICENSE
The git add is not optional. The bundle gathers through Git::GatherDir,
which sees only tracked files, so a LICENSE left untracked in the working
directory fails with the identical message — which reads as though genlicense
did nothing.
Re-run genlicense after changing license, copyright_holder or
copyright_year. Checking the file against them is the whole point of the
plugin: a committed LICENSE otherwise keeps serving the old licence silently,
with no warning and no build failure.
The target is the repository, not the tarball. GitHub, Gitea and Forgejo detect
a licence only from a committed LICENSE, and a generated one never exists
outside the build — which is why a distribution built the default way shows up
as unlicensed on its own project page. genlicense writes the bare licence
text, not the fulltext that [License] generates: the copyright notice
fulltext prefixes is enough to make GitHub report NOASSERTION instead of
naming the licence.
[LicenseFile] is a recent addition to the bundle, so distributions that built
fine before it landed fail on their next build. When picking up an older dist,
ls LICENSE is cheaper than diagnosing it mid-release. For a dist that
deliberately ships no committed LICENSE, generate_license = 1 restores the
generated file and adds no check.
When the bundle applies
[@Author::GETTY] is for Getty's own CPAN work. A distribution that is not
released to CPAN — a proprietary application, a deploy artefact — lists its
Dist::Zilla plugins explicitly instead, because the bundle assumes a CPAN
release. Non-CPAN dists that still use the bundle set no_cpan = 1.
# ABSTRACT lines
Every file the bundle processes carries # ABSTRACT: <one line> directly under
package, before any use; executables in bin/ carry it under the shebang.
PodWeaver turns it into the NAME section. Outside a bundle-managed distribution
the line does nothing — do not scatter it into files Dist::Zilla never sees.
@Author::GETTY Options
Feature Toggles (Boolean)
no_cpan- Skip UploadToCPAN; also defaultsversion_finderto:MainModuleno_podweaver- Skip PodWeaverno_changes- Skip NextReleaseno_installrelease- Skip InstallReleaseno_makemaker- Skip MakeMakerxs- Use ModuleBuildTiny (for pure-Perl XS without Alien deps)deprecated- Add Deprecated pluginadoptme- Add x_adoptme metadatano_github- Skip GithubMeta and GitHub::CreateRelease, use Repository instead. Auto-set to 1 when.git/confighas no github.com remote; setno_github = 0to force GitHub plugins on anywayno_github_release- Skip only GitHub::CreateRelease. Same auto-detection; when active,dzil releasecreates a GitHub Release and attaches the tarball, which needs~/.github-identity(login + token)gitea- Treat the remote host as Gitea/Forgejo (repository/bugtracker/homepage via GiteaMeta). Only needed for self-hosted instances — codeberg.org and the author's own are auto-detected. No effect when a GitHub remote existsinclude_readme- Ship README.md (excluded from the tarball by default)no_install- Resulting distribution can't be installedgenerate_license- Go back to a generated LICENSE:@Basickeeps its License plugin, no LicenseFile check is added. Default 0 — the bundle expects a committed LICENSE (see above)
Identity & Metadata
author- CPAN author name used for the authorityauthority- Override the authority, e.g.authority = ETHERwhen uploading modules owned by another author (default: theauthorvalue)
XS with Alien
xs_alien = Alien::Foo- Auto-configures MakeMaker::Awesome for XS+Alienxs_object = Name- Override XS object name (default: derived from Alien name)
Versioning
task = 1- TaskWeaver + AutoVersionmanual_version = x.x- Manual versionmajor_version = 2- Major version for AutoVersionversion_finder- multi-value; forwarded as thefinderoption of RewriteVersion::Transitional + BumpVersionAfterRelease (default path) and PkgVersion (task/manual_version path). Defaults to:MainModulewhenno_cpanis set, otherwise unset.
Build & Release
weaver_config- PodWeaverconfig_pluginto use (default: the bundle's own)installrelease_command- Command used to install after release, instead of cpanm
Docker
docker_image- Image repository. Auto-adds one Docker::API plugin, which is a working Releaser on its own (no UploadToCPAN needed for non-CPAN dists)docker_tags- Whitespace-separated tag list (default:latest %V %v)docker_local- Build and tag the image, but don't pushdocker_default- Set to 0 to suppress the auto-added plugin when you configure builds exclusively through[@Author::GETTY::Docker / name]subsections
Support
irc = #channel- IRC channelirc_server- Server (default: irc.perl.org)irc_user- Username for SUPPORT section
Git
release_branch- Branch for releases (default: main)tag_format- Release tag format. Default%v, the bare$VERSION(0.317) — not a v-prefixed SemVer tag. Usev%v.0when the tag must satisfy strict vMAJOR.MINOR.PATCH (Perl's decimal$VERSIONhas only two parts, the.0supplies the patch part)commit_files_after_release- Multi-value; extra files folded into the release commit (via Git::Commit'sallow_dirty). For artefacts arun_before_releasehook rewrites, e.g. a sibling Python/JS version file
Alien (prefix alien_)
alien_build = 1- Alien::Build-based dist: adds AlienBuild (Makefile.PL driven by Alien::Build::MM), impliesno_makemaker, expects analienfilein the dist root
For wrapping C libraries with Alien::Base:
Required:
alien_repo- URL to download releases from
Library identification:
alien_name- Name of the alien packagealien_bins- Executables to install (multi-value)
Archive pattern matching:
alien_pattern- Full regex pattern for archive matchingalien_pattern_prefix- Prefix (e.g.,mylib-)alien_pattern_version- Version regex (default:([\d\.]+))alien_pattern_suffix- Suffix (e.g.,\.tar\.gz)
Build configuration:
alien_msys- Use MSYS on Windowsalien_autoconf_with_pic- Pass --with-pic to autoconfalien_isolate_dynamic- Isolate dynamic librariesalien_version_check- Command to check installed version
Custom build commands (for non-autoconf projects):
alien_build_command- Custom build commands (multi-value, use%sfor prefix)alien_install_command- Custom install commands (multi-value)alien_test_command- Custom test commands (multi-value)
Dependencies:
alien_bin_requires- Build dependencies (multi-value)
Run Hooks (prefix run_)
run_before_build,run_after_buildrun_before_release,run_after_releaserun_release,run_test
Use a run hook for the project-specific step that follows a release — a Docker
build and push, a deploy script — so it travels with dzil release instead of
living in someone's shell history. Never wire up a step the bundle already
performs; the hook is for what it does not know about.
[Run::Release]
run = docker build -t registry/app:%v %d && docker push registry/app:%v
POD Commands (Pod::Elemental::Transformer::Author::GETTY)
Section Commands (→ =head1)
=synopsis→=head1 SYNOPSIS=description→=head1 DESCRIPTION=seealso→=head1 SEE ALSO
Inline Commands (→ =head2)
=attr name→=head2 name=method method_name→=head2 method_name=func func_name→=head2 func_name=opt- CLI options=env- Environment variables=hook- Hooks=example- Examples
Auto-generated sections (do NOT write manually): NAME, VERSION, AUTHOR, SUPPORT, CONTRIBUTING, COPYRIGHT
Versioning Convention — CRITICAL
The version in the repository is always the NEXT release version, not the current one.
Before a release, the files already contain the upcoming version:
dist.inior module$VERSION= e.g.1.005Changeshas{{$NEXT}}as the placeholder for unreleased changes- The currently released version on CPAN is
1.004
After dzil release runs:
{{$NEXT}}in Changes is replaced with1.005+ release date- The version is bumped to
1.006(or next AutoVersion value) - A Git tag
v1.005is created
Do NOT treat the version in dist.ini as the released version. If the user asks "what version is released?", check CPAN or git tags — not the current $VERSION in the files.
Do NOT bump the version manually before a release — dzil release handles this automatically.
Every file carries its own $VERSION
Each file under lib/ and bin/ needs its own our $VERSION = '...';, set to
the version that will be released NEXT — one higher than what is on CPAN (or
higher). A file without a $VERSION ships versionless and breaks consumers that
pin against it.
Only the FIRST our $VERSION in a file gets rewritten. RewriteVersion::Transitional
and BumpVersionAfterRelease both stop after the first match, so a file holding two
packages leaves the second one frozen at whatever version it was written with —
while MetaProvides::Update happily reports the real release version. The result is
a distribution whose META and whose code disagree, silently, for as many releases
as it takes someone to notice.
So: one package per file. If you find several package statements in one file,
split them out before releasing.
Executables belong in bin/, never script/. The bundle sets no ExecDir, so
Dist::Zilla's default of bin applies: files under script/ are not installed as
executables and their $VERSION is never rewritten. A distribution with a script/
directory should have it renamed to bin/ — otherwise none of the above takes
effect.
Release Workflow
# Before release: check Changes, ensure {{$NEXT}} section has entries
# Then:
dzil release # Builds, tests, uploads to CPAN, bumps version, commits, tags
Conventions
copyright_yearIS used in dist.ini — GETTY has it in ALL distributions, do NOT remove it- No
=head1 SUPPORT/AUTHOR/COPYRIGHTin POD - Use inline
=attr/=methoddirectly after code - Dependencies in
cpanfile, not dist.ini - Changes file with
{{$NEXT}}for unreleased - For XS+Alien modules: use
xs_alien = Alien::Foo(auto-configures MakeMaker::Awesome) LICENSEis generated once withdzil genlicenseand committed — the build aborts without it