Skip to content

Code quality#51

Merged
Levdbas merged 32 commits intomasterfrom
code-quality
Mar 23, 2026
Merged

Code quality#51
Levdbas merged 32 commits intomasterfrom
code-quality

Conversation

@Levdbas
Copy link
Copy Markdown
Collaborator

@Levdbas Levdbas commented Mar 23, 2026

Code Quality Improvements

This PR modernizes the codebase with updated tooling, CI/CD, and a refactored Routes class.

Changes

CI / Tooling

  • Replaces Travis CI with GitHub Actions workflows for coding standards (PHPCS), linting, Rector, and unit tests
  • Adds ecs.php (Easy Coding Standard) and rector.php configs
  • Updates .gitignore and .gitattributes

Testing

  • Migrates the test suite to the Mantle testing framework
  • Rewrites tests/RoutesTest.php with updated test cases
  • Updates phpunit.xml and tests/bootstrap.php
  • Removes the old tests/test-routes.php and bin/install-wp-tests.sh

Routes class

  • Refactors Routes.php for improved readability, maintainability, and documentation
  • Applies coding standards fixes (lint pass)

Composer

  • Updates composer.json with new dependencies and scripts

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Modernizes the project’s development and CI tooling while migrating the WordPress test setup to Mantle and refactoring the Routes plugin code to align with newer PHP/testing standards.

Changes:

  • Replace Travis-based CI with GitHub Actions for tests/linting/Rector (plus shared setup action).
  • Migrate and rewrite the test suite to Mantle (tests/bootstrap.php, tests/RoutesTest.php).
  • Update Routes.php formatting/docs and update Composer + tooling configs (ECS/Rector/PHPUnit).

Reviewed changes

Copilot reviewed 17 out of 20 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
Routes.php Refactors/modernizes the core plugin class and documentation.
composer.json Raises PHP requirement and adds dev tooling/testing dependencies and scripts.
phpunit.xml Updates PHPUnit configuration for newer PHPUnit versions and stricter failure settings.
tests/bootstrap.php Replaces WP test bootstrap with Mantle bootstrap + SQLite setup.
tests/RoutesTest.php Rewrites route behavior tests for Mantle’s Integration_Test_Case.
tests/Supports/single.php Adds a fixture file used by tests.
ecs.php / rector.php Adds code style and automated refactoring configs.
.github/workflows/php-unit-tests.yml Adds PHPUnit workflow matrix.
.github/workflows/php-lint.yml Adds parallel-lint workflow.
.github/workflows/php-rector.yml Adds Rector workflow.
.github/actions/setup/action.yml Adds composite action for PHP + Composer setup.
.gitattributes Updates export-ignore list for release archives.
README.md Updates badges/formatting.
.gitignore Updates ignored files for vendor/build/tmp/test artifacts.
Removed: .travis.yml, bin/install-wp-tests.sh, tests/test-routes.php, tests/single.php Removes Travis + legacy WP test scaffolding in favor of Mantle + Actions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +17 to +35
function cache_sqlite_develop(string $cacheDir): void
{
$sqliteZip = $cacheDir . '/sqlite-database-integration-main.zip';

if (is_file($sqliteZip)) {
return;
}

if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
}

// Download develop branch, extract, rename folder, re-zip with correct name
$url = 'https://github.com/WordPress/sqlite-database-integration/archive/refs/heads/develop.zip';
shell_exec('curl -sL ' . escapeshellarg($url) . ' -o ' . escapeshellarg("$cacheDir/tmp.zip"));
shell_exec('unzip -q ' . escapeshellarg("$cacheDir/tmp.zip") . ' -d ' . escapeshellarg($cacheDir));
rename("$cacheDir/sqlite-database-integration-develop", "$cacheDir/sqlite-database-integration-main");
shell_exec('cd ' . escapeshellarg($cacheDir) . ' && zip -rq sqlite-database-integration-main.zip sqlite-database-integration-main');
shell_exec('rm -rf ' . escapeshellarg("$cacheDir/tmp.zip") . ' ' . escapeshellarg("$cacheDir/sqlite-database-integration-main"));
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cache_sqlite_develop() relies on shell_exec() plus external tools (curl, unzip, zip, rm) with no error handling. This can make test bootstrapping fail in environments where these binaries or shell_exec are unavailable/disabled. Prefer using PHP (e.g., ZipArchive/curl extension) or add explicit checks and actionable failures when commands fail.

Suggested change
function cache_sqlite_develop(string $cacheDir): void
{
$sqliteZip = $cacheDir . '/sqlite-database-integration-main.zip';
if (is_file($sqliteZip)) {
return;
}
if (!is_dir($cacheDir)) {
mkdir($cacheDir, 0755, true);
}
// Download develop branch, extract, rename folder, re-zip with correct name
$url = 'https://github.com/WordPress/sqlite-database-integration/archive/refs/heads/develop.zip';
shell_exec('curl -sL ' . escapeshellarg($url) . ' -o ' . escapeshellarg("$cacheDir/tmp.zip"));
shell_exec('unzip -q ' . escapeshellarg("$cacheDir/tmp.zip") . ' -d ' . escapeshellarg($cacheDir));
rename("$cacheDir/sqlite-database-integration-develop", "$cacheDir/sqlite-database-integration-main");
shell_exec('cd ' . escapeshellarg($cacheDir) . ' && zip -rq sqlite-database-integration-main.zip sqlite-database-integration-main');
shell_exec('rm -rf ' . escapeshellarg("$cacheDir/tmp.zip") . ' ' . escapeshellarg("$cacheDir/sqlite-database-integration-main"));
/**
* Recursively add a directory to a ZipArchive.
*
* @param \ZipArchive $zip
* @param string $directory Absolute path to the directory to add.
* @param string $baseInArchive Path prefix inside the archive.
*/
function cache_sqlite_add_directory_to_zip(\ZipArchive $zip, string $directory, string $baseInArchive = ''): void
{
$directory = rtrim($directory, DIRECTORY_SEPARATOR);
if (!is_dir($directory)) {
return;
}
$items = scandir($directory);
if ($items === false) {
throw new \RuntimeException("Failed to read directory: {$directory}");
}
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$fullPath = $directory . DIRECTORY_SEPARATOR . $item;
$localName = ltrim($baseInArchive . '/' . $item, '/');
if (is_dir($fullPath)) {
if (!$zip->addEmptyDir($localName)) {
throw new \RuntimeException("Failed to add directory to zip: {$localName}");
}
cache_sqlite_add_directory_to_zip($zip, $fullPath, $localName);
} elseif (is_file($fullPath)) {
if (!$zip->addFile($fullPath, $localName)) {
throw new \RuntimeException("Failed to add file to zip: {$fullPath}");
}
}
}
}
/**
* Recursively remove a directory and its contents.
*/
function cache_sqlite_rrmdir(string $directory): void
{
if (!is_dir($directory)) {
return;
}
$items = scandir($directory);
if ($items === false) {
throw new \RuntimeException("Failed to read directory for removal: {$directory}");
}
foreach ($items as $item) {
if ($item === '.' || $item === '..') {
continue;
}
$path = $directory . DIRECTORY_SEPARATOR . $item;
if (is_dir($path)) {
cache_sqlite_rrmdir($path);
} elseif (is_file($path)) {
if (!unlink($path)) {
throw new \RuntimeException("Failed to delete file: {$path}");
}
}
}
if (!rmdir($directory)) {
throw new \RuntimeException("Failed to remove directory: {$directory}");
}
}
function cache_sqlite_develop(string $cacheDir): void
{
$sqliteZip = $cacheDir . '/sqlite-database-integration-main.zip';
// If the cached zip already exists, assume it's valid and skip work.
if (is_file($sqliteZip) && filesize($sqliteZip) > 0) {
return;
}
if (!is_dir($cacheDir)) {
if (!mkdir($cacheDir, 0755, true) && !is_dir($cacheDir)) {
throw new \RuntimeException("Failed to create cache directory: {$cacheDir}");
}
}
$tmpZip = $cacheDir . '/tmp.zip';
// Download develop branch, extract, rename folder, re-zip with correct name.
$url = 'https://github.com/WordPress/sqlite-database-integration/archive/refs/heads/develop.zip';
$zipData = @file_get_contents($url);
if ($zipData === false) {
// Fallback to curl extension if available.
if (function_exists('curl_init')) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
curl_setopt($ch, CURLOPT_FAILONERROR, true);
$zipData = curl_exec($ch);
$curlError = curl_error($ch);
curl_close($ch);
if ($zipData === false) {
throw new \RuntimeException("Failed to download SQLite develop zip via curl: {$curlError}");
}
} else {
throw new \RuntimeException(
'Failed to download SQLite develop zip: allow_url_fopen is disabled and curl extension is unavailable.'
);
}
}
if (file_put_contents($tmpZip, $zipData) === false) {
throw new \RuntimeException("Failed to write temporary zip file: {$tmpZip}");
}
if (!class_exists(\ZipArchive::class)) {
throw new \RuntimeException(
'ZipArchive extension is required to prepare the SQLite develop cache but is not available.'
);
}
$zip = new \ZipArchive();
$openResult = $zip->open($tmpZip);
if ($openResult !== true) {
throw new \RuntimeException("Failed to open downloaded zip file ({$tmpZip}); ZipArchive error code: {$openResult}");
}
if (!$zip->extractTo($cacheDir)) {
$zip->close();
throw new \RuntimeException("Failed to extract downloaded zip to cache directory: {$cacheDir}");
}
$zip->close();
$developDir = $cacheDir . '/sqlite-database-integration-develop';
$mainDir = $cacheDir . '/sqlite-database-integration-main';
if (!is_dir($developDir)) {
throw new \RuntimeException(
"Expected extracted directory not found: {$developDir}"
);
}
// Remove any stale main directory before renaming.
if (is_dir($mainDir)) {
cache_sqlite_rrmdir($mainDir);
}
if (!rename($developDir, $mainDir)) {
throw new \RuntimeException(
"Failed to rename {$developDir} to {$mainDir}"
);
}
// Create the expected sqlite-database-integration-main.zip.
$zip = new \ZipArchive();
$openResult = $zip->open($sqliteZip, \ZipArchive::CREATE | \ZipArchive::OVERWRITE);
if ($openResult !== true) {
throw new \RuntimeException(
"Failed to create SQLite cache zip ({$sqliteZip}); ZipArchive error code: {$openResult}"
);
}
cache_sqlite_add_directory_to_zip($zip, $mainDir, 'sqlite-database-integration-main');
$zip->close();
// Clean up temporary files and directories.
if (is_file($tmpZip) && !unlink($tmpZip)) {
throw new \RuntimeException("Failed to delete temporary zip file: {$tmpZip}");
}
if (is_dir($mainDir)) {
cache_sqlite_rrmdir($mainDir);
}

Copilot uses AI. Check for mistakes.

- name: Run Rector checks
if: steps.changed-files.outputs.all_changed_files != ''
run: ./vendor/bin/rector process --dry-run ${{ steps.changed-files.outputs.files }}
Copy link

Copilot AI Mar 23, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This workflow checks all_changed_files in the if: condition, but the run: command uses steps.changed-files.outputs.files, which isn’t produced by tj-actions/changed-files (so Rector may run with an empty file list). Use the same all_changed_files output (optionally filtered to PHP files) in the Rector command.

Suggested change
run: ./vendor/bin/rector process --dry-run ${{ steps.changed-files.outputs.files }}
run: ./vendor/bin/rector process --dry-run ${{ steps.changed-files.outputs.all_changed_files }}

Copilot uses AI. Check for mistakes.
Levdbas and others added 6 commits March 23, 2026 22:31
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
@Levdbas Levdbas merged commit 5553211 into master Mar 23, 2026
14 checks passed
@Levdbas Levdbas deleted the code-quality branch March 23, 2026 21:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants