fix(ci): composer phpmd was a no-op — CI reported success while printing violations - #99
Merged
Conversation
…ing violations
`composer phpmd` ended in
|| echo 'PHPMD reported violations beyond the baseline — ... NOT yet a hard gate.'
so it always exited 0. That was not only a local convenience: the shared
workflow ConductionNL/.github/.github/workflows/quality.yml invokes
`composer phpmd`, so the "PHP Quality (phpmd)" job reported SUCCESS while
printing the violations — run 30760172028 (2026-08-02) is green with dozens
of CyclomaticComplexity findings in its log.
Measured first, in nextcloud:32-apache (PHP 8.3.32, matches the php-version
8.3 CI pins): phpcs 0/exit 0, psalm 0/exit 0, phpstan 0/exit 0, phpmd 76
findings/exit 2 (82 with no baseline). `composer check:strict` exited 0.
- drop the `|| echo` so phpmd is a real gate, matching phpcs/psalm/phpstan
- regenerate phpmd.baseline.xml (174 entries, 48 files) with the reason for
every family recorded in the file; burn-down tracked in #98
- correct the stale justification comment in the forgejo pre-merge workflow,
which claimed psalm/phpstan/phpmd were all `|| echo`-wrapped
test:all is deliberately left as-is. Measured anyway: the suite passes
standalone (754 tests, 2277 assertions) and exits 1 only on a "no code
coverage driver" warning — so its "requires Nextcloud" story is wrong and
it is a candidate for removal separately.
Refs #97, #98
Contributor
Quality Report — ConductionNL/openbuild @
|
| Check | PHP | Vue | Security | License | Tests |
|---|---|---|---|---|---|
| lint | ✅ | ||||
| phpcs | ✅ | ||||
| phpmd | ✅ | ||||
| psalm | ✅ | ||||
| phpstan | ✅ | ||||
| phpmetrics | ✅ | ||||
| eslint | ✅ | ||||
| stylelint | ✅ | ||||
| composer | ✅ | ✅ 100/100 | |||
| npm | ✅ | ✅ 662/662 | |||
| PHPUnit | ✅ | ||||
| Newman | ⏭️ | ||||
| Playwright | ⏭️ |
Coverage: 89.4% (17/19 statements)
Quality workflow — 2026-08-02 19:39 UTC
Download the full PDF report from the workflow artifacts.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Turns
composer phpmdfrom a no-op into a real gate, and records — in the baseline file itself — why every entry is there.Analysis-path verification (before believing any number)
nextcloud:32-apache(PHP 8.3.32, imagick present), matching thephp-version: "8.3"this repo's CI pins. Host PHP is 8.2 andnextcloud:34is PHP 8.5, where Psalm 5.26.1 crashes.vendor/was produced by a realcomposer installfromcomposer.lockin a fresh worktree (11s). No sibling repo's vendor tree was borrowed, so the absolute counts below are real, not approximate.vendor/nextcloud/ocp/OCPis a real directory with 132 entries, not a dangling symlink to/var/www/html/lib/public, and there is noOCP.bak/. So nounknown class OCP\*cascade is inflating the phpstan number.phpcs.xml→lib,phpstan.neon→lib,psalm.xml→lib,phpmd→lib;lib/holds 129 PHP files. No zero here is a zero-because-nothing-was-scanned.Does CI actually run this gate? Yes — and that is the point
.github/workflows/code-quality.ymlcalls the sharedConductionNL/.github/.github/workflows/quality.yml@main, whose PHP-quality matrix runscomposer phpmd,composer psalm,composer phpstan,composer phpcs. So the|| echodid not just weaken a local convenience — it made a CI job lie.Proof, on
developmenttoday: run 30760172028 (2026-08-02), jobquality / PHP Quality (phpmd)→success, while its log prints violations such as(
.forgejo/workflows/pre-merge-check-strict.yamlruns onlycomposer lint+composer phpcs; unchanged in behaviour here, but its justification comment was factually stale and is corrected.)Pre-repair measurements
composer check:strictphpmd top rules (as the script invokes it, with
--exclude+ old baseline): CyclomaticComplexity 16, ElseExpression 13, NPathComplexity 11, CouplingBetweenObjects 8, ExcessiveClassComplexity 7, TooManyMethods 3, ShortVariable 3, ExcessiveMethodLength 3, ExcessiveClassLength 3, LongVariable 2, ExcessiveParameterList 2, BooleanArgumentFlag 2, UnusedFormalParameter 1, StaticAccess 1, LongClassName 1.composer.json shape chosen: (b) plain
./vendor/bin/XNot
if [ -f vendor/bin/X ]. Three reasons:phpcs,psalmandphpstanin this repo are already plain. phpmd was the only outlier.composer installfirst, so "the tool is genuinely absent" is not a state we need to tolerate.if [ -f ]guard re-creates the failure mode we are removing: a missing binary would silently skip instead of failing.Baselines and their reasons
phpmd.baseline.xmlregenerated: 174 entries across 48 files. The reasoning is written into the file as an XML comment, in short:CyclomaticComplexity42,NpathComplexity26,WeightedMethodCount23,ElseExpression16,CouplingBetweenObjects15,LongMethod11,LongClass7,BooleanArgumentFlag7,LongVariable5,ShortVariable4,TooManyMethods4,StaticAccess4,UnusedFormalParameter3,LongParameterList3,MissingImport2,LongClassName1,TooManyPublicMethods1). None of these rules asserts incorrect behaviour — they assert that a method is long or branchy.No entry was added that resembles a real defect.
test:all— measured, deliberately not flippedLeft ending
|| echo 'Tests require Nextcloud environment, skipping...'per the fleet-wide decision. But the story is wrong and worth recording: the suite passes standalone —Tests: 754, Assertions: 2277— and PHPUnit exits 1 only because ofNo code coverage driver available. So this|| echois masking a coverage-driver warning, not a Nextcloud dependency, and is a candidate for removal once a driver is available on the runner. Not done here because it is a separate, measurable change.Vacuous-green control
Before opening this PR I dropped a deliberately broken
lib/ProbeGateLive.phpinto the tree — an undefined method call, an unused local variable, and non-conforming formatting — and confirmed the repaired analysers fail on it, including that the freshly generated baseline does not mask it (it cannot: the baseline is keyed on file+rule and the probe file is new).UndefinedMethod)The probe was then deleted and
git statusconfirmed only the three intended files modified, with no untracked leftovers.The control also found something. The first probe used
\OCP\IRequestand psalm did not catch it — psalm never resolves the OCP stubs, andpsalm.xmlsuppressesUndefinedClassfor ~60OCP\*names, so method calls on OCP types are unanalysable. It also suppressesInvalidReturnType,InvalidReturnStatement,InvalidArgument,InvalidCast,InvalidMethodCall,InvalidArrayOffsetand more. Re-running with those handlers restored yields 17 findings. So "psalm 0/exit 0" above is honest about what the current config reports, but that config is narrower than it looks. Filed as #97 with the triage of all 17 (none is a live defect; two are docblock drift, one is a psalm false positive, one is worth a caller check). Not changed here, because widening psalm changes what the gate reports and deserves its own review.Real defects found
None in this repo. Specifically checked for the openregister-style PHP 8 named-argument fatals:
lib/contains no class-level@methoddocblocks, so there is no stale annotation for phpstan/psalm to resolve named arguments against, and phpstan (level 5) resolves the named-argument call sites — e.g.ApplicationCreationService'sregister:/schema:/uuid:calls — against real declarations with 0 errors.Not done / not verified
test:allnot flipped.phpmetrics,phpqa,quality:*scripts untouched — out of scope.Recent merges on this repo land on GitHub (squash style,
(#NN)); Codeberg is a mirror. This PR targetsdevelopment, the default branch.