diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..1d8cba4
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,15 @@
+{
+ "env": {
+ "browser": true,
+ "es2021": true
+ },
+ "extends": "eslint:recommended",
+ "parserOptions": {
+ "ecmaVersion": 12,
+ "sourceType": "module"
+ },
+ "rules": {
+ "no-unused-vars": "warn",
+ "no-console": "off"
+ }
+}
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..abb8862
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,60 @@
+name: CI
+
+on:
+ push:
+ branches: [ "main", "dev" ]
+
+permissions:
+ contents: read
+ security-events: write
+
+jobs:
+ build-test-scan:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.2'
+ tools: composer, phpstan, phpunit, phpcs
+
+ - name: Cache Composer
+ uses: actions/cache@v4
+ with:
+ path: ~/.composer/cache
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Install PHP dependencies
+ run: composer install --no-interaction --prefer-dist
+
+ - name: Run PHPStan (static analysis)
+ run: vendor/bin/phpstan analyse --memory-limit=1G
+
+ - name: Run PHPUnit (unit tests)
+ run: vendor/bin/phpunit --coverage-clover coverage.xml
+
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v4
+ with:
+ files: coverage.xml
+ fail_ci_if_error: true
+
+ - name: Run Trivy FS scan
+ uses: aquasecurity/trivy-action@master
+ with:
+ scan-type: 'fs'
+ ignore-unfixed: true
+ severity: 'HIGH,CRITICAL'
+
+ - name: Build Docker image
+ run: docker build -t myapp:ci .
+
+ - name: Run Trivy image scan
+ uses: aquasecurity/trivy-action@master
+ with:
+ image-ref: 'myapp:ci'
+ severity: 'HIGH,CRITICAL'
diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml
new file mode 100644
index 0000000..fa63722
--- /dev/null
+++ b/.github/workflows/codeql.yml
@@ -0,0 +1,47 @@
+name: "CodeQL"
+
+on:
+ push:
+ branches: [ "main", "dev" ]
+ pull_request:
+ branches: [ "main", "dev" ]
+ schedule:
+ - cron: '0 0 * * 0' # weekly scan
+
+permissions:
+ contents: read
+ security-events: write
+
+jobs:
+ analyze:
+ name: Analyze PHP and JavaScript
+ runs-on: ubuntu-latest
+ permissions:
+ actions: read
+ contents: read
+ security-events: write
+
+ strategy:
+ fail-fast: false
+ matrix:
+ language: [ 'php', 'javascript' ]
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Initialize CodeQL
+ uses: github/codeql-action/init@v3
+ with:
+ languages: ${{ matrix.language }}
+
+ - name: Autobuild
+ uses: github/codeql-action/autobuild@v3
+
+ # If autobuild fails for PHP, uncomment and adjust:
+ # - run: composer install --no-interaction --prefer-dist
+
+ - name: Perform CodeQL Analysis
+ uses: github/codeql-action/analyze@v3
+ with:
+ category: "/language:${{ matrix.language }}"
diff --git a/.github/workflows/mega-linter.yml b/.github/workflows/mega-linter.yml
deleted file mode 100644
index 6067802..0000000
--- a/.github/workflows/mega-linter.yml
+++ /dev/null
@@ -1,85 +0,0 @@
-name: MegaLinter
-
-on:
- push:
- branches: [ "main", "dev" ]
- pull_request:
-
-permissions:
- contents: write # needed to commit to a fixes branch
- pull-requests: write # needed to open PRs with fixes
- security-events: write # for SARIF upload (optional)
-
-jobs:
- lint:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- # Cache Composer (PHP)
- - name: Cache Composer
- uses: actions/cache@v4
- with:
- path: |
- ~/.composer/cache
- vendor
- key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
- restore-keys: ${{ runner.os }}-composer-
-
- # Cache npm (JS/CSS linters)
- - name: Cache npm
- uses: actions/cache@v4
- with:
- path: |
- ~/.npm
- node_modules
- key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
- restore-keys: ${{ runner.os }}-npm-
-
- # Install dev deps if present (WPCS, ESLint plugins, etc.)
- - name: Composer install (no scripts)
- run: |
- if [ -f composer.json ]; then
- composer install --no-interaction --no-progress --no-scripts || true
- fi
-
- - name: npm install if present
- run: |
- if [ -f package.json ]; then
- npm ci || npm i
- fi
-
- - name: Run MegaLinter
- uses: oxsecurity/megalinter@v8
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- # Only lint changed files
- VALIDATE_ALL_CODEBASE: false
- # Enable just these linters
- ENABLE_LINTERS: >-
- PHP_PHPCS,
- PHP_PHPLINT,
- JAVASCRIPT_ES,
- CSS_STYLELINT,
- HTML_HTMLHINT
- # Limit JS/CSS/HTML to update-api/ only
- JAVASCRIPT_ES_FILTER_REGEX_INCLUDE: "^update-api/"
- CSS_STYLELINT_FILTER_REGEX_INCLUDE: "^update-api/"
- HTML_HTMLHINT_FILTER_REGEX_INCLUDE: "^update-api/"
- # PHPCS ruleset file assigns WordPress vs PSR-12 per directory
- PHP_PHPCS_CONFIG_FILE: "phpcs.xml"
- # Auto-fix only changed files and open a PR with fixes
- APPLY_FIXES: all
- APPLY_FIXES_EVENT: pull_request
- APPLY_FIXES_MODE: pull_request
- # Reports (optional)
- REPORT_OUTPUT_FOLDER: megalinter-reports
-
- - name: Upload SARIF (optional)
- if: always()
- uses: github/codeql-action/upload-sarif@v3
- with:
- sarif_file: megalinter-reports/megalinter-report.sarif
diff --git a/.github/workflows/pr-auto-fix.yml b/.github/workflows/pr-auto-fix.yml
new file mode 100644
index 0000000..8b2ee5a
--- /dev/null
+++ b/.github/workflows/pr-auto-fix.yml
@@ -0,0 +1,70 @@
+name: PR Lint and Auto-Fix
+
+on:
+ pull_request:
+
+permissions:
+ contents: write
+ pull-requests: write
+
+jobs:
+ lint-fix:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Setup PHP
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.2'
+ tools: composer, php-cs-fixer, phpcs
+
+ - name: Cache Composer
+ uses: actions/cache@v4
+ with:
+ path: ~/.composer/cache
+ key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
+ restore-keys: ${{ runner.os }}-composer-
+
+ - name: Install PHP dependencies
+ run: composer install --no-interaction --prefer-dist || true
+
+ - name: Cache npm
+ uses: actions/cache@v4
+ with:
+ path: |
+ ~/.npm
+ node_modules
+ key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }}
+ restore-keys: ${{ runner.os }}-npm-
+
+ - name: Install npm dependencies
+ run: if [ -f package.json ]; then npm ci || npm i; fi
+
+ # PHPCS WordPress for mu-plugins/
+ - name: Fix PHP (WordPress standard in mu-plugins/)
+ run: vendor/bin/phpcbf --standard=WordPress mu-plugins || true
+
+ # PHPCS PSR-12 for update-api/
+ - name: Fix PHP (PSR-12 standard in update-api/)
+ run: vendor/bin/phpcbf --standard=PSR12 update-api || true
+
+ - name: PHP-CS-Fixer
+ run: vendor/bin/php-cs-fixer fix --allow-risky=yes || true
+
+ - name: ESLint Fix
+ run: npx eslint update-api/**/*.js --fix || true
+
+ - name: Stylelint Fix
+ run: npx stylelint "update-api/**/*.css" --fix || true
+
+ - name: Commit fixes to PR branch
+ uses: peter-evans/create-pull-request@v6
+ with:
+ commit-message: "chore: auto-fix lint issues"
+ branch: lint-fixes
+ title: "Lint Auto-Fixes"
+ body: "Automated lint fixes from PR workflow"
diff --git a/.mega-linter.yml b/.mega-linter.yml
deleted file mode 100644
index 1e470bb..0000000
--- a/.mega-linter.yml
+++ /dev/null
@@ -1,23 +0,0 @@
-# MegaLinter configuration
-# Lints only changed files and only selected linters.
-VALIDATE_ALL_CODEBASE: false
-ENABLE_LINTERS: >
- PHP_PHPCS,
- PHP_PHPLINT,
- JAVASCRIPT_ES,
- CSS_STYLELINT,
- HTML_HTMLHINT
-
-# Restrict web linters to update-api/
-JAVASCRIPT_ES_FILTER_REGEX_INCLUDE: "^update-api/"
-CSS_STYLELINT_FILTER_REGEX_INCLUDE: "^update-api/"
-HTML_HTMLHINT_FILTER_REGEX_INCLUDE: "^update-api/"
-
-# Use project ruleset to target different PHP standards per directory
-PHP_PHPCS_CONFIG_FILE: "phpcs.xml"
-
-# Auto-fix behavior controlled from workflow env, can be overridden here if needed
-# APPLY_FIXES: all
-# APPLY_FIXES_EVENT: pull_request
-# APPLY_FIXES_MODE: pull_request
-# REPORT_OUTPUT_FOLDER: megalinter-reports
diff --git a/.stylelintrc.json b/.stylelintrc.json
new file mode 100644
index 0000000..11fceb0
--- /dev/null
+++ b/.stylelintrc.json
@@ -0,0 +1,7 @@
+{
+ "extends": "stylelint-config-standard",
+ "rules": {
+ "indentation": 2,
+ "color-hex-length": "short"
+ }
+}
diff --git a/phpcs.xml b/phpcs.xml
index c50b2c1..2efcd2c 100644
--- a/phpcs.xml
+++ b/phpcs.xml
@@ -4,16 +4,13 @@
-
mu-plugins
update-api
-
^mu-plugins/.*
-
^update-api/.*
diff --git a/phpstan.neon b/phpstan.neon
new file mode 100644
index 0000000..729e58f
--- /dev/null
+++ b/phpstan.neon
@@ -0,0 +1,6 @@
+parameters:
+ level: 6
+ paths:
+ - mu-plugins
+ - update-api
+ memoryLimit: 1G
diff --git a/phpunit.xml b/phpunit.xml
new file mode 100644
index 0000000..550968e
--- /dev/null
+++ b/phpunit.xml
@@ -0,0 +1,11 @@
+
+
+
+
+ tests
+
+
+