|
| 1 | +# Contribution Guidelines |
| 2 | + |
| 3 | +Thanks for taking the time to contribute to Yoast Duplicate Post! Before filing a bug report, feature request, or pull request, please read the guidelines below. |
| 4 | + |
| 5 | +This file is the canonical contributor guide for this repository. It is written for both humans and AI coding tools. The repo-root [`AGENTS.md`](../AGENTS.md) adds a small set of behaviours specific to AI agents on top of the rules here; the [`PULL_REQUEST_TEMPLATE.md`](./PULL_REQUEST_TEMPLATE.md) carries the detailed changelog and label rules. |
| 6 | + |
| 7 | +## Contents |
| 8 | + |
| 9 | +- [How to use GitHub](#how-to-use-github) |
| 10 | +- [Security issues](#security-issues) |
| 11 | +- [I have found a bug](#i-have-found-a-bug) |
| 12 | +- [I have a feature request](#i-have-a-feature-request) |
| 13 | +- [I want to create a patch](#i-want-to-create-a-patch) |
| 14 | + - [License and copyright](#license-and-copyright) |
| 15 | + - [Supported environment](#supported-environment) |
| 16 | + - [Repository layout](#repository-layout) |
| 17 | + - [Where to put new code](#where-to-put-new-code) |
| 18 | + - [Legacy procedural files](#legacy-procedural-files) |
| 19 | + - [No dependency-injection container](#no-dependency-injection-container) |
| 20 | + - [PHP workflow](#php-workflow) |
| 21 | + - [JavaScript workflow](#javascript-workflow) |
| 22 | + - [Testing](#testing) |
| 23 | + - [Code style](#code-style) |
| 24 | + - [Opening a pull request](#opening-a-pull-request) |
| 25 | + - [Before you push or open/update a PR](#before-you-push-or-openupdate-a-pr) |
| 26 | + - [Changelog entry and label](#changelog-entry-and-label) |
| 27 | + - [Submitting an issue you have found](#submitting-an-issue-you-have-found) |
| 28 | +- [Additional resources](#additional-resources) |
| 29 | + |
| 30 | +## How to use GitHub |
| 31 | + |
| 32 | +We use GitHub exclusively for well-documented bugs, feature requests, and code contributions. Communication is always done in English. |
| 33 | + |
| 34 | +For support with Duplicate Post, use the [support forum](https://wordpress.org/support/plugin/duplicate-post) on WordPress.org. |
| 35 | + |
| 36 | +## Security issues |
| 37 | + |
| 38 | +Please do **not** report security issues on GitHub. Follow our [security program](https://yoast.com/security-program/) instead — see [`yoast.com/security.txt`](https://yoast.com/security.txt) for the canonical contact details — so we can handle them quickly and responsibly. |
| 39 | + |
| 40 | +## I have found a bug |
| 41 | + |
| 42 | +Before opening a new issue, please: |
| 43 | + |
| 44 | +* update to the latest versions of WordPress and Duplicate Post. |
| 45 | +* search for duplicate issues to avoid filing the same report twice. If an open issue already exists, please comment on it. |
| 46 | +* check for plugin and theme conflicts, and include your findings. |
| 47 | +* check for JavaScript errors in your browser's console and include any output. |
| 48 | +* pick the matching GitHub issue form (Bug report, Feature request, Task) and fill in every section. |
| 49 | +* include everything needed to understand and reproduce the problem — screenshots, clear reproduction steps, plugin and theme versions, and any relevant logs — but stay focused. A tight, reproducible report is easier to triage than a long narrative with unrelated context. |
| 50 | + |
| 51 | +## I have a feature request |
| 52 | + |
| 53 | +Before opening a new issue: |
| 54 | + |
| 55 | +* search for duplicate issues to avoid filing the same request twice. If an open request already exists, please add your thoughts there. |
| 56 | +* pick the Feature request issue form and explain *why* you think this feature is worth considering. |
| 57 | + |
| 58 | +## I want to create a patch |
| 59 | + |
| 60 | +Community patches, localizations, bug reports, and contributions are very welcome. |
| 61 | + |
| 62 | +### License and copyright |
| 63 | + |
| 64 | +Duplicate Post is licensed under [GPL-2.0-or-later](../LICENSE). By opening a pull request you confirm that your contribution is offered under the same license. Before contributing code, make sure that: |
| 65 | + |
| 66 | +- You wrote the code yourself, or you have the right to relicense it under GPL-2.0-or-later. |
| 67 | +- You have not copied code from sources whose license is incompatible with GPL-2.0-or-later (for example proprietary code, CC-licensed snippets that restrict commercial use, or GPL-3.0-only code). |
| 68 | +- If you have reused code from a GPL-2.0-compatible source (MIT, BSD, public domain, etc.), you have preserved the original copyright notice and license header, and noted the provenance in the commit message. |
| 69 | +- You have not included code whose licensing status is unclear — including AI-generated code whose training or output terms you have not verified as compatible. |
| 70 | + |
| 71 | +If you are unsure whether a piece of code is safe to include, ask in the issue or PR before opening it for review. |
| 72 | + |
| 73 | +### Supported environment |
| 74 | + |
| 75 | +* PHP: the minimum version is the `Requires PHP` header in [`readme.txt`](../readme.txt) (also pinned in [`composer.json`](../composer.json)). |
| 76 | +* WordPress: the supported range is the `Requires at least` and `Tested up to` headers in [`readme.txt`](../readme.txt). |
| 77 | +* The plugin is a single PHP plugin with a small JavaScript bundle (block-editor integration) built through webpack and Grunt. |
| 78 | + |
| 79 | +### Repository layout |
| 80 | + |
| 81 | +The top-level paths you will touch (or explicitly avoid): |
| 82 | + |
| 83 | +| Path | Purpose | Editable? | |
| 84 | +| --- | --- | --- | |
| 85 | +| `src/` | Namespaced PHP (`Yoast\WP\Duplicate_Post\`). All new backend work lives here. | Yes | |
| 86 | +| `js/src/` | Source for the block-editor JavaScript. | Yes | |
| 87 | +| `tests/` | PHPUnit tests (`tests/Unit`, `tests/WP`). | Yes | |
| 88 | +| `config/` | Grunt, webpack, wp-env, composer actions, build scripts. | Yes, with care | |
| 89 | +| `compat/` | Compatibility shims for third-party plugins. | Yes, with care | |
| 90 | +| `admin-functions.php`, `common-functions.php`, `options.php` | Legacy procedural PHP. | **Maintenance only** — see below | |
| 91 | +| `duplicate-post.php` | Plugin bootstrap and manual service wiring. | With care | |
| 92 | +| `vendor/`, `node_modules/` | Composer / Yarn dependencies. | Never hand-edit | |
| 93 | +| `js/dist/`, `artifact/`, `languages/` | Generated or distribution artifacts. | Never hand-edit — regenerate via Grunt | |
| 94 | +| `readme.txt`, `changelog.md` | wordpress.org readme and the generated changelog. | See [Changelog entry and label](#changelog-entry-and-label) | |
| 95 | + |
| 96 | +### Where to put new code |
| 97 | + |
| 98 | +`src/` is classmap-autoloaded under the `Yoast\WP\Duplicate_Post\` namespace. Code is grouped by its role in the plugin rather than by onion layers: |
| 99 | + |
| 100 | +``` |
| 101 | +src/ |
| 102 | +├── admin/ Admin screens, settings, and their views. |
| 103 | +├── handlers/ Request handlers (bulk, REST, save-post, links, …). |
| 104 | +├── ui/ User-facing surfaces (metabox, block editor, columns, row actions, …). |
| 105 | +├── watchers/ Hook listeners that react to post lifecycle events. |
| 106 | +└── *.php Shared services (post-duplicator, post-republisher, permissions-helper, utils, …). |
| 107 | +``` |
| 108 | + |
| 109 | +When you extend an existing feature, keep the new code in the matching folder and follow the surrounding patterns. New files are a last resort — prefer extending an existing class. PHP files are kebab-case (`link-handler.php`); class names follow Yoast's convention of snake_case with underscores (e.g. `Link_Handler`). |
| 110 | + |
| 111 | +#### Legacy procedural files |
| 112 | + |
| 113 | +`admin-functions.php`, `common-functions.php`, and `options.php` contain pre-namespace procedural code. Treat them as **maintenance-only**: fix bugs and keep them compatible, but do not add new features there. When a legacy function needs significant changes, consider extracting the affected responsibility into a class under `src/`. |
| 114 | + |
| 115 | +### No dependency-injection container |
| 116 | + |
| 117 | +Unlike Yoast SEO, Duplicate Post does **not** use a compiled DI container. Services are instantiated and wired by hand in the `duplicate-post.php` bootstrap. There is no `compile-di` step — when you add a service, wire it explicitly in the bootstrap and pass its dependencies through the constructor. |
| 118 | + |
| 119 | +### PHP workflow |
| 120 | + |
| 121 | +All commands are run from the repo root. |
| 122 | + |
| 123 | +| Command | What it does | |
| 124 | +| --- | --- | |
| 125 | +| `composer update` | Install PHP dependencies. `composer.lock` is not committed in this repo, so use `update` rather than `install`. | |
| 126 | +| `composer lint` | PHP parse-error check across the repo. | |
| 127 | +| `composer check-cs` | Run phpcs with the Yoast ruleset (errors only, no warnings). | |
| 128 | +| `composer check-branch-cs` | Run phpcs against the files changed on the current branch. | |
| 129 | +| `composer check-staged-cs` | Run phpcs against staged files. | |
| 130 | +| `composer fix-cs` | Auto-fix fixable phpcs violations. | |
| 131 | +| `composer test` | Run PHPUnit unit tests (no WP, no coverage). | |
| 132 | +| `composer test-wp` | Run the WP integration tests against a local WP test install. | |
| 133 | +| `composer test-wp-env` | Run the WP integration tests inside the `wp-env` Docker environment (preferred locally). | |
| 134 | +| `composer coverage` / `coverage-wp-env` | The test commands above, with coverage. | |
| 135 | + |
| 136 | +Coding standards are enforced by the Yoast Coding Standard (`yoast/yoastcs`) — a superset of the WordPress Coding Standards — plus parallel-lint for syntax. Run `composer check-branch-cs` before opening a PR. |
| 137 | + |
| 138 | +### JavaScript workflow |
| 139 | + |
| 140 | +The block-editor JavaScript is built through Grunt (which drives webpack). `package.json` has no npm scripts; use Grunt directly. |
| 141 | + |
| 142 | +| Command | What it does | |
| 143 | +| --- | --- | |
| 144 | +| `yarn install` | Install JS build dependencies. | |
| 145 | +| `grunt` / `grunt build` | Build the block-editor JS bundle (webpack development build). | |
| 146 | +| `grunt release` | Production JS build (used by the release/artifact pipeline). | |
| 147 | +| `grunt build:images` | Optimise the wp.org store assets in `svn-assets/` (banner, icons, screenshots) via imagemin. Run manually only when you change those assets — it is **not** part of `grunt build` or the release pipeline. | |
| 148 | + |
| 149 | +### Testing |
| 150 | + |
| 151 | +- **Every PR that changes PHP behaviour should ship unit tests.** Place them under `tests/Unit/…`, mirroring the path of the class under test. |
| 152 | +- **Integration tests** (those that boot WordPress) live under `tests/WP/…` and run via `composer test-wp-env`, which starts an isolated WP in Docker through `@wordpress/env`. |
| 153 | +- Test one method per test class where practical; share setup via abstract base classes or traits (examples already exist under `tests/Unit`). |
| 154 | +- If you cannot run the integration tests in your environment, say so explicitly in the PR description rather than skipping them silently. |
| 155 | + |
| 156 | +### Code style |
| 157 | + |
| 158 | +- Follow the existing code. When two styles look plausible, match the file you are editing. |
| 159 | +- PHP: Yoast CS (`yoast/yoastcs`), configured in [`.phpcs.xml.dist`](../.phpcs.xml.dist). Namespaces live under `Yoast\WP\Duplicate_Post\…`. |
| 160 | +- The CS check enforces an error/warning **threshold** (see the `check-cs-thresholds` composer script). Do not raise the threshold to make a violation pass — fix the violation. |
| 161 | +- Comments: document **why**, not **what**. End every inline comment with a full stop. |
| 162 | +- Don't add features, scaffolding, or abstractions the task doesn't need. |
| 163 | + |
| 164 | +### Opening a pull request |
| 165 | + |
| 166 | +1. Create your branch from `trunk`. `trunk` is the active development branch and the default branch on GitHub — every PR should target it unless a maintainer asks otherwise. When the work tracks a GitHub issue, name your branch `<issue-number>-<short-description>` (e.g. `210-scheduled-republish`). |
| 167 | +2. Make your changes. |
| 168 | +3. Follow the [Yoast Coding Standards](https://github.com/Yoast/yoastcs). |
| 169 | +4. Document any new functions, actions, and filters following the [PHP inline-documentation standards](https://make.wordpress.org/core/handbook/best-practices/inline-documentation-standards/php/). |
| 170 | +5. Write tests. We expect every PR that changes PHP behaviour to ship unit tests. |
| 171 | +6. Use the [Conventional Commits](https://www.conventionalcommits.org/) format for commit messages (e.g. `fix: …`, `feat(ui): …`). **Prefer atomic commits** — each commit should represent a single logical change. If your PR mixes unrelated changes, split them into separate commits or ideally separate PRs. |
| 172 | +7. Push your branch and open a pull request against `trunk`. **Use the pull request template** at [`.github/PULL_REQUEST_TEMPLATE.md`](./PULL_REQUEST_TEMPLATE.md) and fill in every section — even for small changes. |
| 173 | +8. **Keep the PR description focused.** Fill every required section of the template with what the reviewer actually needs. *Test instructions* should be concrete steps, not essays. Link to the issue rather than restating it. |
| 174 | + |
| 175 | +#### Before you push or open/update a PR |
| 176 | + |
| 177 | +Run these checks locally and make sure each one is clean. CI runs the same checks. |
| 178 | + |
| 179 | +* `composer test` — the unit test suite must pass. |
| 180 | +* `composer test-wp-env` — the WordPress integration tests (Docker via `@wordpress/env`) must pass if your change touches code that has or needs WP integration coverage. |
| 181 | +* `composer check-branch-cs` — must report **no new errors or warnings** introduced by your branch. Use `composer fix-cs` to auto-fix what it can, and address the rest by hand. |
| 182 | +* `composer lint` — PHP parse-error check. |
| 183 | +* For changes under `js/`: run `grunt build` and confirm the bundle builds. |
| 184 | +* Only if you changed a wp.org store asset under `svn-assets/`: run `grunt build:images` and commit the optimised output. (It is not run by `grunt build` or the release pipeline, so it will not happen automatically.) |
| 185 | + |
| 186 | +If a check fails or you need to skip one (e.g. you can't run Docker locally for `test-wp-env`), say so explicitly in the PR description so reviewers know what still needs validating. |
| 187 | + |
| 188 | +### Changelog entry and label |
| 189 | + |
| 190 | +Every PR needs a **changelog entry** in the Summary section of the PR body and a **changelog label** on the PR itself: |
| 191 | + |
| 192 | +* Write one bullet describing the change in present tense, 3rd person singular, ending with a full stop. For bugfixes, describe the incorrect behaviour followed by the condition that triggered it, in clear past tense (e.g. `Fixes a bug where X happened when Y`). See [`PULL_REQUEST_TEMPLATE.md`](./PULL_REQUEST_TEMPLATE.md) for the full grammar. |
| 193 | +* Keep each bullet to one short sentence. Extra context belongs in *Context* or *Relevant technical choices*, not in the bullet. |
| 194 | +* Attach one of: `changelog: bugfix`, `changelog: enhancement`, `changelog: other`, `changelog: non-user-facing`. |
| 195 | +* If the change also affects another Yoast repo, add an extra bullet prefixed with `[<repo-name>]`. |
| 196 | +* The release `changelog.md` is generated from merged PRs by [`.github/scripts/generate-changelog.sh`](./scripts/generate-changelog.sh) — do not hand-write release sections into it; the PR body's bullet and label are the source. |
| 197 | + |
| 198 | +Milestones are set by the maintainer who merges your PR. |
| 199 | + |
| 200 | +### Submitting an issue you have found |
| 201 | + |
| 202 | +Make sure your problem doesn't already have a ticket by searching [the existing issues](https://github.com/Yoast/duplicate-post/issues). If you can't find anything matching, please [open a new issue](https://github.com/Yoast/duplicate-post/issues/new/choose). |
| 203 | + |
| 204 | +## Additional resources |
| 205 | + |
| 206 | +* [Yoast developer portal](https://developer.yoast.com/) |
| 207 | +* [General GitHub documentation](https://docs.github.com/) |
| 208 | +* [GitHub Pull Request documentation](https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) |
0 commit comments