Skip to content

Robustness pass: engine retry, JSON parsing, concurrency limits, stal… #55

Robustness pass: engine retry, JSON parsing, concurrency limits, stal…

Robustness pass: engine retry, JSON parsing, concurrency limits, stal… #55

Workflow file for this run

name: CI
on:
push:
branches: ["**"]
tags-ignore: ["**"] # tags are handled by release.yml
pull_request:
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
# ─────────────────────────────────────────────────────────────────────────────
# Lint — JS, CSS, PHP syntax, PHPCS, PHPStan
# ─────────────────────────────────────────────────────────────────────────────
lint:
name: Lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5.0.1
- name: Set up Node.js
uses: actions/setup-node@v5.0.0
with:
node-version: "lts/*"
cache: "npm"
- name: Set up PHP
uses: shivammathur/setup-php@v2
with:
php-version: "8.2"
tools: cs2pr
coverage: none
- name: Install JS dependencies
run: npm ci --legacy-peer-deps
- name: Install PHP dependencies
run: composer install --no-interaction --prefer-dist
- name: Lint — JavaScript
run: npm run lint:js
- name: Lint — CSS/SCSS
run: npm run lint:css
- name: Lint — PHP syntax
run: npm run lint:php:syntax
- name: Lint — PHP CodeSniffer
run: composer run phpcs -- -q -n --report=checkstyle | cs2pr
- name: Analyze — PHPStan
run: composer run phpstan -- --memory-limit=1G --error-format=github
- name: Validate block HTML
run: npm run test:php:validate
# ─────────────────────────────────────────────────────────────────────────────
# Test — JS unit tests
# ─────────────────────────────────────────────────────────────────────────────
test-js:
name: JS Tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5.0.1
- name: Set up Node.js
uses: actions/setup-node@v5.0.0
with:
node-version: "lts/*"
cache: "npm"
- name: Install dependencies
run: npm ci --legacy-peer-deps
- name: Run Jest
run: npm run test:js -- --coverage
# ─────────────────────────────────────────────────────────────────────────────
# Test — PHP unit tests (no WP install required, uses stubs bootstrap)
# ─────────────────────────────────────────────────────────────────────────────
test-php-unit:
name: PHP Unit (PHP ${{ matrix.php }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php: ["8.2", "8.3"]
steps:
- name: Checkout
uses: actions/checkout@v5.0.1
- name: Set up PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
coverage: none
- name: Install PHP dependencies
run: composer install --no-interaction --prefer-dist
- name: Run PHPUnit (unit suite)
run: vendor/bin/phpunit --testsuite unit
# ─────────────────────────────────────────────────────────────────────────────
# Test — PHP integration tests (requires WordPress test suite)
# Matrix: PHP 8.0 / WP 6.4 and PHP 8.3 / WP latest
# ─────────────────────────────────────────────────────────────────────────────
test-php-integration:
name: PHP Integration (PHP ${{ matrix.php }} / WP ${{ matrix.wp }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- php: "8.2"
wp: "6.4"
- php: "8.2"
wp: "6.7"
- php: "8.3"
wp: "latest"
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wp_tests
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping"
--health-interval=10s
--health-timeout=5s
--health-retries=5
steps:
- name: Checkout
uses: actions/checkout@v5.0.1
- name: Set up PHP ${{ matrix.php }}
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
extensions: mysqli
coverage: none
- name: Install PHP dependencies
run: composer install --no-interaction --prefer-dist
- name: Install WordPress test suite
run: bash bin/install-wp-tests.sh wp_tests root root 127.0.0.1 ${{ matrix.wp }} true
- name: Run PHPUnit (integration suite)
run: vendor/bin/phpunit -c phpunit-integration.xml.dist
# ─────────────────────────────────────────────────────────────────────────────
# Build — only runs when lint + unit tests all pass
# ─────────────────────────────────────────────────────────────────────────────
build:
name: Build
runs-on: ubuntu-latest
needs: [lint, test-js, test-php-unit]
steps:
- name: Checkout
uses: actions/checkout@v5.0.1
- name: Set up Node.js
uses: actions/setup-node@v5.0.0
with:
node-version: "lts/*"
cache: "npm"
- name: Install dependencies
run: npm ci --legacy-peer-deps
- name: Build
run: npm run build
- name: Verify build output
run: |
# Required files exist
test -f build/index.js || { echo "MISSING: build/index.js"; exit 1; }
test -f build/index.css || { echo "MISSING: build/index.css"; exit 1; }
test -f build/block.json || { echo "MISSING: build/block.json"; exit 1; }
test -f build/index.asset.php || { echo "MISSING: build/index.asset.php"; exit 1; }
test -f build/render.php || { echo "MISSING: build/render.php"; exit 1; }
# index.js must be at least 50 KB — catches empty or truncated builds
ACTUAL=$(wc -c < build/index.js)
[ "$ACTUAL" -gt 50000 ] || { echo "FAIL: build/index.js is only ${ACTUAL} bytes (min 50000)"; exit 1; }
# No undefined icon prop — catches missing named exports reaching the bundle
if grep -qP 'icon:\s*void 0' build/index.js; then
echo "FAIL: 'icon: void 0' found in build/index.js — an undefined icon import reached the bundle"
exit 1
fi
echo "Build output verified."
- name: Upload build artifact
uses: actions/upload-artifact@v6.0.0
with:
name: build-${{ github.sha }}
path: build/
retention-days: 7
# ─────────────────────────────────────────────────────────────────────────────
# Plugin Check — WordPress.org compatibility and best-practice checks
# Runs after build so the compiled assets are present in the workspace.
# ─────────────────────────────────────────────────────────────────────────────
plugin-check:
name: Plugin Check
runs-on: ubuntu-latest
needs: [build]
steps:
- name: Checkout
uses: actions/checkout@v5.0.1
- name: Set up Node.js
uses: actions/setup-node@v5.0.0
with:
node-version: "lts/*"
cache: "npm"
- name: Install dependencies
run: npm ci --legacy-peer-deps
- name: Build plugin assets
run: npm run build
- name: WordPress Plugin Check
uses: wordpress/plugin-check-action@9a0e904d688a8b72f99eba093641ec725404e1e8 # post-v1.1.5, uses nick-fields/retry@v4 (node24)
with:
build-dir: "./"
# Exclude dev-only directories so the check only sees distributable files.
exclude-directories: |
tests
vendor
node_modules
bin
scripts
docs
# Check names to skip entirely:
# plugin_review_phpcs — we run PHPCS separately with stricter standards.
# i18n_usage — JS translations are handled by our own i18n pipeline.
# plugin_updater — Update URI: header is intentional for GitHub
# distribution; not applicable for wordpress.org.
exclude-checks: |
plugin_review_phpcs
i18n_usage
plugin_updater
# Error/warning codes to suppress:
# hidden_files / github_directory / unexpected_markdown_file —
# dev-tree artefacts (.gitignore, .github/, HOOKS.md) that are
# stripped by bin/release-zip.sh before distribution.
# application_detected — composer/npm/webpack root files are dev-only,
# excluded from the release zip.
ignore-codes: |
hidden_files
github_directory
unexpected_markdown_file
application_detected