-
Notifications
You must be signed in to change notification settings - Fork 6
CI: Add SonarCloud, fix PHPStan on PHP 7.4/8.0, refine workflows #133
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CI: Add SonarCloud, fix PHPStan on PHP 7.4/8.0, refine workflows #133
Conversation
- Add sonar-project.properties: org=xoops, projectKey=XOOPS_xmf, PHP 7.4, coverage from clover.xml - Add .github/workflows/sonarcloud.yml: runs tests with coverage then SonarCloud scan on push to master and PRs - ci.yml: split test step into coverage/non-coverage to generate clover.xml only on coverage row - qodana.yml: fix checkout ref to fall back to github.sha on push - Remove legacy phpcs.xml.dist and phpunit10.xml.dist - Add sonar-project.properties to .gitattributes export-ignore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The baseline file uses PHPStan 2.x format (message/path singular, identifier, count fields) which PHPStan 1.x (installed on PHP 7.4 and 8.0) does not understand. PHPStan 2.x requires PHP 8.1+. Tests and lint still run on all PHP versions (7.4-8.5). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Note
|
| Cohort / File(s) | Summary |
|---|---|
SonarCloud integration \.github/workflows/sonarcloud.yml, sonar-project.properties |
Introduces a new SonarCloud GitHub Actions workflow (checkout, PHP 8.3 setup, composer install/update, PHPUnit with Clover coverage, SonarSource scan) and adds SonarQube project config with sources/tests, PHP version, and coverage path. |
CI workflows & action pinning \.github/workflows/ci.yml, \.github/workflows/qodana.yml |
Pins several actions to specific commits/hashes, adds conditional test steps (separate coverage vs non-coverage test runs), adds repo gating and checkout ref fallback for Qodana workflow. |
Repository exports / metadata \.gitattributes |
Adds export-ignore entries for sonar-project.properties and CHANGELOG.md. |
Code quality / tests phpcs.xml, phpunit10.xml.dist, qodana.yaml |
Disables Generic.Files.LineLength rule (severity 0 / removes limits), removes phpunit10.xml.dist file, and updates Qodana image tag to jetbrains/qodana-php:2025.2. |
Changelog CHANGELOG.md |
Replaces Unreleased header with versioned release entries (1.2.33, 1.2.33-beta2) and reorganizes historical entries; many bugfix and infra notes added. |
Sequence Diagram(s)
sequenceDiagram
autonumber
participant GH as GitHub Actions
participant Runner as Runner (ubuntu-latest)
participant Repo as Repository
participant PHP as PHP runtime (8.3)
participant Composer as Composer
participant PHPUnit as PHPUnit
participant Sonar as SonarCloud
GH->>Runner: triggered (push/PR)
Runner->>Repo: checkout (fetch-depth:0, pinned ref)
Runner->>PHP: setup PHP 8.3 (intl, mbstring, xdebug)
Runner->>Composer: install/update deps
Runner->>PHPUnit: run tests + generate Clover (build/logs/clover.xml)
PHPUnit-->>Runner: clover.xml
Runner->>Sonar: run SonarSource scan (uses SONAR_TOKEN)
Sonar-->>Runner: analysis result
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~20 minutes
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title check | ✅ Passed | The title accurately reflects the main changes: adding SonarCloud integration, fixing PHPStan on older PHP versions, and refining CI workflows—all core objectives of this PR. |
| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check. |
✏️ Tip: You can configure your own custom pre-merge checks in the settings.
✨ Finishing touches
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Post copyable unit tests in a comment
Comment @coderabbitai help to get the list of available commands and usage tips.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Updates the repository’s CI configuration to integrate SonarCloud and stabilize static analysis across the supported PHP matrix by only running PHPStan on PHP ≥ 8.1, while keeping tests/lint across PHP 7.4–8.5.
Changes:
- Add SonarCloud configuration + workflow to run coverage and publish analysis.
- Adjust CI workflow to (a) skip PHPStan on PHP < 8.1 and (b) split coverage vs non-coverage test execution.
- Update Qodana checkout ref handling and remove legacy PHPCS/PHPUnit config files; add Sonar config to
export-ignore.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
sonar-project.properties |
Adds SonarCloud project configuration (sources/tests/coverage paths). |
.github/workflows/sonarcloud.yml |
New workflow to run PHPUnit with coverage and execute Sonar scan. |
.github/workflows/ci.yml |
Skips PHPStan on older PHP, splits coverage run, uploads Clover to Codecov. |
.github/workflows/qodana.yml |
Fixes checkout ref to work for non-PR push events. |
.gitattributes |
Adds sonar-project.properties to export-ignore. |
phpcs.xml.dist |
Removes PHPCS ruleset file. |
phpunit10.xml.dist |
Removes legacy PHPUnit 10 config file. |
Comments suppressed due to low confidence (1)
phpcs.xml.dist:1
- This PR removes
phpcs.xml.dist, but the repo doesn’t appear to contain a replacementphpcs.xml. Without a PHPCS ruleset defining what to scan (e.g.,src/) and the custom exclusions/line-length settings,composer lint(phpcs) will likely fail with “You must supply at least one file or directory to process”, and style checks will change behavior. Either keepphpcs.xml.distor add the intendedphpcs.xmlreplacement in this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In @.github/workflows/ci.yml:
- Around line 85-87: The CI step named "Run tests with coverage" currently
invokes the global phpunit binary which can fail; update the step so it calls
the project-local PHPUnit instead (e.g. use vendor/bin/phpunit or run via
composer exec/defined composer script) while keeping the conditional if: ${{
matrix.coverage }} and the same arguments (--colors=always --coverage-clover
build/logs/clover.xml) to ensure the workflow uses the dev dependency bundled
with the repo.
In @.github/workflows/sonarcloud.yml:
- Around line 32-33: The workflow invokes phpunit via the PATH ("phpunit
--colors=always --coverage-clover build/logs/clover.xml"), which is unsafe;
change that command to call the project-local vendor binary
("./vendor/bin/phpunit --colors=always --coverage-clover build/logs/clover.xml")
so the workflow uses the correct phpunit binary, and make the same replacement
for the other identical phpunit invocation elsewhere in the workflows.
In `@sonar-project.properties`:
- Around line 12-13: The Sonar config currently excludes all test files via the
sonar.test.exclusions setting (sonar.test.exclusions=tests/**), which prevents
Sonar from indexing tests and collecting test metrics; update this setting to a
narrower pattern (e.g., exclude only fixtures or specific subfolders such as
tests/fixtures/** or tests/**/generated/**) or remove the sonar.test.exclusions
entry entirely so test files are indexed—modify the sonar.test.exclusions value
accordingly in the file where the symbol sonar.test.exclusions is defined.
- Use ./vendor/bin/phpunit instead of bare phpunit in ci.yml and sonarcloud.yml (bare phpunit not on PATH in GitHub Actions) - Remove sonar.test.exclusions=tests/** which prevented SonarCloud from indexing test files and collecting test metrics Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #133 +/- ##
=========================================
Coverage ? 35.18%
Complexity ? 835
=========================================
Files ? 38
Lines ? 2288
Branches ? 0
=========================================
Hits ? 805
Misses ? 1483
Partials ? 0 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
The phpcs.xml.dist was removed in this PR but the replacement phpcs.xml was not tracked. Without it, composer lint (phpcs) fails with no config. Changes from .dist version: line-length check fully excluded instead of using a high limit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @.github/workflows/ci.yml:
- Around line 77-79: The workflow step using the PHP gate currently does a
lexicographic comparison ("if: ${{ matrix.php >= '8.1' }}") which breaks for
versions like "8.10"; replace this with an explicit allowlist of exact version
equality checks instead of a string comparison. Update the condition on the "Run
static analysis" step to explicitly check matrix.php equals each supported
version (e.g., replace the single comparison with something like "if: ${{
matrix.php == '8.1' || matrix.php == '8.2' || matrix.php == '8.3' }}" or use a
contains/lookup against a hardcoded list of allowed versions) so future PHP
versions do not mis-order.
In @.github/workflows/sonarcloud.yml:
- Around line 35-38: The SonarCloud Scan step currently unconditionally uses
SONAR_TOKEN which causes failures for forked PRs that lack secrets; update the
SonarCloud Scan step (the job/stage named "SonarCloud Scan" that sets env
SONAR_TOKEN) to include an "if" conditional that only runs the step when the
token exists and when the PR is from the same repo (e.g., check
secrets.SONAR_TOKEN is non-empty and either the event is not a pull_request or
github.repository == github.event.pull_request.head.repo.full_name); this
ensures the step is skipped for forked PRs while still running for same-repo PRs
and non-PR runs.
- qodana.yaml: pin linter to jetbrains/qodana-php:2025.2 (was :latest) - sonarcloud.yml: pin all actions to full SHA hashes (security best practice) - sonarcloud.yml: only run on upstream repo (if: github.repository) - sonarcloud.yml: skip scan step gracefully if SONAR_TOKEN not set Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
phpcs.xml:13
- This change disables the line-length rule entirely via
<exclude-pattern>*</exclude-pattern>, which conflicts with the documented convention that the project uses a 200-character line limit (see.github/copilot-instructions.md:38). Either keep the rule enabled with a 200-char limit, or update the documentation/conventions to match the new behavior.
.github/workflows/sonarcloud.yml
Outdated
| run: ./vendor/bin/phpunit --colors=always --coverage-clover build/logs/clover.xml | ||
|
|
||
| - name: SonarCloud Scan | ||
| if: env.SONAR_TOKEN != '' |
Copilot
AI
Feb 8, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if: env.SONAR_TOKEN != '' is evaluated before the step-level env: is applied, so this condition will likely always be false and the scan step will be skipped. Use if: secrets.SONAR_TOKEN != '' (or move SONAR_TOKEN to job/workflow env) so the scan runs when the secret is available and still skips on forks.
| if: env.SONAR_TOKEN != '' | |
| if: secrets.SONAR_TOKEN != '' |
- Pin all action references to full commit SHA hashes (supply-chain security) - Fix PHPStan skip condition from lexicographic >= to explicit != exclusion - Fix SonarCloud token check (secrets context, not env context) - Add repository guard to Qodana workflow - Fix CHANGELOG: move Ulid overhaul from v1.2.32 to v1.2.33-beta2 (correct release), add missing PRs XOOPS#118/XOOPS#119/XOOPS#122, correct 11 release dates to match GitHub releases - Improve phpcs.xml: use severity=0 instead of exclude-pattern for line length Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @.github/workflows/sonarcloud.yml:
- Around line 36-40: The step "SonarCloud Scan" uses a step-level if: ${{
secrets.SONAR_TOKEN != '' }} which is invalid because the secrets context isn't
available in step if-expressions; fix by exposing the secret presence via an
environment variable or moving the condition to job-level: set an env like
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} at the job or workflow level (or add a
prior step that writes a step output/var indicating presence), then change the
step's if to check that env/var (e.g., if: env.SONAR_TOKEN != '' or if:
needs.<step>.outputs.sonar_present == 'true'), keeping the "SonarCloud Scan"
uses and SONAR_TOKEN environment mapping intact.
In `@CHANGELOG.md`:
- Around line 67-95: Add a single blank line immediately after each top-level
section heading to satisfy markdownlint MD022: insert one empty line below the
headings "### Changed", "### Removed", "### Infrastructure", and "### Tests" in
the changelog so each heading is followed by a blank line before the list
content.
| - name: SonarCloud Scan | ||
| if: ${{ secrets.SONAR_TOKEN != '' }} | ||
| uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9 # v7 | ||
| env: | ||
| SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix: secrets context is not available in step-level if conditions.
The secrets context cannot be used in step if expressions—only env, github, inputs, job, matrix, needs, runner, steps, strategy, and vars are available. This condition will cause a workflow syntax error.
Use an environment variable to expose the secret's presence, then check that variable:
🐛 Proposed fix using env variable
- name: SonarCloud Scan
- if: ${{ secrets.SONAR_TOKEN != '' }}
+ if: ${{ env.SONAR_TOKEN != '' }}
uses: SonarSource/sonarqube-scan-action@a31c9398be7ace6bbfaf30c0bd5d415f843d45e9 # v7
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}Alternatively, move the check to job level or use a dedicated step to set an output variable based on secret presence.
🧰 Tools
🪛 actionlint (1.7.10)
[error] 37-37: context "secrets" is not allowed here. available contexts are "env", "github", "inputs", "job", "matrix", "needs", "runner", "steps", "strategy", "vars". see https://docs.github.com/en/actions/learn-github-actions/contexts#context-availability for more details
(expression)
🤖 Prompt for AI Agents
In @.github/workflows/sonarcloud.yml around lines 36 - 40, The step "SonarCloud
Scan" uses a step-level if: ${{ secrets.SONAR_TOKEN != '' }} which is invalid
because the secrets context isn't available in step if-expressions; fix by
exposing the secret presence via an environment variable or moving the condition
to job-level: set an env like SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} at the job
or workflow level (or add a prior step that writes a step output/var indicating
presence), then change the step's if to check that env/var (e.g., if:
env.SONAR_TOKEN != '' or if: needs.<step>.outputs.sonar_present == 'true'),
keeping the "SonarCloud Scan" uses and SONAR_TOKEN environment mapping intact.
| ### Changed | ||
| * Use strict comparison (`===`) instead of loose (`==`) in `FilterInput` attribute filtering and `Database\Tables` column lookups | ||
| * Fix `FileStorageTest` namespace from `Xmf\Key` to `Xmf\Test\Key` to match autoload-dev configuration | ||
| * Fix trailing semicolons in `@throws` PHPDoc tags in `Jwt\JsonWebToken::create()` | ||
| * Update composer.json by @mambax7 in #119 | ||
| * Update composer, phpstan, phpcs by @mambax7 in #122 | ||
|
|
||
| ### Removed | ||
| * Remove redundant `paragonie/random_compat` dependency (native in PHP 7+) | ||
|
|
||
| ### Infrastructure | ||
| * Add PHPStan stub files for XOOPS framework classes to eliminate ~524 false-positive errors | ||
| * Configure `phpstan.neon` to scan stubs directory | ||
| * Move changelog to `CHANGELOG.md` at repo root; `docs/changelog.md` now redirects | ||
| * Simplify `.scrutinizer.yml` to analysis-only; move `stubs/` from `excluded_paths` to `dependency_paths` for constant/class resolution | ||
| * Add dedicated PHPStan, PHPCS, and code coverage jobs to GitHub Actions CI workflow | ||
| * Generate PHPStan baseline (`phpstan-baseline.neon`) with ~546 existing errors for incremental cleanup | ||
| * Add `composer baseline` script with backup/restore safety for PHPStan baseline regeneration | ||
| * Update `.gitignore` to exclude `build/` directory (coverage output) | ||
| * Update `.gitattributes` with export-ignore for PHPStan, PHPUnit, and stub files | ||
| * Add GitHub Copilot custom instructions (`.github/copilot-instructions.md`) and reusable XOOPS template | ||
|
|
||
| ### Tests | ||
| * Add unit tests for `Request::setVar()` ENV and SERVER branches | ||
| * Add unit tests for `FilterInput` hex and decimal entity decode | ||
| * Add unit test for `Metagen::html2text()` numeric entity conversion | ||
| * Add unit test for `IPAddress::normalize()` invalid IP handling | ||
| * Add unit tests for `Yaml::read()` and `Yaml::readWrapped()` file size limits | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor: Add blank lines below headings for markdown consistency.
Several section headings (### Changed, ### Removed, ### Infrastructure, ### Tests) are missing the blank line after them that markdownlint expects (MD022). While this doesn't affect rendering in most parsers, it improves consistency with the rest of the file.
📝 Example fix pattern
### Changed
+
* Use strict comparison (`===`) instead of loose (`==`)...Apply similar blank lines after ### Removed, ### Infrastructure, and ### Tests headings.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ### Changed | |
| * Use strict comparison (`===`) instead of loose (`==`) in `FilterInput` attribute filtering and `Database\Tables` column lookups | |
| * Fix `FileStorageTest` namespace from `Xmf\Key` to `Xmf\Test\Key` to match autoload-dev configuration | |
| * Fix trailing semicolons in `@throws` PHPDoc tags in `Jwt\JsonWebToken::create()` | |
| * Update composer.json by @mambax7 in #119 | |
| * Update composer, phpstan, phpcs by @mambax7 in #122 | |
| ### Removed | |
| * Remove redundant `paragonie/random_compat` dependency (native in PHP 7+) | |
| ### Infrastructure | |
| * Add PHPStan stub files for XOOPS framework classes to eliminate ~524 false-positive errors | |
| * Configure `phpstan.neon` to scan stubs directory | |
| * Move changelog to `CHANGELOG.md` at repo root; `docs/changelog.md` now redirects | |
| * Simplify `.scrutinizer.yml` to analysis-only; move `stubs/` from `excluded_paths` to `dependency_paths` for constant/class resolution | |
| * Add dedicated PHPStan, PHPCS, and code coverage jobs to GitHub Actions CI workflow | |
| * Generate PHPStan baseline (`phpstan-baseline.neon`) with ~546 existing errors for incremental cleanup | |
| * Add `composer baseline` script with backup/restore safety for PHPStan baseline regeneration | |
| * Update `.gitignore` to exclude `build/` directory (coverage output) | |
| * Update `.gitattributes` with export-ignore for PHPStan, PHPUnit, and stub files | |
| * Add GitHub Copilot custom instructions (`.github/copilot-instructions.md`) and reusable XOOPS template | |
| ### Tests | |
| * Add unit tests for `Request::setVar()` ENV and SERVER branches | |
| * Add unit tests for `FilterInput` hex and decimal entity decode | |
| * Add unit test for `Metagen::html2text()` numeric entity conversion | |
| * Add unit test for `IPAddress::normalize()` invalid IP handling | |
| * Add unit tests for `Yaml::read()` and `Yaml::readWrapped()` file size limits | |
| ### Changed | |
| * Use strict comparison (`===`) instead of loose (`==`) in `FilterInput` attribute filtering and `Database\Tables` column lookups | |
| * Fix `FileStorageTest` namespace from `Xmf\Key` to `Xmf\Test\Key` to match autoload-dev configuration | |
| * Fix trailing semicolons in `@throws` PHPDoc tags in `Jwt\JsonWebToken::create()` | |
| * Update composer.json by `@mambax7` in `#119` | |
| * Update composer, phpstan, phpcs by `@mambax7` in `#122` | |
| ### Removed | |
| * Remove redundant `paragonie/random_compat` dependency (native in PHP 7+) | |
| ### Infrastructure | |
| * Add PHPStan stub files for XOOPS framework classes to eliminate ~524 false-positive errors | |
| * Configure `phpstan.neon` to scan stubs directory | |
| * Move changelog to `CHANGELOG.md` at repo root; `docs/changelog.md` now redirects | |
| * Simplify `.scrutinizer.yml` to analysis-only; move `stubs/` from `excluded_paths` to `dependency_paths` for constant/class resolution | |
| * Add dedicated PHPStan, PHPCS, and code coverage jobs to GitHub Actions CI workflow | |
| * Generate PHPStan baseline (`phpstan-baseline.neon`) with ~546 existing errors for incremental cleanup | |
| * Add `composer baseline` script with backup/restore safety for PHPStan baseline regeneration | |
| * Update `.gitignore` to exclude `build/` directory (coverage output) | |
| * Update `.gitattributes` with export-ignore for PHPStan, PHPUnit, and stub files | |
| * Add GitHub Copilot custom instructions (`.github/copilot-instructions.md`) and reusable XOOPS template | |
| ### Tests | |
| * Add unit tests for `Request::setVar()` ENV and SERVER branches | |
| * Add unit tests for `FilterInput` hex and decimal entity decode | |
| * Add unit test for `Metagen::html2text()` numeric entity conversion | |
| * Add unit test for `IPAddress::normalize()` invalid IP handling | |
| * Add unit tests for `Yaml::read()` and `Yaml::readWrapped()` file size limits |
🧰 Tools
🪛 LanguageTool
[uncategorized] ~87-~87: The official name of this software platform is spelled with a capital “H”.
Context: ...Add GitHub Copilot custom instructions (.github/copilot-instructions.md) and reusable ...
(GITHUB)
🪛 markdownlint-cli2 (0.20.0)
[warning] 67-67: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 74-74: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 77-77: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
[warning] 89-89: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
🤖 Prompt for AI Agents
In `@CHANGELOG.md` around lines 67 - 95, Add a single blank line immediately after
each top-level section heading to satisfy markdownlint MD022: insert one empty
line below the headings "### Changed", "### Removed", "### Infrastructure", and
"### Tests" in the changelog so each heading is followed by a blank line before
the list content.



Summary
message/pathsingular,identifier,countfields) which PHPStan 1.x (installed on PHP <8.1) does not understand. Static analysis now only runs on PHP ≥8.1 where PHPStan 2.x is available. Tests and lint still run on all PHP versions (7.4–8.5).sonar-project.properties(org=xoops, projectKey=XOOPS_xmf) and.github/workflows/sonarcloud.ymlworkflow. Runs tests with coverage then SonarCloud scan on push to master and PRs. RequiresSONAR_TOKENsecret.clover.xmlonly on the coverage matrix row.refnow falls back togithub.shaon push events (was failing on non-PR pushes).phpcs.xml.dist(superseded byphpcs.xml) andphpunit10.xml.dist(consolidated intophpunit.xml.dist).sonar-project.propertiesto.gitattributesexport-ignore.Test plan
SONAR_TOKENsecret)🤖 Generated with Claude Code
Summary by CodeRabbit
Chores
Tests
Documentation