name: HVAC Plugin CI/CD Pipeline on: push: branches: [ main, develop ] pull_request: branches: [ main ] schedule: # Daily security scan at 2 AM UTC - cron: '0 2 * * *' env: NODE_VERSION: '18' PHP_VERSION: '8.1' WORDPRESS_VERSION: 'latest' MYSQL_VERSION: '8.0' jobs: security-scan: name: Security Analysis runs-on: ubuntu-latest if: github.event_name == 'push' || github.event_name == 'schedule' steps: - name: Checkout Code uses: actions/checkout@v4 with: fetch-depth: 0 # Full history for better analysis - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ env.PHP_VERSION }} extensions: dom, curl, libxml, mbstring, zip, pdo, mysql, pdo_mysql, bcmath, soap, intl, gd, exif, iconv - name: Install Security Tools run: | # Install PHPCS Security Standards composer global require automattic/phpcs-security-audit # Install Semgrep for security scanning python3 -m pip install semgrep # Install npm audit for Node.js dependencies npm install -g npm@latest - name: WordPress Security Scan run: | # Check for WordPress security issues echo "๐Ÿ” Scanning WordPress plugin security..." # PHPCS Security Audit ~/.composer/vendor/bin/phpcs --standard=Security --extensions=php --ignore=vendor/ ./ # Semgrep security rules semgrep --config=auto --error --json --output=security-report.json ./ - name: Credential Scan run: | echo "๐Ÿ” Scanning for exposed credentials..." # Check for hardcoded credentials (enhanced patterns) if grep -r -E "(password\s*=\s*['\"](?!.*\{\{)[^'\"]{8,}|api[_-]?key\s*[=:]\s*['\"][^'\"]{20,}|secret\s*[=:]\s*['\"][^'\"]{16,})" --include="*.php" --include="*.js" --include="*.json" .; then echo "โŒ Potential credentials found in code" exit 1 fi # Check for WordPress salts/keys in wrong location if find . -name "*.php" -exec grep -l "define.*NONCE_SALT\|define.*AUTH_SALT" {} \; | grep -v wp-config; then echo "โŒ WordPress salts found outside wp-config" exit 1 fi - name: Upload Security Report if: always() uses: actions/upload-artifact@v4 with: name: security-report path: security-report.json retention-days: 30 code-quality: name: Code Quality & Standards runs-on: ubuntu-latest steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ env.PHP_VERSION }} extensions: dom, curl, libxml, mbstring, zip tools: composer, phpcs, phpmd, phpstan - name: Install Dependencies run: | if [ -f composer.json ]; then composer install --no-dev --optimize-autoloader fi - name: WordPress Coding Standards run: | echo "๐Ÿ” Checking WordPress coding standards..." # Install WordPress Coding Standards composer global require wp-coding-standards/wpcs phpcs --config-set installed_paths ~/.composer/vendor/wp-coding-standards/wpcs # Run PHPCS with WordPress standards phpcs --standard=WordPress --extensions=php --ignore=vendor/ ./ - name: PHP Static Analysis run: | echo "๐Ÿ” Running PHP static analysis..." # PHPStan analysis if [ -f phpstan.neon ]; then phpstan analyse --memory-limit=1G else phpstan analyse includes/ --level=5 --memory-limit=1G fi - name: PHP Mess Detector run: | echo "๐Ÿ” Checking code complexity..." phpmd includes/ text cleancode,codesize,controversial,design,naming,unusedcode unit-tests: name: Unit Tests runs-on: ubuntu-latest services: mysql: image: mysql:${{ env.MYSQL_VERSION }} env: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: wordpress_test options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ env.PHP_VERSION }} extensions: dom, curl, libxml, mbstring, zip, pdo, mysql, pdo_mysql tools: composer, phpunit - name: Setup WordPress Test Environment run: | echo "๐Ÿ—๏ธ Setting up WordPress test environment..." # Install WordPress test framework bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1:3306 ${{ env.WORDPRESS_VERSION }} - name: Install Test Dependencies run: | if [ -f composer.json ]; then composer install --optimize-autoloader fi - name: Run PHPUnit Tests run: | echo "๐Ÿงช Running WordPress plugin unit tests..." if [ -f phpunit.xml ]; then phpunit --coverage-text --coverage-html=coverage/ else echo "No phpunit.xml found - creating basic test configuration" # Create basic PHPUnit configuration if none exists mkdir -p tests/unit phpunit --bootstrap tests/bootstrap.php tests/unit/ fi - name: Upload Coverage Report uses: actions/upload-artifact@v4 with: name: coverage-report path: coverage/ retention-days: 7 integration-tests: name: Integration Tests runs-on: ubuntu-latest services: mysql: image: mysql:${{ env.MYSQL_VERSION }} env: MYSQL_ROOT_PASSWORD: root MYSQL_DATABASE: wordpress_test options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - name: Setup PHP & WordPress uses: shivammathur/setup-php@v2 with: php-version: ${{ env.PHP_VERSION }} extensions: dom, curl, libxml, mbstring, zip, pdo, mysql, pdo_mysql - name: Install Dependencies run: | npm ci # Install WordPress CLI curl -O https://raw.githubusercontent.com/wp-cli/wp-cli/v2.8.1/wp-cli.phar chmod +x wp-cli.phar sudo mv wp-cli.phar /usr/local/bin/wp - name: Setup WordPress run: | echo "๐Ÿ—๏ธ Setting up WordPress for integration tests..." # Download WordPress wp core download --version=${{ env.WORDPRESS_VERSION }} --path=/tmp/wordpress # Configure WordPress wp config create --dbname=wordpress_test --dbuser=root --dbpass=root --dbhost=127.0.0.1:3306 --path=/tmp/wordpress wp core install --url=http://localhost --title="Test Site" --admin_user=admin --admin_password=admin --admin_email=test@example.com --path=/tmp/wordpress # Activate plugin wp plugin activate upskill-event-manager --path=/tmp/wordpress - name: Install Playwright run: | npx playwright install --with-deps chromium - name: Run Integration Tests run: | echo "๐Ÿงช Running integration tests..." # Set environment variables for tests export WORDPRESS_URL=http://localhost export WORDPRESS_USERNAME=admin export WORDPRESS_PASSWORD=admin export HEADLESS=true # Run existing integration tests if [ -f test-master-trainer-e2e.js ]; then node test-master-trainer-e2e.js fi if [ -f test-final-verification.js ]; then node test-final-verification.js fi - name: Upload Test Results if: always() uses: actions/upload-artifact@v4 with: name: integration-test-results path: | test-results/ playwright-report/ /tmp/playwright-mcp-output/ retention-days: 7 deploy-staging: name: Deploy to Staging runs-on: ubuntu-latest needs: [security-scan, code-quality, unit-tests, integration-tests] if: github.ref == 'refs/heads/develop' && github.event_name == 'push' environment: staging steps: - name: Checkout Code uses: actions/checkout@v4 - name: Setup Deployment Tools run: | echo "๐Ÿš€ Setting up deployment tools..." # Install rsync for file transfer sudo apt-get update && sudo apt-get install -y rsync # Setup SSH key for staging deployment mkdir -p ~/.ssh echo "${{ secrets.STAGING_SSH_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -H ${{ secrets.STAGING_HOST }} >> ~/.ssh/known_hosts - name: Pre-deployment Validation run: | echo "๐Ÿ” Running pre-deployment validation..." # Validate plugin files exist if [ ! -f hvac-community-events.php ]; then echo "โŒ Main plugin file not found" exit 1 fi # Check version consistency PLUGIN_VERSION=$(grep "Version:" hvac-community-events.php | cut -d' ' -f2) echo "Plugin version: $PLUGIN_VERSION" - name: Deploy to Staging run: | echo "๐Ÿš€ Deploying to staging..." # Use the existing deployment script if [ -f scripts/deploy.sh ]; then chmod +x scripts/deploy.sh ./scripts/deploy.sh staging else echo "โŒ Deployment script not found" exit 1 fi - name: Post-deployment Tests run: | echo "๐Ÿงช Running post-deployment verification..." # Basic connectivity test curl -f ${{ secrets.STAGING_URL }} || exit 1 # Plugin activation check via WP-CLI (if available) if command -v wp &> /dev/null; then wp plugin is-active hvac-community-events --ssh=${{ secrets.STAGING_SSH_USER }}@${{ secrets.STAGING_HOST }} --path=${{ secrets.STAGING_WP_PATH }} fi deploy-production: name: Deploy to Production runs-on: ubuntu-latest needs: [deploy-staging] if: github.ref == 'refs/heads/main' && github.event_name == 'push' && contains(github.event.head_commit.message, '[deploy-production]') environment: production steps: - name: Checkout Code uses: actions/checkout@v4 - name: Manual Approval Check run: | echo "๐Ÿšจ Production deployment requires manual approval" echo "This job should only run with explicit '[deploy-production]' in commit message" - name: Setup Deployment Tools run: | echo "๐Ÿš€ Setting up production deployment..." sudo apt-get update && sudo apt-get install -y rsync mkdir -p ~/.ssh echo "${{ secrets.PRODUCTION_SSH_KEY }}" > ~/.ssh/id_rsa chmod 600 ~/.ssh/id_rsa ssh-keyscan -H ${{ secrets.PRODUCTION_HOST }} >> ~/.ssh/known_hosts - name: Production Backup run: | echo "๐Ÿ’พ Creating production backup..." # Create backup before deployment ssh ${{ secrets.PRODUCTION_SSH_USER }}@${{ secrets.PRODUCTION_HOST }} " cd ${{ secrets.PRODUCTION_WP_PATH }}/wp-content/plugins tar -czf hvac-community-events-backup-$(date +%Y%m%d-%H%M%S).tar.gz hvac-community-events/ " - name: Deploy to Production run: | echo "๐Ÿš€ Deploying to production..." chmod +x scripts/deploy.sh ./scripts/deploy.sh production - name: Production Health Check run: | echo "๐Ÿฅ Running production health check..." # Wait for deployment to settle sleep 30 # Basic connectivity and plugin check curl -f ${{ secrets.PRODUCTION_URL }} || exit 1 # More comprehensive checks can be added here echo "โœ… Production deployment successful" notify: name: Notification runs-on: ubuntu-latest needs: [security-scan, code-quality, unit-tests, integration-tests, deploy-staging] if: always() steps: - name: Send Notification run: | echo "๐Ÿ“ข Sending pipeline notification..." # Determine overall status if [ "${{ needs.security-scan.result }}" = "success" ] && \ [ "${{ needs.code-quality.result }}" = "success" ] && \ [ "${{ needs.unit-tests.result }}" = "success" ] && \ [ "${{ needs.integration-tests.result }}" = "success" ]; then STATUS="โœ… SUCCESS" else STATUS="โŒ FAILED" fi echo "Pipeline Status: $STATUS" echo "Commit: ${{ github.sha }}" echo "Branch: ${{ github.ref_name }}" echo "Actor: ${{ github.actor }}" # Additional notification methods can be added here # (Slack, Discord, email, etc.)