name: CI — Lint & Test on: push: branches: [ main ] pull_request: branches: [ main ] workflow_call: permissions: contents: read jobs: # ── PHP Syntax Check ───────────────────────────────────────────── lint-php: name: PHP Lint (${{ matrix.php }}) runs-on: ubuntu-latest strategy: matrix: php: ['7.4', '8.1', '8.3'] steps: - uses: actions/checkout@v4 - name: Set up PHP ${{ matrix.php }} uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} tools: composer - name: Syntax check all PHP files run: find . -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l # ── Security Audit ─────────────────────────────────────────────── security-audit: name: Security Audit runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Check for common PHP security issues run: | echo ":: Checking for direct SQL queries without prepare..." # Exclude uninstall.php (DROP TABLE is standard WP pattern), activator (table DDL), # and logger (TRUNCATE) — these use $wpdb->prefix which is safe internal state. if grep -r "query\s*(\s*['\"].*\$" --include="*.php" --exclude-dir=vendor . 2>/dev/null \ | grep -v "\\\$wpdb->prepare" \ | grep -v "uninstall\.php" \ | grep -v "class-peptide-news-activator\.php" \ | grep -v "class-peptide-news-logger\.php" \ | grep -v "DROP TABLE" \ | grep -v "TRUNCATE" \ && [ ${PIPESTATUS[0]} -eq 0 ]; then echo "WARNING: Found potential unescaped SQL queries. Use wpdb->prepare() for all dynamic SQL." exit 1 fi echo ":: Checking for eval() usage..." if grep -r "eval\s*(" --include="*.php" --exclude-dir=vendor . 2>/dev/null; then echo "ERROR: eval() found. This is a critical security risk." exit 1 fi echo ":: Checking for base64_decode without validation..." if grep -r "base64_decode\s*(" --include="*.php" --exclude-dir=vendor . 2>/dev/null; then echo "WARNING: Found base64_decode(). Ensure the input is properly validated." fi echo ":: Checking for file_get_contents with user input..." if grep -r "file_get_contents\s*(\s*\$" --include="*.php" --exclude-dir=vendor . 2>/dev/null; then echo "WARNING: Found file_get_contents with variable. Ensure input is validated and restricted." fi echo "All security checks passed." # ── PHPCS Coding Standards ─────────────────────────────────────── phpcs: name: PHPCS runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up PHP uses: shivammathur/setup-php@v2 with: php-version: '8.3' tools: composer - name: Install dependencies run: composer install --prefer-dist --no-progress - name: Run PHPCS run: vendor/bin/phpcs --standard=.phpcs.xml.dist || test $? -eq 2 # exit 2 = warnings-only, not a build failure # ── PHPUnit Tests ──────────────────────────────────────────────── phpunit: name: PHPUnit (${{ matrix.php }}) runs-on: ubuntu-latest strategy: matrix: php: ['7.4', '8.1', '8.3'] steps: - uses: actions/checkout@v4 - name: Set up PHP ${{ matrix.php }} uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} tools: composer coverage: xdebug - name: Install dependencies run: composer install --prefer-dist --no-progress - name: Run PHPUnit run: vendor/bin/phpunit --testdox # ── JavaScript Lint ────────────────────────────────────────────── lint-js: name: JS Lint runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up Node uses: actions/setup-node@v4 with: node-version: '20' - name: Check JS syntax run: | for f in $(find . -name '*.js' -not -path './vendor/*' -not -path './node_modules/*' -not -path '*/vendor/*'); do node --check "$f" || exit 1 done