WordPress à Grande Échelle : Caching Distribué avec Redis et Memcached
Introduction Le caching distribué est la clé pour scaler WordPress au-delà de millions de pages…
En 2025, le déploiement manuel de WordPress est devenu obsolète pour les équipes professionnelles. L’intégration continue (CI) et le déploiement continu (CD) sont désormais des standards de l’industrie, garantissant qualité, fiabilité et rapidité des livraisons.
GitHub Actions offre une plateforme CI/CD native, gratuite pour les projets open source, et parfaitement intégrée à l’écosystème Git. Cet article présente une architecture complète de pipeline CI/CD pour WordPress, incluant tests automatisés, analyses de sécurité, et déploiements multi-environnements.
┌─────────────┐
│ Git Push │
└──────┬──────┘
│
▼
┌────────────────────────────────────────────┐
│ GitHub Actions Workflow │
├────────────────────────────────────────────┤
│ 1. Validation & Qualité du Code │
│ - PHP CodeSniffer (WPCS) │
│ - PHPStan (Analyse statique) │
│ - ESLint/Stylelint │
├────────────────────────────────────────────┤
│ 2. Tests Automatisés │
│ - Unit Tests (PHPUnit) │
│ - Integration Tests │
│ - E2E Tests (Playwright) │
├────────────────────────────────────────────┤
│ 3. Analyse de Sécurité │
│ - WPScan │
│ - Dependency Check │
│ - OWASP ZAP │
├────────────────────────────────────────────┤
│ 4. Build & Packaging │
│ - Composer install --no-dev │
│ - npm run build │
│ - Asset optimization │
├────────────────────────────────────────────┤
│ 5. Deployment │
│ - Development (auto) │
│ - Staging (auto on main) │
│ - Production (manual approval) │
└────────────────────────────────────────────┘
# .github/workflows/wordpress-ci-cd.yml
name: WordPress CI/CD Pipeline
on:
push:
branches: [ main, develop, 'feature/' ]
pullrequest:
branches: [ main, develop ]
workflowdispatch:
inputs:
environment:
description: 'Target environment'
required: true
default: 'staging'
type: choice
options:
- development
- staging
- production
env:
PHPVERSION: '8.2'
NODEVERSION: '20'
WPVERSION: 'latest'
jobs:
# Job 1: Code Quality & Linting
code-quality:
name: Code Quality Analysis
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHPVERSION }}
extensions: mbstring, xml, ctype, iconv, intl, pdomysql, dom, filter, gd, json
coverage: none
tools: composer:v2
- name: Get Composer Cache Directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUBOUTPUT
- name: Cache Composer dependencies
uses: actions/cache@v4
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --prefer-dist --no-progress --no-suggest
- name: WordPress Coding Standards
run: |
composer require --dev wp-coding-standards/wpcs
vendor/bin/phpcs --config-set installedpaths vendor/wp-coding-standards/wpcs
vendor/bin/phpcs --standard=WordPress plugins/ themes/ --extensions=php
- name: PHPStan Static Analysis
run: |
composer require --dev phpstan/phpstan
vendor/bin/phpstan analyse plugins/ themes/ --level=5
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODEVERSION }}
cache: 'npm'
- name: Install Node dependencies
run: npm ci
- name: ESLint JavaScript
run: npm run lint:js
- name: Stylelint CSS
run: npm run lint:css
# Job 2: Unit & Integration Tests
tests:
name: Automated Tests
runs-on: ubuntu-latest
needs: code-quality
services:
mysql:
image: mysql:8.0
env:
MYSQLROOTPASSWORD: root
MYSQLDATABASE: wordpresstest
ports:
- 3306:3306
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 with Xdebug
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHPVERSION }}
extensions: mbstring, xml, ctype, iconv, intl, pdomysql, dom, filter, gd, json
coverage: xdebug
tools: composer:v2
- name: Cache Composer dependencies
uses: actions/cache@v4
with:
path: vendor
key: ${{ runner.os }}-composer-${{ hashFiles('/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --prefer-dist --no-progress
- name: Install WordPress Test Suite
run: |
bash bin/install-wp-tests.sh wordpresstest root root 127.0.0.1 ${{ env.WPVERSION }}
- name: Run PHPUnit Tests
run: |
vendor/bin/phpunit --coverage-clover=coverage.xml --coverage-text
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
with:
file: ./coverage.xml
flags: unittests
name: codecov-umbrella
- name: Integration Tests
run: |
vendor/bin/phpunit --testsuite=integration
# Job 3: E2E Tests
e2e-tests:
name: End-to-End Tests
runs-on: ubuntu-latest
needs: tests
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODEVERSION }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps
- name: Start WordPress with Docker
run: |
docker-compose -f docker-compose.test.yml up -d
sleep 30
- name: Run Playwright Tests
run: npx playwright test
- name: Upload Playwright Report
uses: actions/upload-artifact@v4
if: always()
with:
name: playwright-report
path: playwright-report/
retention-days: 30
- name: Cleanup
if: always()
run: docker-compose -f docker-compose.test.yml down
# Job 4: Security Scanning
security:
name: Security Analysis
runs-on: ubuntu-latest
needs: code-quality
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHPVERSION }}
tools: composer:v2
- name: Install Composer dependencies
run: composer install --prefer-dist --no-progress
- name: Check for known security vulnerabilities
run: |
composer audit
- name: WPScan Vulnerability Database Check
run: |
docker run --rm
-v $(pwd):/app
wpscanteam/wpscan
--url http://localhost
--enumerate p,t,u
--plugins-detection mixed
--api-token ${{ secrets.WPSCANAPITOKEN }}
- name: OWASP Dependency-Check
uses: dependency-check/Dependency-CheckAction@main
with:
project: 'WordPress Project'
path: '.'
format: 'HTML'
- name: Upload Dependency-Check Report
uses: actions/upload-artifact@v4
with:
name: dependency-check-report
path: reports/
# Job 5: Build Assets
build:
name: Build & Package
runs-on: ubuntu-latest
needs: [tests, e2e-tests, security]
if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/develop'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHPVERSION }}
tools: composer:v2
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODEVERSION }}
cache: 'npm'
- name: Install production dependencies
run: |
composer install --no-dev --optimize-autoloader --prefer-dist --no-progress
npm ci --production
- name: Build frontend assets
run: |
npm run build
- name: Optimize images
run: |
npm run optimize:images
- name: Create deployment archive
run: |
mkdir -p artifacts
tar -czf artifacts/wordpress-build-${{ github.sha }}.tar.gz
--exclude='.git'
--exclude='nodemodules'
--exclude='tests'
--exclude='.github'
--exclude='.md'
.
- name: Upload build artifact
uses: actions/upload-artifact@v4
with:
name: wordpress-build
path: artifacts/
retention-days: 30
# Job 6: Deploy to Development
deploy-development:
name: Deploy to Development
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/develop'
environment:
name: development
url: https://dev.example.com
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: wordpress-build
- name: Deploy to Development Server
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.DEVHOST }}
username: ${{ secrets.DEVUSER }}
key: ${{ secrets.DEVSSHKEY }}
port: ${{ secrets.DEVPORT }}
script: |
set -e
cd /var/www/development
# Backup actuel
tar -czf backups/backup-$(date +%Y%m%d-%H%M%S).tar.gz wp-content/plugins wp-content/themes
# Maintenance mode
wp maintenance-mode activate
# Déploiement
tar -xzf ~/wordpress-build-${{ github.sha }}.tar.gz -C /tmp/wordpress-deploy
rsync -av --delete /tmp/wordpress-deploy/plugins/ wp-content/plugins/
rsync -av --delete /tmp/wordpress-deploy/themes/ wp-content/themes/
# Database migrations
wp plugin activate custom-plugin
# Clear cache
wp cache flush
wp rewrite flush
# Maintenance mode off
wp maintenance-mode deactivate
# Cleanup
rm -rf /tmp/wordpress-deploy
# Job 7: Deploy to Staging
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
environment:
name: staging
url: https://staging.example.com
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: wordpress-build
- name: Deploy via Deployer
run: |
composer global require deployer/deployer
~/.composer/vendor/bin/dep deploy staging
--tag=${{ github.sha }}
--host=${{ secrets.STAGINGHOST }}
--user=${{ secrets.STAGINGUSER }}
--identity-file=${{ secrets.STAGINGSSHKEY }}
- name: Run smoke tests
run: |
curl -f https://staging.example.com || exit 1
curl -f https://staging.example.com/wp-admin/ || exit 1
- name: Notify Slack
uses: slackapi/slack-github-action@v1.25.0
with:
webhook: ${{ secrets.SLACKWEBHOOK }}
webhook-type: incoming-webhook
payload: |
{
"text": "✅ Staging deployment successful - ${{ github.sha }}"
}
# Job 8: Deploy to Production
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: deploy-staging
if: github.eventname == 'workflowdispatch' && github.event.inputs.environment == 'production'
environment:
name: production
url: https://example.com
steps:
- name: Download build artifact
uses: actions/download-artifact@v4
with:
name: wordpress-build
- name: Blue-Green Deployment
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.PRODHOST }}
username: ${{ secrets.PRODUSER }}
key: ${{ secrets.PRODSSHKEY }}
port: ${{ secrets.PRODPORT }}
script: |
set -e
# Déterminer l'environnement actif
CURRENT=$(readlink /var/www/production/current)
if [ "$CURRENT" = "/var/www/production/blue" ]; then
TARGET="green"
INACTIVE="blue"
else
TARGET="blue"
INACTIVE="green"
fi
echo "Deploying to $TARGET environment"
# Préparer le nouvel environnement
mkdir -p /var/www/production/$TARGET
tar -xzf ~/wordpress-build-${{ github.sha }}.tar.gz -C /var/www/production/$TARGET
# Synchroniser wp-config.php et uploads
cp /var/www/production/$INACTIVE/wp-config.php /var/www/production/$TARGET/
rsync -av /var/www/production/$INACTIVE/wp-content/uploads/ /var/www/production/$TARGET/wp-content/uploads/
# Database migrations
cd /var/www/production/$TARGET
wp plugin activate custom-plugin
# Health check
curl -f http://localhost:8080/$TARGET || exit 1
# Switch symlink (atomic switch)
ln -sfn /var/www/production/$TARGET /var/www/production/current-new
mv -Tf /var/www/production/current-new /var/www/production/current
# Reload PHP-FPM
sudo systemctl reload php8.2-fpm
# Clear cache
wp cache flush
echo "Deployment successful - active: $TARGET"
- name: Post-deployment monitoring
run: |
# Attendre 2 minutes pour les métriques
sleep 120
# Vérifier les métriques (APM, logs, etc.)
# Rollback si nécessaire
- name: Notify team
uses: slackapi/slack-github-action@v1.25.0
with:
webhook: ${{ secrets.SLACKWEBHOOK }}
webhook-type: incoming-webhook
payload: |
{
"text": "🚀 Production deployment successful",
"blocks": [
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "Production Deployment SuccessfulnnCommit: ${{ github.sha }}nAuthor: ${{ github.actor }}nURL: https://example.com"
}
}
]
}
tty', true);
set('keepreleases', 5);
set('sharedfiles', [
'wp-config.php',
'.env',
]);
set('shareddirs', [
'wp-content/uploads',
]);
set('writabledirs', [
'wp-content/uploads',
'wp-content/cache',
]);
set('clearpaths', [
'.git',
'.github',
'nodemodules',
'tests',
'.md',
'composer.json',
'composer.lock',
'package.json',
'package-lock.json',
]);
// Environnements
host('staging')
->setHostname('staging.example.com')
->setRemoteUser('deploy')
->setDeployPath('/var/www/staging')
->setPort(22)
->set('branch', 'main')
->set('httpuser', 'www-data')
->set('writablemode', 'chmod')
->set('writablechmodmode', '0755');
host('production')
->setHostname('example.com')
->setRemoteUser('deploy')
->setDeployPath('/var/www/production')
->setPort(22)
->set('branch', 'main')
->set('httpuser', 'www-data')
->set('writablemode', 'chmod')
->set('writablechmodmode', '0755');
// Tâches personnalisées
/
Backup de la base de données
/
task('wordpress:backup', function () {
$timestamp = date('Y-m-dH-i-s');
$backupFile = "backup{$timestamp}.sql.gz";
within('{{deploypath}}/current', function () use ($backupFile) {
run("wp db export - | gzip > {{deploypath}}/backups/{$backupFile}");
});
writeln("Database backup created: {$backupFile} ");
})->desc('Backup WordPress database');
/
Migrations de base de données
/
task('wordpress:migrate', function () {
within('{{releasepath}}', function () {
run('wp plugin activate custom-plugin');
run('wp custom-plugin migrate');
});
})->desc('Run database migrations');
/
Optimisation de la base de données
/
task('wordpress:optimize', function () {
within('{{releasepath}}', function () {
run('wp db optimize');
run('wp transient delete --all');
});
})->desc('Optimize WordPress database');
/
Clear des caches
/
task('wordpress:cache:clear', function () {
within('{{releasepath}}', function () {
run('wp cache flush');
run('wp rewrite flush');
// Clear opcache
run('php -r "opcachereset();"');
// Clear Redis si disponible
if (test('[ -f /usr/bin/redis-cli ]')) {
run('redis-cli FLUSHALL');
}
});
})->desc('Clear all caches');
/
Maintenance mode
/
task('wordpress:maintenance:enable', function () {
within('{{deploypath}}/current', function () {
run('wp maintenance-mode activate');
});
})->desc('Enable maintenance mode');
task('wordpress:maintenance:disable', function () {
within('{{releasepath}}', function () {
run('wp maintenance-mode deactivate');
});
})->desc('Disable maintenance mode');
/
Health check
/
task('wordpress:health:check', function () {
$url = get('url');
$response = runLocally("curl -s -o /dev/null -w '%{httpcode}' {$url}");
if ($response !== '200') {
throw new Exception("Health check failed. HTTP status: {$response}");
}
writeln("Health check passed: {$url} ");
})->desc('Perform health check');
/
Rollback avec restauration de base de données
/
task('wordpress:rollback:full', function () {
// Rollback des fichiers
invoke('rollback');
// Restaurer le dernier backup de base de données
within('{{deploypath}}/current', function () {
$latestBackup = run('ls -t {{deploypath}}/backups/.sql.gz | head -1');
if ($latestBackup) {
writeln("Restoring database from: {$latestBackup} ");
run("gunzip < {$latestBackup} | wp db import -");
writeln("Database restored successfully ");
}
});
})->desc('Full rollback (files + database)');
/
Workflow de déploiement principal
/
task('deploy', [
'deploy:prepare',
'deploy:vendors',
'wordpress:backup',
'wordpress:maintenance:enable',
'deploy:publish',
'wordpress:migrate',
'wordpress:optimize',
'wordpress:cache:clear',
'wordpress:maintenance:disable',
'wordpress:health:check',
'deploy:cleanup',
'deploy:success',
])->desc('Deploy WordPress application');
// Hook de rollback
after('rollback', 'wordpress:cache:clear');
// Gestion des échecs
fail('deploy', 'deploy:failed');
fail('deploy', 'wordpress:maintenance:disable');
./tests/unit/
./tests/integration/
./plugins/
./themes/
./vendor/
./tests/
#!/usr/bin/env bash
# bin/install-wp-tests.sh
if [ $# -lt 3 ]; then
echo "usage: $0 [db-host] [wp-version] [skip-database-creation]"
exit 1
fi
DBNAME=$1
DBUSER=$2
DBPASS=$3
DBHOST=${4-localhost}
WPVERSION=${5-latest}
SKIPDBCREATE=${6-false}
TMPDIR=${TMPDIR-/tmp}
TMPDIR=$(echo $TMPDIR | sed -e "s//$//")
WPTESTSDIR=${WPTESTSDIR-$TMPDIR/wordpress-tests-lib}
WPCOREDIR=${WPCOREDIR-$TMPDIR/wordpress/}
download() {
if [ which curl ]; then
curl -s "$1" > "$2";
elif [ which wget ]; then
wget -nv -O "$2" "$1"
fi
}
if [[ $WPVERSION =~ ^[0-9]+.[0-9]+-(beta|RC) ]]; then
WPBRANCH=${WPVERSION%-}
WPTESTSTAG="branches/$WPBRANCH"
elif [[ $WPVERSION =~ ^[0-9]+.[0-9]+$ ]]; then
WPTESTSTAG="branches/$WPVERSION"
elif [[ $WPVERSION =~ [0-9]+.[0-9]+.[0-9]+ ]]; then
if [[ $WPVERSION =~ [0-9]+.[0-9]+.[0] ]]; then
WPTESTSTAG="tags/${WPVERSION%??}"
else
WPTESTSTAG="tags/$WPVERSION"
fi
elif [[ $WPVERSION == 'nightly' || $WPVERSION == 'trunk' ]]; then
WPTESTSTAG="trunk"
else
WPTESTSTAG="tags/$WPVERSION"
fi
installwp() {
if [ -d $WPCOREDIR ]; then
return;
fi
mkdir -p $WPCOREDIR
if [[ $WPVERSION == 'nightly' || $WPVERSION == 'trunk' ]]; then
mkdir -p $TMPDIR/wordpress-nightly
download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip
unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/
mv $TMPDIR/wordpress-nightly/wordpress/ $WPCOREDIR
else
if [ $WPVERSION == 'latest' ]; then
local ARCHIVENAME='latest'
elif [[ $WPVERSION =~ [0-9]+.[0-9]+ ]]; then
local ARCHIVENAME="wordpress-$WPVERSION"
else
local ARCHIVENAME="wordpress-$WPVERSION"
fi
download https://wordpress.org/${ARCHIVENAME}.tar.gz $TMPDIR/wordpress.tar.gz
tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WPCOREDIR
fi
download https://raw.githubusercontent.com/markoheijnen/wp-mysqli/master/db.php $WPCOREDIR/wp-content/db.php
}
installtestsuite() {
if [ ! -d $WPTESTSDIR ]; then
mkdir -p $WPTESTSDIR
svn co --quiet https://develop.svn.wordpress.org/${WPTESTSTAG}/tests/phpunit/includes/ $WPTESTSDIR/includes
svn co --quiet https://develop.svn.wordpress.org/${WPTESTSTAG}/tests/phpunit/data/ $WPTESTSDIR/data
fi
if [ ! -f wp-tests-config.php ]; then
download https://develop.svn.wordpress.org/${WPTESTSTAG}/wp-tests-config-sample.php "$WPTESTSDIR"/wp-tests-config.php
WPCOREDIRESCAPED=$(echo $WPCOREDIR | sed 's:/:/:g')
sed -i "s:dirname( FILE ) . '/src/':'$WPCOREDIRESCAPED':" "$WPTESTSDIR"/wp-tests-config.php
sed -i "s/youremptytestdbnamehere/$DBNAME/" "$WPTESTSDIR"/wp-tests-config.php
sed -i "s/yourusernamehere/$DBUSER/" "$WPTESTSDIR"/wp-tests-config.php
sed -i "s/yourpasswordhere/$DBPASS/" "$WPTESTSDIR"/wp-tests-config.php
sed -i "s|localhost|${DBHOST}|" "$WPTESTSDIR"/wp-tests-config.php
fi
}
installdb() {
if [ ${SKIPDBCREATE} = "true" ]; then
return 0
fi
PASSARG=""
if [ ! -z $DBPASS ]; then
PASSARG="-p$DBPASS"
fi
mysqladmin create $DBNAME --user="$DBUSER" $PASSARG --host="$DBHOST" --protocol=tcp
}
installwp
installtestsuite
installdb
// tests/e2e/login.spec.ts
import { test, expect } from '@playwright/test';
test.describe('WordPress Login', () => {
test('should login successfully with valid credentials', async ({ page }) => {
await page.goto('/wp-admin');
await page.fill('#userlogin', process.env.WPADMINUSER || 'admin');
await page.fill('#userpass', process.env.WPADMINPASS || 'password');
await page.click('#wp-submit');
await expect(page).toHaveURL(/wp-admin/);
await expect(page.locator('#wpadminbar')).toBeVisible();
});
test('should show error with invalid credentials', async ({ page }) => {
await page.goto('/wp-admin');
await page.fill('#userlogin', 'invalid');
await page.fill('#userpass', 'wrong');
await page.click('#wp-submit');
await expect(page.locator('#loginerror')).toBeVisible();
await expect(page.locator('#loginerror')).toContainText('Error');
});
});
// tests/e2e/post-creation.spec.ts
import { test, expect } from '@playwright/test';
test.describe('Post Creation', () => {
test.beforeEach(async ({ page }) => {
// Login avant chaque test
await page.goto('/wp-admin');
await page.fill('#userlogin', process.env.WPADMINUSER || 'admin');
await page.fill('#userpass', process.env.WPADMINPASS || 'password');
await page.click('#wp-submit');
});
test('should create a new post', async ({ page }) => {
await page.goto('/wp-admin/post-new.php');
// Titre du post
await page.fill('h1[aria-label="Add title"]', 'Test Post E2E');
// Contenu (Gutenberg)
const editor = page.locator('[aria-label="Block: Paragraph"]').first();
await editor.click();
await editor.fill('This is a test post created via E2E testing.');
// Publier
await page.click('button:has-text("Publish")');
await page.click('button:has-text("Publish")'); // Confirmation
await expect(page.locator('.components-snackbar_content')).toContainText('published');
});
test('should save post as draft', async ({ page }) => {
await page.goto('/wp-admin/post-new.php');
await page.fill('h1[aria-label="Add title"]', 'Draft Post E2E');
const editor = page.locator('[aria-label="Block: Paragraph"]').first();
await editor.click();
await editor.fill('This is a draft post.');
// Sauvegarder brouillon
await page.click('button:has-text("Save draft")');
await expect(page.locator('.editor-post-saved-state')).toContainText('Saved');
});
});
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: [
['html'],
['junit', { outputFile: 'test-results/junit.xml' }],
],
use: {
baseURL: process.env.WPBASEURL || 'http://localhost:8080',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
webServer: {
command: 'docker-compose -f docker-compose.test.yml up',
url: 'http://localhost:8080',
reuseExistingServer: !process.env.CI,
timeout: 120 1000,
},
});
# docker-compose.test.yml
version: '3.8'
services:
wordpress-test:
image: wordpress:6.4-php8.2-apache
ports:
- "8080:80"
environment:
WORDPRESSDBHOST: db-test
WORDPRESSDBUSER: wordpress
WORDPRESSDBPASSWORD: wordpress
WORDPRESSDBNAME: wordpresstest
WORDPRESSDEBUG: 1
volumes:
- ./:/var/www/html/wp-content/plugins/custom-plugin
- ./themes:/var/www/html/wp-content/themes
dependson:
- db-test
db-test:
image: mysql:8.0
environment:
MYSQLDATABASE: wordpresstest
MYSQLUSER: wordpress
MYSQLPASSWORD: wordpress
MYSQLROOTPASSWORD: rootpassword
volumes:
- db-test-data:/var/lib/mysql
redis-test:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
db-test-data:
# .github/workflows/notify.yml
name: Deployment Notifications
on:
workflowrun:
workflows: ["WordPress CI/CD Pipeline"]
types:
- completed
jobs:
notify:
runs-on: ubuntu-latest
steps:
- name: Notify on success
if: ${{ github.event.workflowrun.conclusion == 'success' }}
uses: slackapi/slack-github-action@v1.25.0
with:
webhook: ${{ secrets.SLACKWEBHOOK }}
webhook-type: incoming-webhook
payload: |
{
"text": "✅ Deployment successful",
"blocks": [
{
"type": "header",
"text": {
"type": "plaintext",
"text": "Deployment Successful ✅"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "Repository:n${{ github.repository }}"
},
{
"type": "mrkdwn",
"text": "Branch:n${{ github.refname }}"
},
{
"type": "mrkdwn",
"text": "Commit:n${{ github.sha }}"
},
{
"type": "mrkdwn",
"text": "Author:n${{ github.actor }}"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plaintext",
"text": "View Workflow"
},
"url": "${{ github.event.workflowrun.htmlurl }}"
}
]
}
]
}
- name: Notify on failure
if: ${{ github.event.workflowrun.conclusion == 'failure' }}
uses: slackapi/slack-github-action@v1.25.0
with:
webhook: ${{ secrets.SLACKWEBHOOK }}
webhook-type: incoming-webhook
payload: |
{
"text": "❌ Deployment failed",
"blocks": [
{
"type": "header",
"text": {
"type": "plaintext",
"text": "Deployment Failed ❌"
}
},
{
"type": "section",
"fields": [
{
"type": "mrkdwn",
"text": "Repository:n${{ github.repository }}"
},
{
"type": "mrkdwn",
"text": "Branch:n${{ github.refname }}"
}
]
},
{
"type": "actions",
"elements": [
{
"type": "button",
"text": {
"type": "plaintext",
"text": "View Logs"
},
"url": "${{ github.event.workflowrun.html_url }}",
"style": "danger"
}
]
}
]
}
Un pipeline CI/CD moderne pour WordPress en 2025 intègre :
Cette architecture garantit des livraisons fiables, rapides et sécurisées pour les applications WordPress d’entreprise.
Mots-clés : WordPress CI/CD, GitHub Actions, WordPress deployment, automated testing WordPress, DevOps WordPress, WordPress pipeline, Deployer PHP, WordPress Docker, E2E testing WordPress, continuous deployment
Meta Description** : Guide complet d’intégration CI/CD pour WordPress avec GitHub Actions. Pipeline automatisé, tests E2E, déploiement multi-environnements et code production-ready pour 2025.
Cet article est vivant — corrections, contre-arguments et retours de production sont les bienvenus. Trois canaux, choisissez celui qui vous convient.