2026年6月2日
2026年6月2日
WordPressのCI/CDをGitHub Actionsで組む方法|PHPCS・PHPUnit・rsyncデプロイ
WordPressのテーマやプラグインをGitで管理しているなら、GitHub Actionsで「プッシュのたびに検査し、mainに入ったら本番へ反映する」流れを作れます。この記事では、PHPの構文チェック、WordPressコーディング規約(WPCS)のチェック、PHPUnitによるテスト、rsyncによるデプロイを、1つのワークフローファイルにまとめる手順を説明します。
対象は、テーマやプラグインのディレクトリ単位でリポジトリを作っている構成です。WordPress本体やアップロード済みの画像(wp-content/uploads)はリポジトリに入れず、デプロイでも触らない前提にしています。
全体の流れ
作るワークフローは2つのジョブで構成します。
| ジョブ | 動くタイミング | 内容 |
|---|---|---|
test |
すべてのプッシュとプルリクエスト | PHPの構文チェック、PHPCS(WPCS)、PHPUnit |
deploy |
mainへのプッシュで、test が成功したときだけ |
rsyncでサーバーのテーマ(プラグイン)ディレクトリへ同期 |
deploy は needs: test で test の成功を条件にします。テストに失敗した変更は本番へ届きません。
準備1:リポジトリにWPCSを入れる
WordPressコーディング規約のチェックには、PHP_CodeSnifferとWordPress Coding Standards(WPCS)を使います。Composerで開発用の依存として追加します。
composer config allow-plugins.dealerdirect/phpcodesniffer-composer-installer true
composer require --dev wp-coding-standards/wpcs:"^3.0"
1行目は、WPCSと一緒に入るComposerプラグインの実行を許可する設定です。これを入れないと、規約の登録が行われません。2026年10月時点でWPCSの最新は3.x系です。
検査する範囲と規約は、リポジトリ直下の .phpcs.xml.dist に書いておきます。CIでもローカルでも同じ設定で動きます。
<?xml version="1.0"?>
<ruleset name="My Theme">
<file>.</file>
<exclude-pattern>/vendor/*</exclude-pattern>
<exclude-pattern>/node_modules/*</exclude-pattern>
<exclude-pattern>/tests/*</exclude-pattern>
<arg name="extensions" value="php"/>
<arg value="sp"/>
<rule ref="WordPress"/>
</ruleset>
既存のコードに初めて適用すると大量の指摘が出ることがあります。まずローカルで vendor/bin/phpcs を実行し、vendor/bin/phpcbf で自動修正できるものを直してから、CIに組み込むと進めやすくなります。
準備2:PHPUnitのテスト一式を作る(プラグインの場合)
プラグインであれば、WP-CLIの wp scaffold plugin-tests でテストの雛形を作れます。--ci=github を付けると、GitHub Actions用のワークフロー(.github/workflows/testing.yml)も生成されます。
wp scaffold plugin-tests my-plugin --ci=github
生成されるおもなファイルは次のとおりです。
phpunit.xml.dist:PHPUnitの設定bin/install-wp-tests.sh:WordPressのテストライブラリとテスト用データベースを用意するスクリプトtests/bootstrap.php、tests/test-sample.php:テストの起動処理とサンプル
WordPressのテストライブラリはPHPUnitとYoast PHPUnit Polyfillsを必要とします。どちらもComposerで開発用の依存に加えておきます(2026年10月時点で、WordPress本体のテストはPolyfillsの1.x系とPHPUnit 9系の組み合わせで動いています)。
composer require --dev phpunit/phpunit:"^9.6" yoast/phpunit-polyfills:"^1.1"
生成されたワークフローをそのまま使ってもかまいません。この記事では、構文チェックやデプロイと同じファイルにまとめる形で書き直します。テストの書き方はWordPressのユニットテストをPHPUnitで書く方法で扱っています。テーマのようにテストを用意しない場合は、後述のワークフローからPHPUnitの部分を外してください。
準備3:デプロイ用のSSH鍵とシークレットを登録する
デプロイには、本番サーバーへSSHで接続できる専用の鍵を使います。個人の鍵を流用せず、デプロイ専用のユーザーと鍵を作り、書き込める範囲をテーマ(プラグイン)のディレクトリに絞っておくと、鍵が漏れたときの被害を小さくできます。
# 手元で鍵を作る(パスフレーズなし。CIから使うため)
ssh-keygen -t ed25519 -f deploy_key -C "github-actions-deploy" -N ""
# 公開鍵をサーバーのデプロイ用ユーザーに登録
ssh-copy-id -i deploy_key.pub deploy@example.com
# サーバーのホスト鍵を取得し、内容を目で確かめてから登録する
ssh-keyscan -t ed25519 example.com
GitHubのリポジトリで「Settings」→「Secrets and variables」→「Actions」を開き、次の値を登録します。秘密にすべき値はSecrets、そうでない値はVariablesに分けます。
| 種類 | 名前 | 中身 |
|---|---|---|
| Secret | SSH_PRIVATE_KEY |
deploy_key(秘密鍵)の内容 |
| Secret | SSH_KNOWN_HOSTS |
ssh-keyscan で確かめたホスト鍵の行 |
| Variable | DEPLOY_HOST |
example.com |
| Variable | DEPLOY_USER |
deploy |
| Variable | DEPLOY_PATH |
/var/www/html/wp-content/themes/my-theme |
ワークフローの中で ssh-keyscan を実行して接続先を自動で信頼させる書き方を見かけますが、その時点で接続先が偽物でも気づけません。ホスト鍵は事前に確かめた値をシークレットに入れておきます。シークレットの扱いの詳細はGitHub Actionsのシークレット管理方法を参照してください。
ワークフローファイルを書く
.github/workflows/ci-cd.yml を作り、次の内容を書きます。echo でファイルに1行ずつ追記するのではなく、エディタでYAMLとして書いてください。YAMLはインデント(半角スペース)で構造を表すため、追記方式では崩れやすくなります。
name: CI/CD
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
php: ['8.2', '8.3', '8.4']
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wordpress_test
ports:
- 3306:3306
options: >-
--health-cmd="mysqladmin ping -h 127.0.0.1"
--health-interval=10s
--health-timeout=5s
--health-retries=5
steps:
- uses: actions/checkout@v7
- uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: composer
coverage: none
- name: Install dependencies
run: composer install --no-progress --prefer-dist
- name: PHP syntax check
run: find . -path ./vendor -prune -o -name '*.php' -print0 | xargs -0 -n1 php -l
- name: WordPress Coding Standards
run: vendor/bin/phpcs
- name: Install WordPress test suite
run: bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1 latest true
- name: PHPUnit
env:
WP_TESTS_PHPUNIT_POLYFILLS_PATH: ${{ github.workspace }}/vendor/yoast/phpunit-polyfills
run: vendor/bin/phpunit
deploy:
needs: test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
environment: production
concurrency: production
steps:
- uses: actions/checkout@v7
- name: Set up SSH
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }}
run: |
install -m 700 -d ~/.ssh
printf '%s\n' "$SSH_PRIVATE_KEY" > ~/.ssh/deploy_key
chmod 600 ~/.ssh/deploy_key
printf '%s\n' "$SSH_KNOWN_HOSTS" > ~/.ssh/known_hosts
- name: Deploy with rsync
env:
DEPLOY_HOST: ${{ vars.DEPLOY_HOST }}
DEPLOY_USER: ${{ vars.DEPLOY_USER }}
DEPLOY_PATH: ${{ vars.DEPLOY_PATH }}
run: |
rsync -az --delete \
--exclude-from=.deployignore \
-e "ssh -i ~/.ssh/deploy_key" \
./ "${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/"
ポイントを補足します。
matrix.phpで、複数のPHPバージョンで同じ検査を並行して実行します。並べるバージョンは、本番サーバーと、サポートしたい範囲に合わせて決めてください(2026年10月時点の例です)。services.mysqlは、PHPUnitで使うテスト用データベースです。テストを使わない場合は、servicesと最後の2ステップを削除します。- 構文チェックの行は、
vendorを除くすべての.phpファイルにphp -lを実行します。1つでもエラーがあればxargsが0以外で終了し、ジョブが失敗します。 bin/install-wp-tests.shの引数は、データベース名・ユーザー・パスワード・ホスト・WordPressのバージョン・データベース作成を省くかどうかの順です。データベースはMySQLのサービス側(MYSQL_DATABASE)で作っているため、最後をtrueにしています。WP_TESTS_PHPUNIT_POLYFILLS_PATHは、WordPressのテストライブラリにPolyfillsの場所を教えるための環境変数です。wp scaffoldが作るtests/bootstrap.phpがこの値を読み取ります。deployのifで、プルリクエストでは動かず、mainへのプッシュのときだけ動くようにしています。concurrency: productionで、デプロイが同時に2本走らないようにしています。- シークレットは
envで環境変数に渡してからスクリプトで使います。${{ secrets.… }}をrunの中に直接書くより、値がシェルの構文として解釈される心配が少なくなります。
本番に送らないファイルを決める
rsync --delete は、リポジトリに無いファイルをサーバー側から削除します。送らないファイルを .deployignore に書いておきます。ここに書いたパスは送信の対象外になり、サーバー側に同名のファイルがあっても削除されません。
.git/
.github/
tests/
bin/
vendor/
node_modules/
.phpcs.xml.dist
phpunit.xml.dist
composer.json
composer.lock
.deployignore
テーマやプラグインが実行時にComposerのライブラリを必要とする場合は、vendor/ を除外せず、デプロイジョブの中で composer install --no-dev --optimize-autoloader を実行してから送ってください。開発用のPHPCSやPHPUnitまで本番に送らずに済みます。
初回は、rsyncに --dry-run を付けたステップで、送信・削除されるファイルの一覧を確かめてから本番を動かすと安全です。
本番デプロイの前に承認を挟む
environment: production を指定したジョブは、リポジトリの「Settings」→「Environments」で作った production 環境の保護ルールに従います。「Required reviewers」を設定すると、指定した人が承認するまでデプロイジョブは待機します。環境の保護ルールが使えるかどうかは、リポジトリの公開・非公開とGitHubのプランによって異なります。使えない場合は、ブランチ保護でmainへの直接プッシュを禁止し、プルリクエストのレビューを必須にする方法で代わりにできます。
動作を確かめる
- 作業用ブランチでわざと構文エラーを入れてプッシュし、プルリクエストの「Checks」で
testジョブが失敗することを確かめる - 修正してプッシュし、
testが成功し、deployは動かない(プルリクエストなので)ことを確かめる - mainへマージし、
deployが動いてサーバーのファイルが更新されたことを確かめる
デプロイ後にページキャッシュが残って変更が見えない場合は、キャッシュ系プラグインの機能か、サーバーで wp cache flush(オブジェクトキャッシュの削除)を実行して確かめます。
つまずきやすい点
Permission denied (publickey):公開鍵がデプロイ用ユーザーの~/.ssh/authorized_keysに入っているか、秘密鍵のシークレットに改行まで含めて貼ったかを確かめるHost key verification failed:SSH_KNOWN_HOSTSの行が、接続先のホスト名(またはIPアドレス)と一致しているかを確かめる- rsyncの権限エラー:デプロイ用ユーザーが
DEPLOY_PATHに書き込めるか、サーバー側にrsyncが入っているかを確かめる - PHPCSの指摘が多すぎてマージできない:既存コードの指摘を
phpcbfで減らすか、.phpcs.xml.distで除外するルールを明示する
まとめ
- テストとデプロイを別ジョブにし、
needsでテストの成功をデプロイの条件にする - WPCSはComposerで入れ、
.phpcs.xml.distでCIとローカルの設定をそろえる - プラグインのテストは
wp scaffold plugin-tests --ci=githubで雛形を作れる - SSHの秘密鍵とホスト鍵はシークレットに入れ、デプロイ専用の鍵とユーザーを使う
rsync --deleteは除外ファイルを決め、初回は--dry-runで確かめる
関連記事: