Publishing to the Plugin Directory

This content was generated with the assistance of AI and should be verified against the official documentation before being relied on in production.

This sub-section’s WordPress bibliography lists the reference material consulted while preparing these pages.

The WordPress.org Plugin Directory is where WordPress sites find, install and update free plugins: Plugins > Add New Plugin searches it, and the update checks that run on every site ask it for new versions. Publishing there gives a plugin one-click installs, automatic updates, a support forum and community translations, in exchange for a review, a set of guidelines and a Subversion repository that becomes the only distribution channel. This page is for plugin developers preparing a first submission or maintaining a published plugin. It follows lumen-catalog, the catalogue plugin of the fictitious Lumen Press site, from a private plugin to a directory-ready release: guidelines, readme.txt, assets, Plugin Check findings and fixes, the release zip, the review, Subversion and an automated deploy. The plugin’s code is built in Plugin Fundamentals.

This page was written against WordPress 7.1.3 (released 2026-10-06; 7.1.4, a security release of 2026-10-28, is the version to run in production), PHP 7.4 minimum and 8.3+ recommended, WP-CLI 2.12.0, Plugin Check 2.1.0, @wordpress/scripts 37.0.0 and the 10up deploy action 2.3.0, and checked on 2026-10-28. WordPress 7.2 is in beta and due in December 2026; Tested up to may name it once its release candidate is out.

What the directory is

A directory plugin has a public page at https://wordpress.org/plugins/<slug>/ built from its readme.txt, its main file header and the images in its assets folder. Its code lives in a Subversion (SVN) repository at https://plugins.svn.wordpress.org/<slug>/, which the Plugin Handbook calls a release repository: every commit regenerates the downloadable zips, so development stays in Git and only finished releases reach SVN. Approved plugins also get a support forum and a project on translate.wordpress.org, from which sites receive language packs (see Internationalization).

The directory is optional: in-house plugins such as Lumen Press’s own lumen-catalog install from a zip, Composer or Git and update from a private server (see Updates outside the directory).

The detailed guidelines, summarised

Every plugin, every committer and everyone who officially supports a plugin is bound by the eighteen Detailed Plugin Guidelines, plus the community and forum guidelines. Violations can close a plugin; repeated ones can ban the developer. The guidelines in short, in the order the handbook numbers them:

# Rule What it means in practice

1

GPL-compatible licensing

Code, data and images all under GPL or a GPL-compatible licence; "GPLv2 or later", the licence of WordPress, is recommended. Bundled libraries count.

2

Developers are responsible for their plugin

Every file, including third-party ones, and compliance with the terms of every API used. Restoring removed code or working around a rule is itself a violation.

3

A stable version is available from the directory

Keeping the directory copy stale while distributing newer code elsewhere can get the plugin removed.

4

Code is (mostly) human readable

No obfuscation, packed code or mangled names. Minified files are fine if the source and build tools ship with the plugin or the readme links to them.

5

No trialware

No features locked behind payment, disabled after a trial or quota, or limited to a sandbox API. Premium code belongs in a separate add-on sold elsewhere.

6

Software as a service is allowed

The service must do something substantial and be documented in the readme with its terms. A service that only validates licence keys, or that hosts code moved out of the plugin, is not allowed.

7

No tracking without consent

Contacting external servers needs explicit opt-in. Undocumented calls, analytics, offloaded assets and ad trackers are all tracking.

8

No executable code from third parties

No updates or add-ons from other servers, no premium upgrades installed by the free plugin, no JavaScript or CSS from CDNs (fonts excepted), no iframed admin pages.

9

Nothing illegal, dishonest or offensive

Fake reviews, keyword stuffing, crypto-mining, claims of guaranteed legal compliance, sockpuppets, harassment.

10

No public credits without permission

"Powered by" links are off by default and opt-in.

11

Do not hijack the dashboard

Notices limited in scope, dismissible, with a way to resolve them; advertising in the admin is discouraged.

12

Readmes are not spam

At most five tags, no competitor names as tags, affiliate links disclosed and direct.

13

Use the libraries bundled with WordPress

jQuery, PHPMailer, SimplePie, PHPass and the rest are loaded from core, never shipped again.

14

Avoid frequent commits

SVN is for releases; a readme-only commit to raise Tested up to is the accepted exception.

15

Increment the version for each release

Sites see updates only when Version increases, and trunk/readme.txt always describes the current release.

16

The plugin is complete at submission

Names cannot be reserved; unused approved slugs can be given to others.

17

Respect trademarks and project names

A slug may not start with someone else’s brand: "Book Covers for Acme", not "Acme Book Covers".

18

The directory may be maintained as needed

The Plugins Team can change the guidelines, close plugins, grant exceptions, change committers, and patch a plugin for public safety.

The "Common issues" page lists what reviews flag most: unescaped output, unsanitised input and missing nonces (see Security for Developers), direct file access, missing prefixes, curl instead of the HTTP API, inline scripts instead of wp_enqueue_script(), variables as text domains, plugins that (de)activate other plugins, update checkers, undocumented services and development folders in the zip. Block Directory plugins also follow the Block Specific Plugin Guidelines (one top-level block, minimal PHP, no admin screens); blocks are covered from Block Development Environment onwards.

For lumen-catalog the guidelines change three things: the Update URI header pointing at Lumen Press’s server goes (guideline 8); the optional stock synchronisation stays off until enabled and the readme documents what it sends and the service’s terms (6 and 7; see also Privacy and Personal Data); and the release keeps composer.json beside vendor/ (4).

The slug and the name

The slug is derived from Plugin Name in the main file when the zip is uploaded (Lumen Catalog becomes lumen-catalog); an existing slug gets a -2 suffix. It becomes the page URL, the SVN path, the folder name on every site and the text domain. It can be changed once from the submission page before the review starts, by the Plugins Team until approval, and never afterwards; the display name can change at any time. Slugs may not contain "wordpress" or "plugin", version numbers or vulgarities, and use only English letters and digits.

Because updates match plugins by folder name, a slug already used outside WordPress.org by many sites is refused; for a plugin first released privately, as lumen-catalog was, the Plugins Team asks to be emailed.

readme.txt

The directory page comes from readme.txt, a text file in a dialect of Markdown with === Plugin name ===, == Section == and = Subsection = headings. lumen-catalog ships this one:

=== Lumen Catalog ===
Contributors: lumenpress
Tags: books, catalogue, publishers, isbn, rest-api
Requires at least: 6.9
Tested up to: 7.1
Stable tag: 0.1.0
Requires PHP: 7.4
License: GPL-2.0-or-later
License URI: https://www.gnu.org/licenses/gpl-2.0.html

A book catalogue for publishers: a Books post type with genres, series, ISBN, page count and publication date, and a REST API.

== Description ==

Lumen Catalog turns a WordPress site into a publisher's catalogue.

* A **Books** post type at `/books/`, edited in the block editor.
* **Genres** and **Series** taxonomies.
* ISBN, page count and publication date fields, available in the REST API.
* A read-only REST namespace, `lumen/v1`, for apps and headless front ends.

= External services =

Stock synchronisation is optional and off by default. When an administrator enables it under
Settings > Lumen Catalog, the plugin sends the ISBNs of published books, once an hour, to the distributor API URL
entered on that screen, and stores the stock quantities it returns. No other data is sent. The distributor's terms of
use and privacy policy apply to that exchange; for the Lumen Press distribution service they are at
https://lumenpress.example/distribution/terms/ and https://lumenpress.example/distribution/privacy/.

= Development =

The source code, including the Composer configuration used to build `vendor/`, is at
https://git.lumenpress.example/lumen-catalog.

== Installation ==

1. Install the plugin from Plugins > Add New Plugin, or upload the `lumen-catalog` folder to `/wp-content/plugins/`.
2. Activate it on the Plugins screen.
3. Add books under Books > Add New Book.

== Frequently Asked Questions ==

= Does deleting the plugin delete my books? =

No. Uninstalling removes the plugin's settings and stock table. The books stay in the database as `lp_book` posts.

== Screenshots ==

1. The book list in the block editor, with genre and series columns.
2. A book's ISBN, page count and publication date fields.

== Changelog ==

= 0.1.0 =
* First public release: Books post type, Genres and Series, book fields, `lumen/v1` REST routes.

== Upgrade Notice ==

= 0.1.0 =
First public release.

Header fields

Field Meaning and rules

Contributors

Case-sensitive, comma-separated WordPress.org usernames; each gets a profile link. Other names show without one.

Tags

One to five terms. Extra tags are ignored, and Plugin Check rejects some outright ("plugin", "wordpress").

Requires at least

Minimum WordPress version, shown on the page. Since 5.8 core enforces only the value in the main file header, so both must agree.

Tested up to

Highest WordPress major version tested, numbers only (7.1, not WP 7.1). Minor releases are implied. Never higher than the current release candidate.

Stable tag

The plugin version users get, which must be a folder under tags/. trunk is still parsed but refused for new plugins.

Requires PHP

Minimum PHP version; again enforced from the main file.

License, License URI

The licence, which must match the main file header.

Short description

The line after the header: plain text, at most 150 characters, truncated beyond.

Sections

Description is free text (a video URL on its own line is embedded); Installation may be omitted; FAQ entries are = Question = subheadings; each numbered Screenshots line captions screenshot-N; Changelog lists versions newest first; Upgrade Notice gives each version a reason to update (at most 300 characters), shown on Dashboard > Updates with HTML removed. Custom sections are allowed in moderation. Keep the file under 10 KB, moving old changelog entries to changelog.txt.

How the directory reads it

The directory reads Stable tag from trunk/readme.txt and everything else from tags/<stable tag>/readme.txt, so description edits in trunk wait for the next tag. The download button’s version is the Version header of the tagged main file. A Tested up to three majors behind shows an "out of date" notice, removed by a readme-only commit.

Banners, icons and screenshots

Page images go in the top-level assets folder of the SVN repository, beside trunk (never inside it, so they are not shipped to sites). They are served through a cached CDN; changes can take up to six hours to appear.

File name Size (px) Max file size Notes

banner-772x250.png or .jpg

772 × 250

4 MB

The page header.

banner-1544x500.png or .jpg

1544 × 500

4 MB

High-density version, used only together with the 772 × 250 banner.

banner-772x250-rtl.png, banner-772x250-es_ES.png

as above

4 MB

Right-to-left or per-locale variants, only if the image differs.

icon-128x128.png, .jpg or .gif

128 × 128

1 MB

Search results and the dashboard installer.

icon-256x256.png, .jpg or .gif

256 × 256

1 MB

High-density icon.

icon.svg

vector

1 MB

Needs a PNG icon as fallback.

screenshot-1.png, screenshot-2.jpg, …​

any

10 MB

Lowercase names; captions come from the readme. screenshot-1-de.png replaces screenshot-1 for German.

blueprints/blueprint.json

n/a

n/a

WordPress Playground set-up for the Preview button, enabled from the plugin’s Advanced tab.

The size in a file name is the size the image must have; other names and sizes are ignored. Images must not use someone else’s logo. In Git, lumen-catalog keeps them in .wordpress-org/, the folder the deploy action below copies to assets.

Images committed with the wrong MIME type download instead of displaying; svn propset svn:mime-type image/png fixes them (see the SVN example below). Setting image/svg+xml on icon.svg makes Subversion 1.14 warn that the file looks like text, so the handbook only sets PNG and JPEG types.

Plugin Check

Plugin Check (PCP) runs the automated part of the review: a submission must pass its Plugin repo checks, and other categories cover security, performance, accessibility and best practice. It adds Tools > Plugin Check and wp plugin check; 2.1.0 (August 2026) adds a Tools > Plugin Check SVN screen for stray files in the SVN repository and optional AI features on WordPress 7.0+. Passing it does not guarantee approval.

Running it

The test site is the Docker stack of Local Development Environments (WordPress 7.1.3, MariaDB 11.8, wordpress:cli-php8.4), with the lumen-catalog working copy mounted at wp-content/plugins/lumen-catalog:

wp plugin install plugin-check --activate
wp plugin list-checks --fields=slug,category
wp plugin check lumen-catalog

wp plugin list-checks listed 34 stable checks, from plugin_readme, plugin_updater, late_escaping and direct_db_queries to trademarks and offloading_files. The first run on the working copy (with vendor/ built, no readme.txt yet and the canonical header with Update URI) printed:

FILE: .phpcs.xml.dist
line	column	type	code	message	docs
0	0	ERROR	hidden_files	Hidden files are not permitted.
0	0	ERROR	application_detected	Application files are not permitted.

FILE: lumen-catalog.php
line	column	type	code	message	docs
0	0	ERROR	plugin_updater_detected	Including An Update Checker / Changing Updates functionality. Plugin Updater detected. Use of the Update URI header is not allowed in plugins hosted on WordPress.org.	https://developer.wordpress.org/plugins/wordpress-org/common-issues/#update-checker
0	0	WARNING	plugin_header_nonexistent_domain_path	The "Domain Path" header in the plugin file must point to an existing folder. Found: "languages"	https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/#domain-path

FILE: readme.txt
line	column	type	code	message	docs
0	0	ERROR	no_plugin_readme	The plugin readme.txt does not exist.

That working copy still named its ruleset .phpcs.xml.dist, hence hidden_files. The project now uses phpcs.xml.dist, the name in Coding Standards and Static Analysis; without the leading dot only application_detected applies to it.

A first draft of the readme (Stable tag: trunk, Tested up to: 6.9, License: GPL, a 160-character short description and the tags plugin and wordpress) then produced, with wp plugin check lumen-catalog --checks=plugin_readme --format=csv --fields=type,code:

FILE: readme.txt
type,code
ERROR,outdated_tested_upto_header
ERROR,license_mismatch
ERROR,trunk_stable_tag
WARNING,readme_parser_warnings_ignored_tags
WARNING,readme_parser_warnings_trimmed_short_description

The fixes:

Finding Fix

no_plugin_readme, then the five readme findings

The readme above: Stable tag equal to Version (0.1.0), Tested up to: 7.1 (the message was "Tested up to: 6.9 < 7.1"), the same GPL-2.0-or-later identifier as the header, a shorter description, five relevant tags.

plugin_updater_detected

The directory edition drops the Update URI line. A test copy with Update URI: https://wordpress.org/plugins/lumen-catalog/ passed, since that value keeps WordPress.org updates. A test file adding an update_plugins_{$hostname} filter was not reported, so the check is no substitute for removing private updaters by hand.

plugin_header_nonexistent_domain_path

languages/ now holds lumen-catalog.pot from wp i18n make-pot. Language packs from translate.wordpress.org do not need the folder; removing Domain Path is the other fix.

hidden_files, application_detected

Development files (phpcs.xml.dist, .distignore, .github/) stay in Git but out of the release, through .distignore (next section).

Run on the source folder, Plugin Check keeps reporting development files; run on the unpacked release zip it reported "Success: Checks complete. No errors found."

Things to know about the command

  • wp plugin check exited with status 0 in every run, errors included, also with --format=strict-csv. A CI job therefore parses --format=json output, or uses wordpress/plugin-check-action.

  • The argument is a plugin slug or a directory path. Despite the readme mentioning zip paths and URLs, a zip path failed in 2.1.0 with "Invalid plugin slug" and a fatal error; unzip it and pass the folder.

  • From WP-CLI only static checks run by default. Runtime checks (enqueued assets, for example) need --require=./wp-content/plugins/plugin-check/cli.php, which loads the checker before WordPress.

  • --mode=update applies the rules for updates instead of new submissions; --checks, --ignore-codes and --exclude-files narrow a run, and vendor/ and node_modules/ are skipped by default.

Building the release zip

The release is one folder named after the slug with runtime files only: PHP, built assets, vendor/ from composer install --no-dev --optimize-autoloader, languages/, readme.txt and the build configuration guideline 4 asks for; no tests, CI files, node_modules/ or hidden files. A submission zip may be up to 10 MB.

wp dist-archive and .distignore

wp dist-archive zips a folder minus the paths in its .distignore. It is not part of the WP-CLI 2.12.0 bundle and is installed as a package. The newest releases (3.2.x) require WP-CLI 3, which is not released, so plain wp package install wp-cli/dist-archive-command failed on dependency resolution; pinning 3.1.0 works (below). The .distignore of lumen-catalog, at the plugin root (one pattern per line, no negation):

/.git
/.github
/.wordpress-org
/node_modules
/tests
.distignore
.gitignore
.gitattributes
phpcs.xml.dist
/dist
/vendor/bin
composer.lock
package.json
package-lock.json

The command needs the zip binary, missing from the wordpress:cli image ("sh: zip: not found" until added), and names the archive {name}.{version}:

wp package install wp-cli/dist-archive-command:3.1.0
mkdir -p release
wp dist-archive wp-content/plugins/lumen-catalog release
cd release && unzip -q lumen-catalog.0.1.0.zip && cd ..
wp plugin check /var/www/html/release/lumen-catalog
Success: Created lumen-catalog.0.1.0.zip (Size: 18.48 KB)
Success: Checks complete. No errors found.

The zip held 19 files under lumen-catalog/: the PHP files, readme.txt, composer.json, the .pot file and the Composer autoloader. --plugin-dirname renames the folder inside the zip.

wp-scripts plugin-zip

Plugins that already use @wordpress/scripts for blocks can zip with it. plugin-zip takes the files list of package.json (the npm-packlist rules) or, without one, a default set of plugin files, and puts everything in a root folder named after the package:

{
	"name": "lumen-catalog",
	"version": "0.1.0",
	"files": [
		"lumen-catalog.php",
		"uninstall.php",
		"readme.txt",
		"composer.json",
		"src",
		"vendor",
		"languages",
		"build"
	],
	"scripts": {
		"plugin-zip": "wp-scripts plugin-zip"
	},
	"devDependencies": {
		"@wordpress/scripts": "37.0.0"
	}
}

npm run plugin-zip reported "Using the files field from package.json`", listed 20 files and wrote `lumen-catalog.zip with a lumen-catalog/ root folder (--root-folder changes it). npm always adds package.json, and missing entries such as build are skipped. Use one tool per project, so the zip, Plugin Check and SVN agree.

Submitting and the review

  1. Create or use a WordPress.org account with a regularly read email address (a company address for an organisation’s plugin), and allow mail from plugins@wordpress.org.

  2. Upload the zip on the Add your plugin page (wordpress.org/plugins/developers/add). The upload validates the header and readme, proposes the slug and warns about existing names and restricted terms.

  3. An automated email confirms the submission and the slug; the plugin joins the review queue. Most authors may have only one plugin in the queue at a time.

  4. A volunteer reviewer checks security, guideline compliance and presentation. Issues come by email with the subject "Review in Progress"; the author fixes all of them, uploads the corrected zip on the same page and replies to the email, rather than resubmitting.

  5. On approval an email gives the SVN repository. Nothing is public until code is committed to it.

Official estimates differ: one to ten days on the submission page, 14 business days on the planning page, replies within ten business days in the FAQ. An unfinished review is rejected after three months (resumable by replying); queue jumping is only for security or legal reasons.

The Subversion repository

https://plugins.svn.wordpress.org/lumen-catalog/
├── assets/     # banners, icons, screenshots, blueprints/ (not shipped to sites)
├── tags/
│   └── 0.1.0/  # one folder per release, a copy of trunk at that point
└── trunk/      # the current code; the main file sits at the top of trunk

The rules: the main file sits directly in trunk/ (a trunk/lumen-catalog/ subfolder breaks downloads); branches/ is no longer created and the directory ignores it; tags look like version numbers (0.1.0, not "first release"); no zip files and no svn:externals; nothing goes into SVN that should not reach every site. The SVN username is the case-sensitive WordPress.org username, and the password is a separate SVN password set in the account settings.

The first release

svn checkout https://plugins.svn.wordpress.org/lumen-catalog svn-lumen
cd svn-lumen
rsync -a --delete ../release/lumen-catalog/ trunk/
cp ../lumen-catalog/.wordpress-org/*.png assets/
svn add --force trunk assets
svn propset svn:mime-type image/png assets/*.png
svn cp trunk tags/0.1.0
svn status
svn commit -m "Release 0.1.0" --username <wordpress-org-username>

These steps were run against a local file:// repository with the same folders, not against WordPress.org. svn status showed the files as A and the tag as A + tags/0.1.0 (added with history: svn cp copies inside the repository), and one commit created all three folders. On WordPress.org that commit makes the plugin live.

Later releases

For 0.2.0: svn up, copy the new release over trunk/ with rsync -a --delete (then svn add new files and svn rm the ones svn status marks !), set Version: 0.2.0 in the main file and Stable tag: 0.2.0 in trunk/readme.txt, svn cp trunk tags/0.2.0, and commit once. Keep only the last release or two in tags/, and never change a released tag: a mistake is fixed with 0.2.1. When a new WordPress major version ships, raising Tested up to in both trunk/readme.txt and the current tag’s readme, without a new version, is allowed, unless release confirmation is on (below), which locks tags.

Release confirmation and the automated security review

Release confirmation is opt-in, from the plugin’s Advanced tab: a new tag is not released until a committer confirms it on the Release Management dashboard through an emailed link (the Plugins Team can require two approvals). It protects against releases from a stolen SVN password or a misfiring CI job, but releasing from trunk is then impossible and released tags can no longer be edited, not even to raise Tested up to.

Since June 2026 every release also waits in a cooldown before the update API offers it to sites. During the cooldown its changes are analysed by several AI models together with Jetpack Scan, and a release scoring at or above the risk threshold is blocked: sites keep the previous version, committers receive the findings by email, and the fix is a new release (an appeal by reply is possible but slower). A block is not a closure, and the score measures risk, not intent.

Deploying from GitHub Actions

Teams that develop in Git usually let CI do the SVN steps. The 10up WordPress.org Plugin Deploy action is a widely used community option, not a WordPress.org product: it commits a Git tag to trunk/ and tags/<version>/ and copies .wordpress-org/ to assets/. The lumen-catalog workflow builds the release folder, checks it with Plugin Check, and deploys it:

name: Release to WordPress.org

on:
  push:
    tags:
      - '[0-9]+.[0-9]+.[0-9]+'

permissions:
  contents: read

jobs:
  release:
    runs-on: ubuntu-latest
    environment: wordpress-org
    steps:
      - uses: actions/checkout@v7
      - uses: shivammathur/setup-php@2.40.0
        with:
          php-version: '7.4'
          tools: composer:v2
      - name: Build the release folder
        run: |
          composer install --no-dev --optimize-autoloader --no-interaction
          mkdir -p dist/lumen-catalog
          rsync -a --exclude-from=.distignore ./ dist/lumen-catalog/
      - name: Check that the tag matches the plugin version
        run: |
          grep -q "^ \* Version: *${GITHUB_REF_NAME}$" lumen-catalog.php
          grep -q "^Stable tag: ${GITHUB_REF_NAME}$" readme.txt
      - uses: wordpress/plugin-check-action@v1.1.9
        with:
          build-dir: dist/lumen-catalog
      - uses: 10up/action-wordpress-plugin-deploy@2.3.0
        with:
          dry-run: ${{ vars.WPORG_DRY_RUN == 'true' }}
        env:
          SVN_USERNAME: ${{ secrets.SVN_USERNAME }}
          SVN_PASSWORD: ${{ secrets.SVN_PASSWORD }}
          SLUG: lumen-catalog
          BUILD_DIR: dist/lumen-catalog
          ASSETS_DIR: .wordpress-org

The workflow passed actionlint; the build step’s rsync and the version check were run locally and produced the same file list as wp dist-archive. It was not run against WordPress.org. Notes:

  • With BUILD_DIR the action deploys that folder as it is, ignoring .distignore; without it, it filters the checked-out repository, which would lack the Composer-built vendor/.

  • SVN_PASSWORD is the account’s SVN password, kept as a secret of a protected environment. With dry-run true the action skips the commit and needs no secrets. SLUG defaults to the repository name.

  • 10up/action-wordpress-plugin-asset-update (2.2.0) pushes readme and asset changes between releases.

  • wordpress/plugin-check-action starts WordPress with wp-env, runs Plugin Check on build-dir and reports the results as file annotations.

Version, Stable tag and the Git tag must agree; the grep step stops a release where they do not.

Updates outside the directory

Plugins outside the directory declare their update source with the Update URI header (5.8) and the update_plugins_{$hostname} filter, shown with Lumen Press’s update server in Plugin Fundamentals: Update URI and private updates. For the directory:

  • The directory edition has no Update URI pointing elsewhere and no update-checker code (guideline 8). Plugin Check reports the header as plugin_updater_detected; a WordPress.org URL as value, or no header, passes.

  • A private build that must never be replaced by the directory version keeps its own Update URI, which stops WordPress.org updates for it even when the slug exists in the directory.

After publishing

Committers are managed on the plugin’s Advanced tab (also the fix for a plugin submitted from the wrong account); support happens in its forum. The Plugins Team can close a plugin over a security report and, in extreme cases, patch it. Authors close their own plugins on the Advanced tab below 10,000 users; closure is permanent, stops downloads, keeps SVN readable and shows a broad reason after 60 days. Plugins with no commit six months after approval may be closed.

Older books

The older books (WordPress All-in-One For Dummies, Pro WordPress, Ultimate WordPress Handbook) cover installing directory plugins but not publishing one: no readme.txt, Plugin Check, SVN or review. One of them states that plugins cannot depend on other plugins; since 6.5 Requires Plugins declares dependencies by WordPress.org slug, and Plugin Check 2.1.0 validates each slug against the directory. Release confirmation (2021) and the automated security review with its cooldown (June 2026) postdate all three.

Common errors and pitfalls

Symptom Cause and fix

The directory still offers the old version

Stable tag in trunk/readme.txt was not raised, or the tag folder is missing. Copy trunk to the new tag and update Stable tag in the same commit.

A new banner or icon does not appear

Wrong file name or size, files inside trunk/, or the CDN cache (up to six hours).

CI shows Plugin Check errors but the job is green

wp plugin check exits 0. Parse the JSON output and fail on ERROR entries, or use wordpress/plugin-check-action.

Downloads break after the first release

The main file is in trunk/<slug>/. Move it to the top of trunk/.

Users of a private build get the directory version

The private build has no Update URI. Add one pointing at the private server.

Security notes: SVN credentials and release automation reach every auto-updating site, so a dedicated SVN password, release confirmation and a protected CI environment matter; the cooldown review is a backstop.