-
-
Notifications
You must be signed in to change notification settings - Fork 0
Development
This guide covers contributing to MXRoute Mailer, setting up a development environment, and understanding the CI/CD pipeline.
- PHP 7.3+ with the
curl,json, andmbstringextensions - Git
- WP-CLI (optional, for local WordPress testing)
No Composer or Node.js is required. The project uses the official PHPUnit PHAR and PHP's built-in lint command.
# Clone the repository
git clone https://github.com/richardkentgates/mxroute-mailer.git
cd mxroute-mailer
# Switch to the dev branch
git checkout devTests are run with the official PHPUnit PHAR. The CI workflow downloads the PHAR automatically, but locally you can do the same:
# Download a PHPUnit PHAR compatible with PHP 7.3+
curl -Lo phpunit.phar https://phar.phpunit.de/phpunit-9.phar
chmod +x phpunit.phar
# Run all tests
./phpunit.phar --configuration phpunit.xml
# Run a specific test file
./phpunit.phar --configuration phpunit.xml tests/test-class-settings.php
# Run PHP syntax lint on all plugin files
find . -type f -name '*.php' ! -path './vendor/*' ! -path './tests/*' -print0 | xargs -0 -n1 php -lmxroute-mailer/
├── mxroute-mailer.php # Main plugin file, constants, activation hook
├── includes/
│ ├── class-mxroute-api.php # MXRoute HTTP API client
│ ├── class-mxroute-crypto.php # Reversible encryption for sensitive options
│ ├── class-mxroute-mailer.php # Core mail interception and routing
│ ├── class-mxroute-settings.php # Settings page, menus, help tabs
│ ├── class-mxroute-logger.php # Email logging to database
│ ├── class-mxroute-queue.php # Queue CRUD operations
│ ├── class-mxroute-dashboard.php # AJAX handlers for log management
│ └── class-mxroute-updater.php # GitHub-based auto-updater
├── admin/
│ ├── views/
│ │ ├── settings.php # Settings page template
│ │ ├── logs.php # Logs list page template
│ │ ├── log-view.php # Single log detail template
│ │ └── queue.php # Queue status page template
│ ├── css/admin.css # Admin styles
│ └── js/admin.js # Admin scripts
├── tests/
│ ├── bootstrap.php # Test bootstrap with mocks
│ ├── test-mxroute-mailer.php # Core mailer tests
│ ├── test-class-settings.php # Settings tests
│ ├── test-class-logger.php # Logger tests
│ ├── test-class-dashboard.php # Dashboard AJAX tests
│ ├── test-class-queue.php # Queue and API tests
│ ├── test-class-crypto.php # Encryption tests
│ ├── test-class-updater.php # Updater tests
│ ├── test-class-mxroute-api.php # API client tests
│ ├── test-edge-cases.php # Edge case and boundary tests
│ └── test-coverage-gaps.php # Coverage gap tests
├── .github/
│ ├── workflows/
│ │ ├── ci.yml # Quality and security checks
│ │ ├── version-bump.yml # Auto patch-version bump on dev push
│ │ ├── promote-to-test.yml # Dev → Test promotion
│ │ ├── promote-to-main.yml # Test → Main promotion
│ │ └── release.yml # Release build and publish
│ └── CONTRIBUTING.md # Contribution guidelines
├── wiki/ # GitHub wiki pages (local copies)
├── readme.txt # WordPress plugin readme
├── LICENSE # GPLv2 license
├── phpunit.xml # PHPUnit configuration
└── PROMOTION.md # Exact promotion workflow directive
-
dev- Active development. All new work targets this branch. -
test- Testing branch. Merged fromdevthrough the Promote to Test workflow. -
main- Production branch. Merged fromtestthrough the Promote to Main workflow.
-
Create a feature branch from
dev:git checkout dev git checkout -b feature/my-feature
-
Make changes and commit:
git add -A git commit -m "Add my feature" -
Push and create a pull request targeting
dev. -
After the PR is merged, the code is promoted through the pipeline manually:
- Dev → Test
- Test → Main
The promotion workflows are triggered manually via workflow_dispatch. Always run them from the correct source branch.
gh workflow run "Promote to Test" --repo richardkentgates/mxroute-mailer --ref devThis merges dev into test and uploads a test artifact.
gh workflow run "Promote to Main" --repo richardkentgates/mxroute-mailer --ref testThis merges test into main, creates the release tag, and triggers the Release workflow.
Every human push to dev automatically increments the patch version in mxroute-mailer.php. The bump commit is made by github-actions[bot] with [version] [skip ci] in the message so it does not re-trigger CI.
Trigger: Push to dev.
Jobs:
-
PHP Syntax Lint -
php -lon all PHP files - PHPUnit - Unit tests on PHP 7.3, 7.4, 8.0, 8.1, 8.2, and 8.3 using the official PHPUnit PHAR
-
Security - OSSF Scorecard Replacement - Runs
zizmoron workflow files and verifies all Actions references are pinned to a SHA - Security - CodeQL & PHP Vulnerability Scan - Runs CodeQL analysis and a Semgrep PHP security scan
-
Build Artifact - Builds a test zip with a top-level
mxroute-mailer/folder
Trigger: Push to dev.
Bumps the patch version in mxroute-mailer.php and pushes the change back to dev.
Trigger: Manual dispatch from the dev branch.
- Validates the source branch is
dev - Creates or finds an open PR from
devtotest - Merges the PR
- Builds a test zip artifact
Trigger: Manual dispatch from the test branch.
- Validates the source branch is
test - Creates or finds an open PR from
testtomain - Merges the PR
- Checks out
origin/mainand creates the release tag - Triggers the Release workflow for the new tag
Trigger: Push of a v* tag, or manual dispatch.
- Builds the release zip in
/tmpwith a top-levelmxroute-mailer/folder - Creates a GitHub release and attaches the zip
There is no manual version-editing step for patch releases. The pipeline handles it:
- Merge your feature PR into
dev. - The Auto Bump Version workflow increments the patch version (e.g.,
1.2.14→1.2.15). - Run Promote to Test from
dev. - Run Promote to Main from
test. - The Release workflow creates the GitHub release with the zip attached.
If you need a minor or major version bump, update the version manually in mxroute-mailer.php before pushing to dev.
All code follows the WordPress Coding Standards:
- Tabs for indentation (not spaces)
- Yoda conditions (
if ( true === $var )) - Snake_case for functions and variables
- PascalCase for class names
- Full docblocks on all public methods
- Always sanitize input:
sanitize_email(),sanitize_text_field(),sanitize_textarea_field() - Always escape output:
esc_html(),esc_attr(),esc_url() - Use
$wpdb->prepare()for all database queries - Use nonces for form submissions
- Never log or expose credentials
- Gate debug logging behind
MXROUTE_MAILER_DEBUGconstant — never log passwords or sensitive data
All classes, methods, and properties must have docblocks:
/**
* Class description.
*
* @package MXRoute_Mailer
*/
class MXRoute_Example {
/**
* Property description.
*
* @var string
*/
private $property;
/**
* Method description.
*
* @param string $param Parameter description.
* @return string Return value description.
*/
public function method( $param ) {
return $param;
}
}- Unit tests in
tests/test-*.php - Mock WordPress functions in
tests/bootstrap.php - Edge case tests in
tests/test-edge-cases.php
/**
* Tests for Example functionality.
*/
class MXRoute_Example_Test extends \PHPUnit\Framework\TestCase {
/**
* Test that something works correctly.
*/
public function test_example_works() {
$result = mxroute_mailer_example();
$this->assertEquals( 'expected', $result );
}
}The test bootstrap mocks WordPress functions to allow testing without a full WordPress installation. Key mocks include:
-
\PHPUnit\Framework\TestCasebase class (notWP_UnitTestCase) -
$wpdbdatabase abstraction with configurable query results (get_results,get_var,get_row,get_col,insert,update,delete,prepare) - WordPress functions:
get_option,update_option,delete_option,add_action,add_filter,do_action,wp_upload_dir,wp_basename - Nonce functions:
wp_create_nonce,wp_verify_nonce,check_ajax_referer - AJAX response functions:
wp_send_json_success,wp_send_json_error(throwMXRouteJSONException) - Sanitization and escaping functions:
sanitize_email,sanitize_text_field,esc_html,esc_attr - PHPMailer mock with configurable success port for SMTP smart switch testing
-
wp_remote_post,wp_remote_getmocks with configurable responses -
current_user_canmock with configurable return value via$GLOBALS['wp_mock_current_user_can'] - Constants:
MB_IN_BYTES,DAY_IN_SECONDS,ABSPATH,OBJECT,ARRAY_A,ARRAY_N
When adding new database columns:
- Add the column to the
create_table()method inclass-mxroute-logger.php - Add a migration in
mxroute_mailer_db_upgrade()that:- Checks if the column exists
- Adds it if missing
- Updates the
mxroute_mailer_db_versionoption
- The migration runs automatically on
admin_init
See CONTRIBUTING.md for detailed contribution guidelines.