Certification checks

Every submitted release runs through 13 automated checks before it can publish. This page explains what each one looks for and how to write your plugin so it passes.

How the tiers work

You can see the result of every check for your own release on its version page, under "Technical report".

Required checks

Fail one of these and your release never publishes.

perl_syntax

Every .pm file in your release must compile (perl -cw) against the Koha version you declared as minimum_version. Run perl -cw YourPlugin.pm yourself against that Koha version before tagging a release.

manifest_completeness

Your plugin's $metadata hash must declare both version and license:

our $metadata = {
    name            => 'My Plugin',
    description     => 'What it does',
    author          => 'Your Name',
    minimum_version => '23.05',
    version         => '1.0.0',
    license         => 'GPL-3.0',
};

dependency_allowlist

Your code is scanned for patterns the store won't allow in a submitted plugin, since these can't be reviewed automatically and are common vectors for something going badly wrong on a library's live system:

This is a static analysis of real parsed Perl syntax, not a text search -- mentioning one of these in a comment or string won't false-positive.

Certification-gating checks

These don't block publishing, but all of them need to pass for your release to reach the CERTIFIED tier.

perl_critic

Every .pm file is run through Koha's own Perl::Critic policy set. Run it yourself locally against your plugin's files before submitting.

docs_presence

Include a README, README.md, Development.md, CONTRIBUTING.md, or a docs/ directory somewhere in your repository.

readme_presence

Specifically: GitHub needs to recognize a README for your repository (the same one it'd show on your repo's own front page -- README.md, README.rst, or a bare README all count). It's shown in full on your plugin's page here, so it's worth keeping it current.

changelog_format

Include a CHANGELOG.md (or CHANGES.md) with at least one heading shaped like a Keep a Changelog or Common Changelog version entry:

## [1.2.0] - 2026-01-01

### Added
- A new widget for doing the thing.

### Fixed
- The thing that was broken.

A bare ## v1.2.0 heading passes too -- this check isn't full spec validation (it doesn't check section names, dates, or an "Unreleased" section), just "is there a real, dated version entry here." That same heading is what powers the "What's new in this version" summary shown on your plugin's page, so passing this check also means that summary will actually show up.

tests_presence

Include at least one t/*.t file in your tagged source repository. (Your .kpz release archive itself doesn't need to contain tests -- this check looks at the tagged commit on GitHub, not the archive.)

translatable_templates

Any .tt template rendering visible text (<h1>, <p>, <button>, etc.) should mark that text for translation:

<h1>[% t('Hello, world') %]</h1>

plugin_template_wrapper

Every .tt template should include Koha's plugin template wrapper, currently doc-head-close.inc:

[% INCLUDE 'doc-head-close.inc' %]

hardcoded_credentials

Don't assign a literal secret to a credential-shaped variable name (e.g. api_key, password) anywhere in your code, and don't commit a PEM private key or an AWS access key. Use Koha's own configuration mechanisms for anything a library needs to supply themselves.

Informational checks

Recorded on your release's technical report, but don't block publishing or affect your certification tier.

koha_max_version

Whether your $metadata declares a maximum_version in addition to minimum_version. Worth setting if you know your plugin doesn't work against newer Koha releases yet.

gpg_signed_tag

Whether the release tag itself was GPG-signed on GitHub (git tag -s v1.2.0). This is about your own tag's authenticity, separate from the store's own signing of the published .kpz -- see the verification key page for that.