Build, check and ship a WordPress plugin release with wp-release
Releasing a plugin usually means a list of manual steps: install production dependencies, copy the right files, zip them, run Plugin Check, then copy everything into the wordpress.org SVN trunk and tag it. Miss one step and the zip contains a test folder, or the readme's Stable tag no longer matches the plugin header.
wp-release (opens in a new tab) puts those steps behind one PHP command, mmwp-release, which behaves the same on your machine and in CI.
Install
composer require --dev mammothwp/wp-release
vendor/bin/mmwp-release --help
It is a development dependency and requires PHP 8.1 or newer, plus these tools on PATH: composer, rsync, zip and unzip for build; wp (WP-CLI) and curl for check; and svn for deploy-wporg. With the default SQLite database, check also needs the pdo_sqlite PHP extension.
Prepare the plugin
wp-release expects the main file to be <slug>.php in the plugin root. The slug is detected from the single root PHP file with a Plugin Name: header, or set with --slug, MMWP_SLUG or mmwp-release.json.
A .distignore file (rsync exclude syntax) is required in the plugin root:
/.gitignore
/tests
/docs
/node_modules
List every tracked hidden file, such as .gitignore, because the build rejects hidden files in the zip. Anchor entries with a leading /. An unanchored entry such as tests also strips those folders inside vendor/ packages, and build warns about it.
An optional mmwp-release.json sets defaults:
{
"slug": "my-plugin",
"php_min": "7.4",
"wp_version": "6.8",
"strict": false,
"allow": [],
"excludes": ["*.psd", "docs/*"],
"github_repo": "my-plugin"
}
Build
vendor/bin/mmwp-release build
build runs a fresh composer install --no-dev --optimize-autoloader in a temporary copy, so your working vendor/ is never touched. In a git work tree it ships only tracked files, applies .distignore, writes dist/<slug>/ and <slug>.zip, and prints the zip size and file count.
It also validates the result. The plugin header needs Text Domain equal to the slug, plus Requires PHP, Requires at least, a GPL License and Version. readme.txt needs Stable tag and Tested up to, and Version must equal Stable tag. The zip may not contain files such as .git, node_modules, tests, .env* or *.log, any hidden file or *.phar. If your plugin requires runtime packages, the zip must contain their vendor/ directories.
Check
vendor/bin/mmwp-release check
check creates a throwaway WordPress site in a temporary directory, installs Plugin Check, unzips the built zip, activates the plugin and runs wp plugin check. It writes plugin-check.json and plugin-check.txt, then removes the site. It fails on any Plugin Check error, and on warnings too with --strict. Other options include --self-test (also runs wp mmwp self-test <slug>), --keep (keeps the temporary site), --deps=<slugs> for plugins that need other plugins active first, and --db=mysql.
With --strict, you can let specific, known warnings through with --allow=<regex>. Errors can never be allowed.
Status and deploy
vendor/bin/mmwp-release status
vendor/bin/mmwp-release deploy-wporg --dry-run
status prints the slug, the header version, the readme Stable tag, whether that version already exists on wordpress.org and GitHub, and what each deploy command would do.
deploy-wporg needs a passing check. If tags/<version> already exists on plugins.svn.wordpress.org, or the plugin has no SVN repository yet, it skips and exits 0. Otherwise it syncs trunk, copies .wordpress-org/ into assets, runs svn cp trunk tags/<version> and commits. With --dry-run, it prints the svn status diff and stops before the commit. SVN credentials come from WPORG_USERNAME and WPORG_PASSWORD.
release-github creates a GitHub release v<version> with the matching readme.txt changelog section as notes and uploads the zip. The owner defaults to mammothwp, so set MMWP_GITHUB_OWNER (and MMWP_GITHUB_REPO if needed) for your own repositories. It needs GITHUB_TOKEN.
all runs build, check, deploy-wporg and release-github in that order and stops at the first failure.
Release model
A version bump is the release. Every deploy command compares the plugin's Version header with what is already published and does nothing when that version is already out, so the release job can run on every push to your main branch. Exit codes are specific: for example 11 for a validation failure, 20 for Plugin Check errors and 30 when deploy-wporg has no passing check.
When to use it
- You publish a plugin on wordpress.org and want build, Plugin Check and SVN deploy as one repeatable process.
- You want the same commands locally and in CI.
- Your plugin ships Composer packages in
vendor/and you want the zip validated before it goes out.
When not to
- Your plugin is not distributed through wordpress.org or GitHub releases.
- Your release process uses git tags as the trigger. wp-release uses the version header instead.
- Your build environment cannot run PHP 8.1, rsync, WP-CLI or svn.
Links
- GitHub: https://github.com/mammothwp/wp-release (opens in a new tab)
- Packagist: https://packagist.org/packages/mammothwp/wp-release (opens in a new tab)
- License: GPLv2 or later