Skip to content

Conversation

@matthewdeaves
Copy link
Owner

No description provided.

matthewdeaves and others added 4 commits January 12, 2026 17:16
- Add phploc and phpmd composer dependencies for code metrics
- Create phpmd.xml configuration for complexity analysis
- Create .jscpd.json for PHP/JS/CSS duplication detection
- Add CI jobs for metrics generation:
  - metrics-coverage: PHPUnit with clover XML and HTML reports
  - metrics-static-analysis: PHPStan and PHPCS JSON output
  - metrics-security: phpcs-security-audit JSON output
  - metrics-complexity: PHPLOC and PHPMD JSON output
  - metrics-duplication: jscpd for PHP, JavaScript, and CSS
- Create metrics.yml workflow to process and deploy dashboard
- Add Python script to parse all metrics and generate:
  - Interactive HTML dashboard with Chart.js trends
  - SVG badges for each metric category
  - JSON API endpoint for programmatic access
  - Historical data with 90-day retention
- Add test fixtures for local script development
- Add code quality badges to README linking to dashboard

Dashboard will be available at:
https://matthewdeaves.github.io/willow/metrics/

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove phploc from composer.json (conflicts with phpunit via sebastian/version)
- Replace phploc with PHPMD for complexity metrics in all workflows
- Use defusedxml for secure XML parsing (fixes Semgrep security warning)
- Update dashboard template to show PHPMD violations instead of phploc metrics
- Update Python script to use PHPMD data throughout
- Add PHPMD and Duplication badges to README
- Remove unused phploc fixture file

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
jscpd returns exit code 1 when duplicates are found, which is
expected behavior. Add || true to allow the workflow to continue
and upload the report regardless of the exit code.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
CakePHP 5.2.11 (released Jan 9, 2026) has a bug with PHP 8.1 that
causes "Cannot use 'Cake\ORM\Behavior\true' as class name" error
in TreeBehavior. Pin to <5.2.11 only for PHP 8.1 until fixed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comment on lines +110 to +166
metrics-coverage:
runs-on: ubuntu-latest
name: Metrics - Coverage
needs: test

steps:
- uses: actions/checkout@v6

- name: Setup MySQL
run: |
sudo service mysql start
mysql -e 'CREATE DATABASE IF NOT EXISTS cms_test;' -uroot -proot
- name: Setup Redis
run: |
sudo apt-get update
sudo apt-get install -y redis-server
sudo service redis-server start
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, intl, pdo_mysql, pcntl, sockets, bcmath, zip
coverage: xdebug

- name: Install Composer dependencies
run: composer update --no-interaction --prefer-dist

- name: Copy Configs
run: |
cp docker/github/app_local.php config/app_local.php
cp docker/github/app.php config/app.php
- name: Run PHPUnit with Coverage
run: |
php -d display_errors=on vendor/bin/phpunit \
--coverage-clover coverage.xml \
--coverage-html coverage-html/ \
--coverage-text
env:
XDEBUG_MODE: coverage
REDIS_HOST: 127.0.0.1
REDIS_PORT: 6379
REDIS_PASSWORD: ""
REDIS_URL: "redis://127.0.0.1:6379/0"

- name: Upload Coverage Artifacts
uses: actions/upload-artifact@v4
with:
name: phpunit-coverage
path: |
coverage.xml
coverage-html/
retention-days: 30

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}
Comment on lines +166 to +207
metrics-static-analysis:
runs-on: ubuntu-latest
name: Metrics - Static Analysis

steps:
- uses: actions/checkout@v6

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, intl

- name: Install Composer dependencies
run: composer update --no-interaction --prefer-dist

- name: Run PHPStan (JSON output)
run: |
php -d memory_limit=-1 vendor/bin/phpstan analyse src/ \
--error-format=json > phpstan.json || true
- name: Run PHP CodeSniffer (JSON output)
run: |
vendor/bin/phpcs \
--standard=vendor/cakephp/cakephp-codesniffer/CakePHP \
--report=json src/ tests/ > phpcs.json || true
- name: Upload PHPStan Results
uses: actions/upload-artifact@v4
with:
name: phpstan-results
path: phpstan.json
retention-days: 30

- name: Upload PHPCS Results
uses: actions/upload-artifact@v4
with:
name: phpcs-results
path: phpcs.json
retention-days: 30

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 6 days ago

In general, the fix is to add an explicit permissions block that scopes down the GITHUB_TOKEN to only what the workflow needs. In this workflow, all shown jobs (security, metrics-coverage, metrics-static-analysis, metrics-security, metrics-duplication, etc.) only need to read repository contents (for actions/checkout) and upload artifacts, which does not require any special write scopes to the repository. Therefore, the best minimal fix is to add a single root-level permissions block with contents: read, which applies to all jobs that do not override it.

Concretely, in .github/workflows/ci.yml, insert:

permissions:
  contents: read

near the top of the file, after the name: CI line and before the on: trigger block. This will ensure that every job, including the one beginning at line 167, runs with a read-only GITHUB_TOKEN for repository contents. No additional imports or methods are required; this is purely a YAML workflow configuration change and does not alter the functional behavior of the jobs, since none of them currently rely on write permissions.

Suggested changeset 1
.github/workflows/ci.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,5 +1,8 @@
 name: CI
 
+permissions:
+  contents: read
+
 on:
   push:
     branches:
EOF
@@ -1,5 +1,8 @@
name: CI

permissions:
contents: read

on:
push:
branches:
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +207 to +235
metrics-security:
runs-on: ubuntu-latest
name: Metrics - Security

steps:
- uses: actions/checkout@v6

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, intl

- name: Install Composer dependencies
run: composer update --no-interaction --prefer-dist

- name: Run Security Audit (JSON output)
run: |
vendor/bin/phpcs --standard=phpcs-security.xml \
--report=json src/ > security-phpcs.json || true
- name: Upload Security Results
uses: actions/upload-artifact@v4
with:
name: security-results
path: security-phpcs.json
retention-days: 30

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 6 days ago

In general, the fix is to explicitly define a minimal permissions: block so the GITHUB_TOKEN has only the scopes required. For this workflow, all shown jobs are CI/metrics jobs that only need to read the repository (via actions/checkout) and upload artifacts; they do not need to write to repository contents, issues, or pull requests. Therefore, setting permissions: contents: read at the top level of the workflow is sufficient and avoids repeating the same block in each job.

The single best change with minimal functional impact is to add a root-level permissions: section just under the name: CI line (and before on:). This will apply to all jobs in the workflow, including metrics-security, metrics-static-analysis, metrics-complexity, and metrics-duplication, unless any job overrides it with its own permissions: block. No other code or steps need to change, and no new imports or actions are required.

Concretely:

  • Edit .github/workflows/ci.yml.
  • Insert:
permissions:
  contents: read

immediately after the existing name: CI line at the top of the file. This ensures CodeQL sees an explicit permission limitation and that the GITHUB_TOKEN is restricted to read-only contents access for the entire workflow.

Suggested changeset 1
.github/workflows/ci.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,5 +1,8 @@
 name: CI
 
+permissions:
+  contents: read
+
 on:
   push:
     branches:
EOF
@@ -1,5 +1,8 @@
name: CI

permissions:
contents: read

on:
push:
branches:
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +235 to +261
metrics-complexity:
runs-on: ubuntu-latest
name: Metrics - Complexity

steps:
- uses: actions/checkout@v6

- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
extensions: mbstring, intl

- name: Install Composer dependencies
run: composer update --no-interaction --prefer-dist

- name: Run PHPMD (JSON output)
run: vendor/bin/phpmd src/ json phpmd.xml > phpmd.json || true

- name: Upload Complexity Results
uses: actions/upload-artifact@v4
with:
name: complexity-results
path: phpmd.json
retention-days: 30

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 6 days ago

In general, fix this by explicitly declaring minimal GITHUB_TOKEN permissions either at the workflow root (affecting all jobs without their own permissions) or at the specific job level. For these metrics jobs, they only need to read the repository contents and upload artifacts, which does not require any extra GitHub write scopes, so contents: read is sufficient.

The single best, non-invasive fix is to add a permissions block at the top level of .github/workflows/ci.yml, just under the name: CI line and before on:, setting contents: read. This will apply to all jobs (including metrics-complexity) that do not override it, tightening GITHUB_TOKEN while preserving existing functionality. No additional imports or dependencies are needed; we are only changing workflow configuration.

Concretely:

  • Edit .github/workflows/ci.yml.
  • After line 1: name: CI, insert:
    permissions:
      contents: read
  • Leave all job definitions and steps unchanged.
Suggested changeset 1
.github/workflows/ci.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,4 +1,6 @@
 name: CI
+permissions:
+  contents: read
 
 on:
   push:
EOF
@@ -1,4 +1,6 @@
name: CI
permissions:
contents: read

on:
push:
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +261 to +282
metrics-duplication:
runs-on: ubuntu-latest
name: Metrics - Duplication

steps:
- uses: actions/checkout@v6

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Run jscpd
run: |
npx jscpd src/ plugins/ webroot/ --reporters json --output jscpd-report/ || true
- name: Upload Duplication Results
uses: actions/upload-artifact@v4
with:
name: duplication-results
path: jscpd-report/

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI 6 days ago

In general, the fix is to explicitly specify minimal GITHUB_TOKEN permissions for the workflow or for the specific job. Since the metrics jobs only need to read the repository contents and upload artifacts, the minimal safe permission is contents: read. This prevents the job from inheriting potentially broader repo/org defaults.

The best targeted fix here is to add a permissions block to the metrics-duplication job (the one CodeQL flags at line 262). That job uses actions/checkout, actions/setup-node, runs npx jscpd, and uploads artifacts; none of these require write access to the repo. We can therefore insert:

permissions:
  contents: read

directly under the job name (e.g., after metrics-duplication: and before runs-on:). This change is localized to the shown snippet, does not alter functionality, and satisfies the least-privilege recommendation. No imports or additional methods are necessary because this is pure workflow-configuration.

Suggested changeset 1
.github/workflows/ci.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -259,6 +259,8 @@
         retention-days: 30
 
   metrics-duplication:
+    permissions:
+      contents: read
     runs-on: ubuntu-latest
     name: Metrics - Duplication
 
EOF
@@ -259,6 +259,8 @@
retention-days: 30

metrics-duplication:
permissions:
contents: read
runs-on: ubuntu-latest
name: Metrics - Duplication

Copilot is powered by AI and may make mistakes. Always verify output.
matthewdeaves and others added 2 commits January 12, 2026 18:50
The defusedxml import is already in place with fallback. Adding
nosemgrep comment to suppress the false positive since ET could
be defusedxml.ElementTree when the package is installed.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Since the metrics workflow always installs defusedxml, use it
directly instead of a try/except fallback. This is the proper
fix for the Semgrep XML security warning.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@matthewdeaves matthewdeaves merged commit b4687f8 into main Jan 12, 2026
23 checks passed
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