Automating CI/CD Workflows for Enterprise Full Site Editing (FSE) Block Themes and theme.json in Legacy Core PHP Implementations
Establishing a Robust CI/CD Pipeline for FSE Block Themes
Automating the deployment of Full Site Editing (FSE) block themes, especially those incorporating complex `theme.json` configurations and targeting legacy Core PHP implementations, presents unique challenges. This post details a production-ready CI/CD strategy leveraging GitHub Actions, Docker, and advanced WordPress CLI (WP-CLI) techniques to ensure consistent, reliable deployments.
Phase 1: Local Development and Linting
Before any code is committed, a rigorous local development environment is paramount. This includes automated checks for code style, syntax errors, and theme validation. We’ll integrate these into a pre-commit hook and a CI pipeline stage.
Pre-commit Hooks with ESLint and Stylelint
For JavaScript and CSS within your block theme, enforcing coding standards is crucial. We’ll use ESLint for JavaScript and Stylelint for CSS. These can be integrated via Husky and lint-staged for automated execution on commit.
`package.json` Configuration
Ensure your `package.json` includes the necessary dev dependencies and scripts:
{
"name": "my-fse-theme",
"version": "1.0.0",
"scripts": {
"lint:js": "eslint js/",
"lint:css": "stylelint \"**/*.css\"",
"lint:php": "phpcs --standard=WordPress-Core --extensions=php src/",
"validate:theme": "wp theme validate --path=/path/to/wordpress/root"
},
"devDependencies": {
"@wordpress/eslint-plugin": "^10.0.0",
"eslint": "^8.0.0",
"husky": "^8.0.0",
"lint-staged": "^13.0.0",
"stylelint": "^14.0.0",
"stylelint-config-standard": "^28.0.0",
"phpcs": "^3.0.0"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.{js,jsx,ts,tsx}": [
"eslint --fix"
],
"*.{css,scss,sass}": [
"stylelint --fix"
],
"*.php": [
"phpcs --standard=WordPress-Core --extensions=php"
]
}
}
Installing Dependencies and Husky
After setting up `package.json`, install dependencies and initialize Husky:
npm install npx husky install
PHP Code Sniffer for PHP Linting
For PHP code, we’ll leverage PHP Code Sniffer (PHPCS) with the WordPress Coding Standards. This ensures your PHP files adhere to WordPress’s established best practices.
Installing PHPCS and WordPress Standards
If not already installed, use Composer:
composer require --dev squizlabs/php_codesniffer wp-coding-standards/wpcs
Then, register the WordPress standards:
phpcs --config-set installed_paths vendor/wp-coding-standards/wpcs/
Theme Validation with WP-CLI
WP-CLI’s `theme validate` command is indispensable for checking `theme.json` and other theme structure issues.
wp theme validate --path=/path/to/your/wordpress/installation
Phase 2: CI Pipeline Setup with GitHub Actions
We’ll construct a GitHub Actions workflow to automate these checks on every push and pull request. This workflow will use a Docker container to provide a consistent WordPress environment.
Workflow File: `.github/workflows/ci.yml`
This YAML file defines the CI stages:
name: CI Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
container:
image: wordpress:php8.1-apache # Or your preferred PHP/Apache version
ports:
- 80:80
volumes:
- wordpress_data:/var/www/html
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1' # Match container version
extensions: gd, imagick, mbstring, zip, intl
coverage: none
- name: Install Node.js and npm
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install Composer dependencies
run: composer install --prefer-dist --no-progress --no-suggest
- name: Install npm dependencies
run: npm install
- name: Lint JavaScript
run: npm run lint:js
- name: Lint CSS
run: npm run lint:css
- name: Lint PHP
run: npm run lint:php
- name: Setup WordPress environment (WP-CLI)
run: |
# Wait for Apache to start (basic check)
sleep 10
# Install WordPress core (if not present in image)
if [ ! -f /var/www/html/wp-load.php ]; then
wp core download --path=/var/www/html --allow-root
wp config create --dbname=wordpress --dbuser=root --dbpass=root --dbhost=localhost --path=/var/www/html --allow-root
wp db create --path=/var/www/html --allow-root
wp core install --url=http://localhost --title="Test Site" --admin_user=admin --admin_password=password [email protected] --path=/var/www/html --allow-root
fi
# Install theme
wp theme install ./ --activate --path=/var/www/html --allow-root
- name: Validate Theme and theme.json
run: |
# Ensure WP-CLI can access the theme directory
# The theme is installed in the previous step, so its path is relative to WP root
wp theme validate --path=/var/www/html
- name: Run WordPress Unit Tests (Optional but Recommended)
# This step requires a more complex setup with WP_TESTS_DIR and a database
# See WordPress Core development documentation for details
# run: |
# # ... setup for WP_TESTS_DIR and database ...
# wp core unit-tests run --path=/var/www/html
- name: Build Theme Assets (if applicable)
# If your theme uses a build process for JS/CSS (e.g., Webpack, Gulp)
# run: npm run build
- name: Upload Theme Artifacts (Optional)
# If you need to package the theme for distribution
# uses: actions/upload-artifact@v3
# with:
# name: theme-package
# path: ./dist/ # Or wherever your built theme is
# Add deployment jobs here for staging/production
# deploy_staging:
# needs: build
# runs-on: ubuntu-latest
# if: github.ref == 'refs/heads/main' && github.event_name == 'push'
# steps:
# - name: Deploy to Staging
# # ... deployment steps using SSH, rsync, or a deployment service ...
Explanation of Key Steps
- Containerization: The `wordpress:php8.1-apache` image provides a pre-configured WordPress environment, simplifying setup. Volumes ensure data persistence if needed, though for CI, ephemeral environments are common.
- PHP and Node.js Setup: Explicitly sets up the required PHP and Node.js versions.
- Dependency Installation: Installs both Composer and npm packages.
- Linting: Executes the defined linting scripts.
- WordPress Environment Setup: This is a critical part. It downloads WordPress core (if not already in the image), creates a database, installs WordPress, and then installs and activates your theme. This ensures the theme is tested within a functional WordPress context. The `sleep 10` is a rudimentary wait for Apache; more robust health checks might be needed in complex scenarios.
- Theme Validation: Runs `wp theme validate` to catch structural and `theme.json` errors.
- Artifact Upload: An optional step to save built theme files.
Phase 3: Advanced `theme.json` and Block Validation
Beyond basic linting, ensuring the integrity and correctness of `theme.json` and custom blocks is vital. This involves schema validation and potentially custom validation scripts.
Schema Validation for `theme.json`
The WordPress core team maintains a JSON schema for `theme.json`. We can leverage this in our CI pipeline to validate the structure and types of our `theme.json` file.
Obtaining the Schema
The schema is typically found within the WordPress core repository. For CI, it’s often best to download a specific version or reference it directly if available via a stable URL. As of WordPress 6.2, the schema is part of the core distribution. A common approach is to fetch it from a known stable location or include it in your repository.
Using a JSON Schema Validator (e.g., `ajv-cli`)
We can add a JSON schema validator to our `devDependencies` and create a script to run the validation.
npm install --save-dev ajv-cli
Add a script to `package.json`:
"scripts": {
// ... other scripts
"validate:themejson": "ajv validate -s /path/to/wordpress/core/wp-content/themes/your-theme/theme.json -d /path/to/wordpress/core/wp-content/themes/your-theme/theme.json"
}
Note: The above `ajv-cli` example is simplified. A more robust approach would involve fetching the official WordPress `theme.json` schema and validating against that. You might need to adjust paths or download the schema dynamically within your CI script.
Custom Block Validation Scripts
For custom blocks, you might have specific validation logic beyond what `theme.json` or standard linters cover. This could involve checking attribute types, default values, or dependencies.
Example: Python Script for Block Validation
Create a Python script (e.g., `scripts/validate_blocks.py`) to parse block metadata and perform checks. This script would need to be executed within the CI environment.
import json
import os
import sys
def validate_block_attributes(block_json_path):
try:
with open(block_json_path, 'r') as f:
block_data = json.load(f)
except FileNotFoundError:
print(f"Error: Block JSON not found at {block_json_path}", file=sys.stderr)
return False
except json.JSONDecodeError:
print(f"Error: Invalid JSON in {block_json_path}", file=sys.stderr)
return False
is_valid = True
if 'attributes' in block_data:
for attr_name, attr_config in block_data['attributes'].items():
# Example validation: Ensure 'type' is present and is a string
if 'type' not in attr_config:
print(f"Validation Error in {block_json_path}: Attribute '{attr_name}' is missing 'type'.", file=sys.stderr)
is_valid = False
elif not isinstance(attr_config['type'], str):
print(f"Validation Error in {block_json_path}: Attribute '{attr_name}' has non-string 'type'.", file=sys.stderr)
is_valid = False
# Add more specific attribute validations here (e.g., allowed types, default values)
return is_valid
def main():
blocks_dir = 'src/blocks' # Assuming blocks are in src/blocks
all_valid = True
if not os.path.isdir(blocks_dir):
print(f"Warning: Blocks directory '{blocks_dir}' not found.", file=sys.stderr)
return
for root, _, files in os.walk(blocks_dir):
for file in files:
if file == 'block.json':
block_json_path = os.path.join(root, file)
if not validate_block_attributes(block_json_path):
all_valid = False
if not all_valid:
sys.exit(1) # Fail the CI job
else:
print("All custom blocks validated successfully.")
if __name__ == "__main__":
main()
Integrate this script into your CI workflow:
- name: Validate Custom Blocks
run: |
python scripts/validate_blocks.py
Phase 4: Deployment Strategies
Once the CI pipeline passes, the theme is ready for deployment. For enterprise environments, this typically involves staging and production environments.
Staging Environment Deployment
Automate deployment to a staging server using SSH, rsync, or a dedicated deployment tool. This allows for final testing before production.
Example: SSH and Rsync Deployment
You’ll need SSH keys configured for GitHub Actions and access to your staging server. Store sensitive information like SSH keys and server IPs as GitHub Secrets.
deploy_staging:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push' # Deploy only on push to main
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup SSH Key
uses: webfactory/[email protected]
with:
ssh-private-key: ${{ secrets.STAGING_SSH_PRIVATE_KEY }}
- name: Add SSH Host Key
run: ssh-keyscan ${{ secrets.STAGING_SERVER_IP }} >> ~/.ssh/known_hosts
- name: Deploy Theme to Staging
run: |
rsync -avz --delete \
--exclude '.git' \
--exclude '.github' \
--exclude 'node_modules' \
--exclude 'vendor' \
. ${{ secrets.STAGING_SSH_USER }}@${{ secrets.STAGING_SERVER_IP }}:/path/to/your/staging/wordpress/wp-content/themes/my-fse-theme/
# Optional: Run WP-CLI commands on staging server
ssh ${{ secrets.STAGING_SSH_USER }}@${{ secrets.STAGING_SERVER_IP }} "wp cache flush --path=/path/to/your/staging/wordpress"
ssh ${{ secrets.STAGING_SSH_USER }}@${{ secrets.STAGING_SERVER_IP }} "wp theme activate my-fse-theme --path=/path/to/your/staging/wordpress"
Production Environment Deployment
Production deployments should be highly controlled, often requiring manual approval or a separate trigger. The process is similar to staging but targets the production environment.
Manual Approval Gate
GitHub Actions allows for environments with manual approval steps. Configure an environment in your GitHub repository settings and reference it in the workflow.
deploy_production:
needs: build # Or needs: deploy_staging if you want sequential deployment
runs-on: ubuntu-latest
environment:
name: production
url: https://your-production-site.com
if: github.ref == 'refs/heads/main' && github.event_name == 'push' # Deploy only on push to main
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Setup SSH Key
uses: webfactory/[email protected]
with:
ssh-private-key: ${{ secrets.PRODUCTION_SSH_PRIVATE_KEY }}
- name: Add SSH Host Key
run: ssh-keyscan ${{ secrets.PRODUCTION_SERVER_IP }} >> ~/.ssh/known_hosts
- name: Deploy Theme to Production
run: |
rsync -avz --delete \
--exclude '.git' \
--exclude '.github' \
--exclude 'node_modules' \
--exclude 'vendor' \
. ${{ secrets.PRODUCTION_SSH_USER }}@${{ secrets.PRODUCTION_SERVER_IP }}:/path/to/your/production/wordpress/wp-content/themes/my-fse-theme/
ssh ${{ secrets.PRODUCTION_SSH_USER }}@${{ secrets.PRODUCTION_SERVER_IP }} "wp cache flush --path=/path/to/your/production/wordpress"
ssh ${{ secrets.PRODUCTION_SSH_USER }}@${{ secrets.PRODUCTION_SERVER_IP }} "wp theme activate my-fse-theme --path=/path/to/your/production/wordpress"
Advanced Diagnostics and Troubleshooting
When things go wrong, systematic diagnostics are key. Here are common failure points and how to address them.
CI Job Failures
- Check Logs: The first step is always to examine the detailed logs provided by GitHub Actions. Look for specific error messages from linters, WP-CLI, or build scripts.
- Environment Mismatch: Ensure the PHP version in `setup-php` matches the Docker image and your local development environment. Similarly, Node.js versions can cause build failures.
- Dependency Issues: `npm install` or `composer install` failures often point to network issues, incompatible package versions, or corrupted lock files. Try clearing caches (`npm cache clean –force`, `composer clear-cache`) and re-running.
- WP-CLI Path Errors: Ensure the `–path` argument for WP-CLI commands correctly points to the WordPress root directory within the container.
- Permissions: In Docker containers, file permissions can be tricky. The `–allow-root` flag for WP-CLI is often necessary but should be used judiciously. Ensure the user running commands has write access to necessary directories.
Theme Validation Errors
wp theme validate errors typically relate to `theme.json` syntax, missing required files (like `style.css` or `index.php`), or incorrect theme headers.
Deployment Failures
- SSH Connection Errors: Verify SSH keys are correctly added to GitHub Secrets and that the server’s `authorized_keys` file is properly configured. Check firewall rules.
- Rsync Permissions: Ensure the SSH user has write permissions to the target theme directory on the server.
- WP-CLI Commands on Remote Server: If `wp cache flush` or `wp theme activate` fail on the remote server, log in manually via SSH and run the commands to debug. Ensure WP-CLI is installed and accessible on the remote server.
Conclusion
Implementing a comprehensive CI/CD pipeline for FSE block themes requires a multi-faceted approach. By integrating local development checks, robust CI stages with containerized environments, and controlled deployment strategies, you can significantly improve the quality, consistency, and reliability of your WordPress theme development workflow. The key is automation at every stage, from code linting to production deployment, with clear diagnostic pathways for troubleshooting.