This repository was archived by the owner on Mar 4, 2026. It is now read-only.
ci: fix lint stage — use PHP 8.2 for dev tooling #2
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
| name: CI | |
| on: | |
| push: | |
| pull_request: | |
| workflow_dispatch: | |
| concurrency: | |
| group: ci-${{ github.ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| # ── Stage 1: Fast checks (~30s) ────────────────────────────────────── | |
| lint: | |
| name: Lint | |
| 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: cs2pr | |
| coverage: none | |
| - name: Install Composer dependencies | |
| run: composer install --no-progress --prefer-dist | |
| - name: PHP syntax check | |
| run: find deploy-forge -name '*.php' -print0 | xargs -0 -n1 php -l | |
| - name: WordPress Coding Standards (PHPCS) | |
| run: composer phpcs -- --report-full --report-checkstyle=phpcs-report.xml | |
| - name: Annotate PHPCS results | |
| if: always() | |
| run: cs2pr phpcs-report.xml | |
| # ── Stage 2: Integration tests (~2min) ─────────────────────────────── | |
| integration: | |
| name: Integration (PHP ${{ matrix.php-version }}, WP ${{ matrix.wp-version }}) | |
| runs-on: ubuntu-latest | |
| needs: lint | |
| strategy: | |
| matrix: | |
| php-version: ['8.0', '8.1', '8.2'] | |
| wp-version: ['6.4', 'latest'] | |
| services: | |
| mysql: | |
| image: mysql:8.0 | |
| env: | |
| MYSQL_ROOT_PASSWORD: root | |
| MYSQL_DATABASE: wordpress | |
| ports: | |
| - 3306:3306 | |
| options: >- | |
| --health-cmd="mysqladmin ping --silent" | |
| --health-interval=10s | |
| --health-timeout=5s | |
| --health-retries=5 | |
| env: | |
| WP_DB_NAME: wordpress | |
| WP_DB_USER: root | |
| WP_DB_PASS: root | |
| WP_DB_HOST: 127.0.0.1 | |
| WP_URL: http://localhost:8080 | |
| WP_TITLE: "Test Site" | |
| WP_ADMIN_USER: admin | |
| WP_ADMIN_PASS: admin123 | |
| WP_ADMIN_EMAIL: admin@example.com | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup PHP ${{ matrix.php-version }} | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: ${{ matrix.php-version }} | |
| extensions: mysqli, zip, sodium | |
| coverage: none | |
| - name: Install WP-CLI | |
| run: | | |
| curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar | |
| chmod +x wp-cli.phar | |
| sudo mv wp-cli.phar /usr/local/bin/wp | |
| wp --info | |
| - name: Install WordPress ${{ matrix.wp-version }} | |
| run: | | |
| mkdir -p /tmp/wordpress | |
| cd /tmp/wordpress | |
| wp core download --version=${{ matrix.wp-version }} | |
| wp config create \ | |
| --dbname=$WP_DB_NAME \ | |
| --dbuser=$WP_DB_USER \ | |
| --dbpass=$WP_DB_PASS \ | |
| --dbhost=$WP_DB_HOST | |
| wp core install \ | |
| --url=$WP_URL \ | |
| --title="$WP_TITLE" \ | |
| --admin_user=$WP_ADMIN_USER \ | |
| --admin_password=$WP_ADMIN_PASS \ | |
| --admin_email=$WP_ADMIN_EMAIL \ | |
| --skip-email | |
| echo "WordPress installed successfully" | |
| wp core version | |
| - name: Install plugin | |
| run: | | |
| # Copy plugin to WordPress plugins directory | |
| cp -r deploy-forge /tmp/wordpress/wp-content/plugins/deploy-forge | |
| # Verify plugin is detected | |
| wp plugin list --path=/tmp/wordpress | |
| wp plugin list --path=/tmp/wordpress | grep deploy-forge | |
| - name: Activate plugin | |
| run: | | |
| wp plugin activate deploy-forge --path=/tmp/wordpress | |
| echo "Plugin activated successfully" | |
| # Verify it's active | |
| ACTIVE=$(wp plugin list --status=active --field=name --path=/tmp/wordpress | grep deploy-forge) | |
| if [ -z "$ACTIVE" ]; then | |
| echo "FAIL: Plugin is not active after activation" | |
| exit 1 | |
| fi | |
| echo "Confirmed: deploy-forge is active" | |
| - name: Check for PHP errors in error log | |
| run: | | |
| # Enable error logging | |
| wp config set WP_DEBUG true --raw --path=/tmp/wordpress | |
| wp config set WP_DEBUG_LOG true --raw --path=/tmp/wordpress | |
| # Load WordPress once to trigger any init errors | |
| wp eval "echo 'WordPress loaded successfully';" --path=/tmp/wordpress | |
| # Check for fatal errors / warnings in debug.log | |
| LOG_FILE="/tmp/wordpress/wp-content/debug.log" | |
| if [ -f "$LOG_FILE" ]; then | |
| echo "=== debug.log contents ===" | |
| cat "$LOG_FILE" | |
| echo "==========================" | |
| # Fail on fatal errors or deploy-forge-related warnings | |
| if grep -iE "(Fatal error|Parse error)" "$LOG_FILE"; then | |
| echo "FAIL: Fatal/parse errors found" | |
| exit 1 | |
| fi | |
| if grep -i "deploy.forge" "$LOG_FILE" | grep -iE "(Warning|Notice|Deprecated)" ; then | |
| echo "WARN: Plugin-related warnings found (non-fatal)" | |
| fi | |
| else | |
| echo "No debug.log — no errors" | |
| fi | |
| - name: Verify database tables created | |
| run: | | |
| TABLE=$(wp db query "SHOW TABLES LIKE '%github_deployments';" --path=/tmp/wordpress 2>/dev/null) | |
| if [ -z "$TABLE" ]; then | |
| echo "FAIL: Deployment table was not created on activation" | |
| exit 1 | |
| fi | |
| echo "Database table created: $TABLE" | |
| # Show table structure | |
| wp db query "DESCRIBE $(wp db prefix --path=/tmp/wordpress)github_deployments;" --path=/tmp/wordpress | |
| - name: Verify admin pages load without errors | |
| run: | | |
| cd /tmp/wordpress | |
| # Start PHP built-in server | |
| php -S localhost:8080 -t . &>/tmp/php-server.log & | |
| SERVER_PID=$! | |
| sleep 2 | |
| # Log in and get auth cookie | |
| COOKIE_JAR=$(mktemp) | |
| # Get the login nonce | |
| curl -s -c "$COOKIE_JAR" "http://localhost:8080/wp-login.php" > /dev/null | |
| # Log in | |
| curl -s -b "$COOKIE_JAR" -c "$COOKIE_JAR" \ | |
| -d "log=$WP_ADMIN_USER&pwd=$WP_ADMIN_PASS&wp-submit=Log+In&redirect_to=%2Fwp-admin%2F&testcookie=1" \ | |
| -L "http://localhost:8080/wp-login.php" > /dev/null | |
| ERRORS=0 | |
| # Test each admin page | |
| for PAGE in "admin.php?page=deploy-forge" "admin.php?page=deploy-forge-settings" "admin.php?page=deploy-forge-logs"; do | |
| echo "Testing: $PAGE" | |
| HTTP_CODE=$(curl -s -o /tmp/page-output.html -w "%{http_code}" \ | |
| -b "$COOKIE_JAR" "http://localhost:8080/wp-admin/$PAGE") | |
| if [ "$HTTP_CODE" != "200" ]; then | |
| echo " FAIL: HTTP $HTTP_CODE" | |
| ERRORS=$((ERRORS + 1)) | |
| else | |
| echo " OK: HTTP 200" | |
| fi | |
| # Check for fatal errors in the HTML output | |
| if grep -qi "Fatal error\|Parse error\|Call to undefined" /tmp/page-output.html; then | |
| echo " FAIL: PHP error found in page output" | |
| grep -i "Fatal error\|Parse error\|Call to undefined" /tmp/page-output.html | |
| ERRORS=$((ERRORS + 1)) | |
| fi | |
| done | |
| # Clean up | |
| kill $SERVER_PID 2>/dev/null || true | |
| rm -f "$COOKIE_JAR" | |
| if [ $ERRORS -gt 0 ]; then | |
| echo "FAIL: $ERRORS admin page errors found" | |
| exit 1 | |
| fi | |
| echo "All admin pages loaded successfully" | |
| - name: Verify plugin deactivation | |
| run: | | |
| wp plugin deactivate deploy-forge --path=/tmp/wordpress | |
| echo "Plugin deactivated cleanly" | |
| # Verify it's inactive | |
| ACTIVE=$(wp plugin list --status=active --field=name --path=/tmp/wordpress | grep deploy-forge || true) | |
| if [ -n "$ACTIVE" ]; then | |
| echo "FAIL: Plugin still active after deactivation" | |
| exit 1 | |
| fi | |
| echo "Confirmed: deploy-forge is inactive" | |
| - name: Re-activate and verify (upgrade simulation) | |
| run: | | |
| wp plugin activate deploy-forge --path=/tmp/wordpress | |
| wp eval "echo 'Re-activation successful';" --path=/tmp/wordpress | |
| echo "Plugin re-activated without errors" | |
| # ── Stage 3: E2E staging tests (~2min) ─────────────────────────────── | |
| e2e: | |
| name: E2E Staging | |
| runs-on: ubuntu-latest | |
| needs: integration | |
| if: github.ref == 'refs/heads/staging' | |
| timeout-minutes: 30 | |
| concurrency: | |
| group: e2e-staging | |
| cancel-in-progress: false | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup PHP | |
| uses: shivammathur/setup-php@v2 | |
| with: | |
| php-version: '8.2' | |
| extensions: zip | |
| coverage: none | |
| - name: Install Composer dependencies | |
| run: composer install --no-progress --prefer-dist | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 20 | |
| cache: npm | |
| cache-dependency-path: tests/e2e/package-lock.json | |
| - name: Install E2E dependencies | |
| run: cd tests/e2e && npm ci | |
| - name: Install Playwright browsers | |
| run: cd tests/e2e && npx playwright install chromium --with-deps | |
| - name: Build plugin ZIP | |
| run: | | |
| chmod +x build.sh | |
| ./build.sh | |
| echo "PLUGIN_ZIP_PATH=$(realpath dist/deploy-forge-*.zip)" >> $GITHUB_ENV | |
| - name: Run E2E tests | |
| run: cd tests/e2e && npx playwright test | |
| env: | |
| STAGING_WP_URL: ${{ secrets.STAGING_WP_URL }} | |
| STAGING_WP_ADMIN_USER: ${{ secrets.STAGING_WP_ADMIN_USER }} | |
| STAGING_WP_ADMIN_PASS: ${{ secrets.STAGING_WP_ADMIN_PASS }} | |
| STAGING_APP_URL: ${{ secrets.STAGING_APP_URL }} | |
| STAGING_APP_E2E_EMAIL: ${{ secrets.STAGING_APP_E2E_EMAIL }} | |
| STAGING_APP_E2E_PASSWORD: ${{ secrets.STAGING_APP_E2E_PASSWORD }} | |
| E2E_RESET_SECRET: ${{ secrets.E2E_RESET_SECRET }} | |
| E2E_TEST_REPO: ${{ secrets.E2E_TEST_REPO }} | |
| E2E_TEST_BRANCH: ${{ secrets.E2E_TEST_BRANCH }} | |
| VERCEL_AUTOMATION_BYPASS: ${{ secrets.VERCEL_AUTOMATION_BYPASS }} | |
| PLUGIN_ZIP_PATH: ${{ env.PLUGIN_ZIP_PATH }} | |
| - name: Upload test report | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-playwright-report | |
| path: tests/e2e/playwright-report/ | |
| retention-days: 14 | |
| - name: Upload test results on failure | |
| if: failure() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: e2e-test-results | |
| path: tests/e2e/test-results/ | |
| retention-days: 7 |