-
Notifications
You must be signed in to change notification settings - Fork 214
Development and contributing
This page covers everything you need to get a working development setup, run the test suites, and submit a contribution back to the plugin.
- Node.js 18+ and npm — for the build tooling and the wp-env harness.
- Composer 2+ — for PHP dependencies and the PHPCS/PHPUnit runners.
- Docker Desktop or Docker Engine — wp-env runs its WordPress containers against Docker.
- PHP 7.4+ on your host — only needed if you want to run PHPCS and PHPUnit outside the container.
git clone https://github.com/Automattic/co-authors-plus.git
cd co-authors-plus
composer install
npm install
npm run buildThe npm run build step compiles the block editor assets. If you skip it, the block-related integration tests will report one skipped case and the editor sidebar won't render your changes to JS source until you re-run it.
The plugin ships a .wp-env.json that brings up two WordPress sites — one for manual poking and one for integration tests.
npx wp-env startThis starts both environments. If the default ports (8888/8889) clash with something else you have running locally, create a .wp-env.override.json next to .wp-env.json and set alternate ports there; the override file is gitignored.
Once started:
-
Development site: http://localhost:8888 — admin login
admin/password. - Tests site: http://localhost:8889 — not meant for manual browsing; that's what the integration suite hits.
npx wp-env stop shuts both sites down; npx wp-env destroy removes their volumes if you need a clean slate.
CAP has three test suites, each with a different scope.
This is the primary test suite. It exercises the plugin against a real WordPress install and real database.
composer test:integration # single-site
composer test:integration-ms # multisiteBoth scripts run inside the wp-env tests container. If you get "Environment not initialised", you forgot npx wp-env start.
To run a single test class or filter:
# Replace the path with the full composer script expansion.
npx wp-env run tests-cli --env-cwd=wp-content/plugins/co-authors-plus \
./vendor/bin/phpunit --exclude=ms-required --no-coverage \
--filter=CoAuthorsControllerTestCoverage reports (HTML):
composer coverage
open .phpunit.cache/coverage-html/index.htmlTests live under tests/Integration/. Test groups ms-required (multisite-only) and ms-excluded (single-site-only) are used to keep the two modes compatible.
End-to-end UI tests, used sparingly for flows where an integration test can't cover the interaction.
composer prepare-behat-tests # starts wp-env if not already running
composer behat # runs features/
composer behat-rerun # reruns only the previously-failed scenariosFeatures live under features/.
composer cs # report violations
composer cs-fix # auto-fix what can be fixed
composer lint # php-parallel-lint for parse errorsPHPCS enforces two profiles — WPVIP Minimum and WP-CS — defined in .phpcs.xml.dist. The tests/ directory has its own profile; CI treats tests as must-pass but runtime-code violations as informational (they block PRs soft but not hard). Always run composer cs -- <path-to-changed-files> before pushing; it's faster than letting CI tell you.
If you're making more than a surface change, these are the pieces worth knowing about.
-
Taxonomy:
author, registered on every post type that supportsauthor(filterable viacoauthors_supported_post_types). Each term's slug iscap-<user_nicename>. -
Post type:
guest-author, private, accessible under Users → Guest Authors. Guest authors are stored as posts of this type, with profile fields as post meta (keys prefixedcap-). -
Per-post authorship: a post's byline is the set of
authorterms assigned to it, resolved to WP users or guest authors by slug. -
Fallback: on posts without any
authorterms (e.g. pre-CAP legacy posts), the plugin can optionally fall back topost_author, gated bycoauthors_plus_should_query_post_author. See Backfilling coauthor terms for legacy posts for the CLI command that seeds missing terms.
| Class | File | Responsibility |
|---|---|---|
CoAuthors_Plus |
php/class-coauthors-plus.php |
The orchestrator: taxonomy registration, meta box, REST bridges, save hooks, AJAX search, filters. |
CoAuthors_Guest_Authors |
php/class-coauthors-guest-authors.php |
The guest-author CPT, its admin UI, profile fields, import/export. |
CoAuthors_WP_List_Table |
php/class-coauthors-wp-list-table.php |
The admin list table at Users → Guest Authors. |
CoAuthors\API\Endpoints |
php/class-coauthors-endpoint.php |
Older plugin-UI REST routes (/coauthors/v1/search, /authors/{id}, etc.). |
CoAuthors\API\Endpoints\CoAuthors_Controller |
php/api/endpoints/class-coauthors-controller.php |
Newer publish-aware REST routes (/coauthors/v1/coauthors). |
CoAuthors\Blocks |
php/blocks/class-blocks.php |
Block editor integration and server-side render callbacks. |
CoAuthorsPlus_Command |
php/class-wp-cli.php |
All wp co-authors-plus subcommands. |
Template tag functions live in template-tags.php at the plugin root — not inside any class, by historical convention.
The block editor uses WordPress core's entity store rather than a custom Redux store. Author taxonomy term IDs flow through as the coauthors field on /wp/v2/posts responses; the editor sidebar resolves them to rich data via /coauthors/v1/authors-by-term-ids and saves back via rest_after_insert_{post_type}.
See REST API for the endpoint shapes.
If you're working in the guts of the plugin, these are the ones you'll touch most often:
-
rest_prepare_coauthor/rest_coauthors_item_schema— REST response mutation. -
coauthors_plus_edit_authors— the master permission check for assignment. -
coauthors_plus_should_query_post_author— the fallback toggle with large performance implications. -
coauthors_supported_post_types— what CAP hooks onto.
The full list is at Hooks and filters.
-
main— latest released code. Don't open PRs against it. -
develop— the integration branch for the next release. Open PRs here. - Feature branches are fine to keep in the main repo if you have commit access (
GaryJones/<thing>is the existing convention); external contributors should fork.
The bar for a mergeable PR:
- Targets
develop. -
Passes CI, specifically the
Basic CS and QA checksjob (test-suite PHPCS is blocking), the integration tests on both the lowest and latest supported PHP/WP combinations, and PHP linting. - Has a test that demonstrates the behaviour change, unless the PR is a pure refactor or docs-only change. For bug fixes, prefer a test that would have failed before the fix.
- Touches no more than it needs to. Keep formatting-only changes in their own commits so reviewers can see the substance at a glance.
A good PR description explains the why: what problem does this solve, what was the alternative, and what edge cases are in or out of scope? A bare "Fixes #NNN" is acceptable for tiny fixes but rarely enough for anything substantive.
The project uses conventional commit prefixes (fix:, feat:, chore:, test:, docs:, refactor:) in its commit history. Aim for a subject line under 72 characters explaining the outcome, plus a body explaining the reasoning. See the existing history for the house style.
From CONTRIBUTING.md:
If talking about co-authors in a documentation, DocBlock, comment, or user-facing string, please include the hyphen. Variables, constants, class, trait, interface, function, method, hook, and other programmatic names can all use no-hyphen.
So: get_coauthors() the function, "co-authors" the noun. This wiki follows the same rule.
Maintainers only — external contributors can skip this section.
CAP uses version-bump-prompt (the bump npm CLI) for patch, minor and major bumps:
npm run bump:patch # 4.0.0 → 4.0.1
npm run bump:minor # 4.0.x → 4.1.0
npm run bump:major # 4.x.y → 5.0.0Each command commits the version change to package.json, package-lock.json, co-authors-plus.php and README.md in one pass. After the bump:
- Update
CHANGELOG.mdwith the release entry. - Open a release PR to
mainfromdevelop. - Once merged, publish a GitHub Release. The
.github/workflows/deploy.ymlworkflow triggers onrelease: releasedand pushes the build to the WordPress.org SVN repository.
Open a discussion or issue on the plugin's GitHub repository — that's the canonical place for contributor questions. The Automattic/vip-plugins team is the owning team and does the bulk of reviews.