WordPressのテーマやプラグイン開発でその内容をGitHub Actionsを使ってエックスサーバに自動デプロイする

# はじめに
WordPressで開発を行っている際、アップロードが手間だったり、接続方法を聞いたり、アップロード事故[^incident]を起こしたり…、と色々と悩みがあります。
Gitにプッシュすると開発環境にデプロイされて、Slackで通知されて、PRで確認できて…、という環境になるとそういった悩みを軽減するとができます。
複数人での開発が捗るので是非オススメです。

[^incident]: 別のファイルを古いファイルで上書きしてしまった…、などです。

# エックスサーバで環境の準備
エックスサーバで環境を準備していきます。

## SSHの有効化

自動化する前にまずは手元（ローカル環境）からデプロイを行えるようにしておきます。

今回の対象はエックスサーバです。
デプロイをFTPで行うこともできますが、SSHを使ったほうが転送が高速なrsyncを使えたり、色々なコマンドを使えたりするため便利です。
まずはSSHを有効化しておきます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/1ee20cd7-8a58-7fc5-0d6b-e530ddf13014.png)

接続のためのキーペアを準備します。
いくつか方法はありますが、エックスサーバ上で鍵を作成します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/10e4937d-a692-63ca-7df7-c13c8bc3b80e.png)

作成したキーをローカルのリポジトリ上においておきます。今回は `etc/id_rsa` して保存しておきました。[^1]

```
.
├── etc
    └── id_rsa
```

秘密鍵があると環境（エックスサーバではサーバ単位）でアクセスできてしまうため、複数のドメインを運用している場合などは注意が必要です。取り扱いには十分注意してください。
[鍵ごとに実行できるコマンドを制限](https://qiita.com/yousan/items/dbec7375fbbb53d38e65)することもできるため、自動デプロイとセキュリティのバランスを取れるようにしましょう。

この鍵を使ってアクセスをしておきます。
ユーザー名（サーバのアカウント名）、サーバ名を事前に調べておきます。

```console
$ ssh -o StrictHostKeyChecking=no -i etc/id_rsa  -p 10022 example@svxxxxx.xserver.jp

[example@svxxxx ~]$
```

新しいコマンドプロンプトが出れば成功です。
`exit` として抜けておきましょう。

# アップロード先、DB接続先の確認

アップロード先ディレクトリを確認しておきます。サブドメインかサブディレクトリにおくことになります。
今回はサブディレクトリに置きました。
エックスサーバではホームディレクトリにドメイン名のディレクトリがあり、そのしたに`public_html`というディレクトリがあります。ここがドキュメントルートとなります。

今回は `example.com` というドメインがあり、そのサブディレクトリに `sample` というディレクトリでデプロイ先とすることにしました。
下記のディレクトリとなります。

`/home/example/example.com/public_html/sample/`

DBについてはデータベースの作成とユーザの作成をから行っておきます。

![Screen Shot 2020-09-20 at 21.16.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/c19718c1-2a49-8752-bc27-5e3c76e08359.png)

この接続情報とにローカルのwp-config.phpを元に、エックスサーバ上でのwp-config.phpを作成しておきます。


```php:wp-config-xserv.php（抜粋）
define( 'DB_NAME', 'example_sample' );
define( 'DB_USER', 'example_sample' );
define( 'DB_PASSWORD', 'somethingpassword' );
define( 'DB_HOST', 'mysqlxxxxxx.xserver.jp' );
```

## WP-CLIのインストール
インポートにWP-CLIを使うため、エックスサーバ上でWP-CLIが使えるようにしておきます。

> Xserver で wp-cli を動かす - Qiita
> https://qiita.com/kkotaro0111/items/26221712625e79c3ba8d

> エックスサーバーにWP-CLIをインストール - Qiita
> https://qiita.com/kurudrive/items/17d39404baf8cb6e162e

# ファイル構成
WordPressで色々と制作をする場合、テーマ、プラグイン、記事（データベース）、メディア（画像ファイル）といったものが必要になります。

制作案件の場合には主にテーマファイルを修正することが多いので、制作現場ではまずはテーマディレクトリだけをGit管理にすることが多いです。

僕の入る案件ではシステム（っぽい）案件が多いため、プラグインやDBデータもGitで管理するようにしています。そうすることで再現性が高くなり、複数人での開発や自動デプロイも便利になります。
もちろんテーマのみの管理でも自動デプロイも可能です。

今回のファイル構成は下記のようにしています。

- テーマ
- プラグイン
- WordPressコアファイル
- DB
- メディア

Git管理するときには容量に注意が必要です。
特に大規模なサイトの場合にはDBが数GB、メディアに至っては数百GBになってしまうため、そういった場合には管理から外します。
あくまで開発の対象主体はテーマファイルやプラグインであるため、Gitには検証用のデータを用意して本番の再現性ではなく開発用の再現性を担保しておきます。


<!-- 
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">最近の案件ではWordPressのコアファイルやプラグインも直接リポジトリで管理していて、それをそのままデプロイするようにしている。そこにDBと主要なメディアファイルも一緒に入れてる。<br>そうするとコアやプラグインバージョンも固定されて再現性高くて良いなと。</p>&mdash; Yousan_O (@Yousan_O) <a href="https://twitter.com/Yousan_O/status/1307646818411397120?ref_src=twsrc%5Etfw">September 20, 2020</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
-->

## DBのエクスポート
上記の通り、DBデータもGitで管理します。DBをエクスポートするスクリプトを記述しておき、必要に応じて開発者がエクスポートできるようにしておきます。

```bash:export.sh
#!/usr/bin/env bash
set -xe # ここからデバッグ用の実行中コマンドが出力されます
cd -- "$(dirname "$BASH_SOURCE")" # このスクリプトファイルがある場所に移動する
cd ../
wp db export - | gzip > etc/local.sql.gz
```

ファイルは `etc/local.sql.gz` としています。
Gitのコミット時フックを管理する[husky](https://www.npmjs.com/package/husky)などを導入して、コミット時に自動的にエクスポートするようにしても良いです。[^husky]

[^husky]: huskyはNode.jsのNPMモジュールのため、WordPress案件制作に関わる人はNPM環境を導入していないことがあるので注意が必要です。

# デプロイスクリプト

デプロイスクリプトを作成しますが、環境ごとに違う部分だけを先に記載します。
同期先ディレクトリ、URL、SSH接続ユーザ名、サーバホスト名を記述します。

```bash
BASE_DIR=/home/example/example.com/public_html/sample/
BASE_URL='https://example.com/sample/'
SSH_HOST="example@svxxxx.xserver.jp"
```

その情報を元に書いたスクリプトが下記の通りです。

```bash:bin/deploy.sh
#!/usr/bin/env bash
# 1. 送り先のディレクトリを決定し、なければ作成する
# 2. 構成ファイル（wpコア、プラグイン、テーマ）を送る
# 3. コンフィグファイルの変更
# 4. DBをダンプし、送る
# 5. 通知

set -xe # エラーが有った場合に停止
cd -- "$(dirname "$BASH_SOURCE")" # ファイルがある場所に移動する
cd ../ # リポジトリのルートに移動する

BASE_DIR=/home/example/example.com/public_html/sample/
BASE_URL='https://example.com/sample/'
TARGET_URL=${BASE_URL}
SSH_OPTION="-o StrictHostKeyChecking=no -i ./etc/id_rsa -p 10022"
SSH_HOST="example@svxxxx.xserver.jp"
SSH_COMMAND='ssh '${SSH_OPTION}' '${SSH_HOST}

# 秘密鍵を0600にする
chmod 0600 etc/id_rsa

# 送り先ディレクトリの決定
TARGET_DIR=${BASE_DIR}${BRANCH_NAME}

# ここからデプロイのためのコマンドスタート
# デプロイ先にディレクトリを作成
${SSH_COMMAND} 'mkdir -p '${TARGET_DIR}

rsync -avr -e "ssh ${SSH_OPTION}" \
  app/public/ \
  ${SSH_HOST}:${TARGET_DIR}

# コンフィグファイルの上書き
${SSH_COMMAND} 'cd '${TARGET_DIR}'; mv wp-config-xserv.php wp-config.php'

# DBのエクスポートとインポート
cat ./etc/local.sql.gz | gunzip - | \
  ${SSH_COMMAND} 'cd '${TARGET_DIR}'; wp db import -'

${SSH_COMMAND} 'cd '${TARGET_DIR}'; wp option set siteurl ' ${TARGET_URL}
${SSH_COMMAND} 'cd '${TARGET_DIR}'; wp option set home ' ${TARGET_URL}
```


実行すると
1. 必要なファイルの転送
2. DBのエクスポートとインポート
3. オプションの変更

が行われます。エラーが無ければURLにアクセスするとGitと同じ環境のWordPressが展開されています。

## GitHub Actionsの設定

コミットごとに自動でデプロイされるよう、GitHub Actionsを設定していきます。
GitHubの`Actions`の項目を選び、最初のファイルをコミットしておきます。
どのファイルでも良いですが、今回はPHPを選びました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/abdcaa51-0b58-ee57-c1d0-e48774815991.png)

一旦ブラウザ上でコミットをした後、手元で修正を加えます。


```yaml:.github/workflow/php.yml
name: PHP Composer

on: push

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2

    - name: Deploy
      run: ./bin/deploy.sh
```

このCI上で行うことはほとんどなく、先ほど作成したデプロイスクリプトを実行します。
また対象となるブランチを全てにしておきます。（デフォルトはでdevelopのみ）
これでコミットを行えばデプロイスクリプトが走るようになります。

うまく動かない場合には `deploy.sh` にデバッグ情報をしこみ、CIの結果を見ながら調整を行います。

今回作成した際にはSSH関連でエラーが発生したため、 `ssh -v` のオプションを追記して調整しました。
うまくデプロイされるようなっていればOKです。

## Slackの通知

複数人で開発する際など、Slackで通知が来ると便利です。
通知にはSlackのトークンが必要です。取得には下記をご参考ください。

> slackのIncoming webhookが新しくなっていたのでまとめてみた - Qiita
> https://qiita.com/kshibata101/items/0e13c420080a993c5d16


```bash
(最下部に追加）
# デプロイ結果をSlackに通知
curl -X POST -H 'Content-type: application/json' \
  --data '{"text": "=> '${DEPLOYED_URL}'",
           "username": "デプロイ通知",
           "channel": "#example",
           "icon_emoji": ":tada:",
           }' \
https://hooks.slack.com/services/XXXXXXx/XXXXX/XXXXXXXXXXXXXX
```

成功すると下記のようなメッセージが通知されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/47327/82eddb11-4958-2155-00ad-2737a943a07f.png)

デプロイ通知のSlackはよく使いますが、コミットしてくれた人をねぎらうような内容が良いですね :tada:

これで大体完成となり、良いデプロイライフを送ることができますね。


# ブランチごとにデプロイを分ける

上記では単一の環境にデプロイを行うようにしていましたが、僕は開発ブランチごとにデプロイを独立させる方法をオススメしています。
Gitの基本的なワークフロー（ブランチやプルリクエストといった仕組み）を導入すると、ブランチごとに独立して機能追加などを行うことができます。
レビューをする際、そういったブランチごとに追加された機能のみに注目してレビューを行いたいのですが、それぞれでブランチがデプロイされているとレビューを行う人にとって大変便利です。

開発者的にはもっと言えばコミットごとに独立していたほうが良いのですが、そうすると今度は必要となる容量が大きくなってしまうため、その折衷案としてブランチごとのデプロイを推奨しています。（個人的にこれらを Commits go to deploy, Branches go to deployと呼んでいます）

ブランチごとに独立したデプロイを行うためには環境を分ける必要があります。環境とはコア、テーマ、プラグイン、メディアなどのファイル群、データベースです。
ファイルは転送するディレクトリを変えることで分けます。
データベースはプリフィクスを変更することで分けます。

## エックスサーバでMySQLのDB無制限になった！！
なんと最近、エックスサーバではデータベースの上限数が無制限となりました。[^２]
そのため理論上はプリフィックスで分けること無く、それぞれの環境ごとにデータベースを作成することができます。ただ実際にデータベースを作成の際にはウェブコンソールから手動で作成する必要があるため、残念ながら自動化ができません。
SSH越しにデータベースの作成ができればよいのですが、権限が足りないため不可能です。今後何かしらの形で改善するといいですね。

[^2]: https://www.xserver.ne.jp/news_detail.php?view_id=6663


## ブランチごとデプロイ
ブランチごとのテーブルプリフィクスを決定します。ブランチ名を取得させておき、そこからmd5でユニークな文字列とします。

``

```bash:deploy.sh（抜粋）
BRANCH_NAME=`git rev-parse --abbrev-ref HEAD`

# テーブル名のプリフィクス MySQLの最大は64byte
if type "md5" > /dev/null 2>&1; then
  TABLE_PREFIX=$(echo ${BRANCH_NAME} | md5 | cut -c 1-8)_
else
  # CircleCIのubuntuはmd5sumコマンドを使う（本当はelseifしたい)
  TABLE_PREFIX=$(echo ${BRANCH_NAME} | md5sum | cut -c 1-8)_
fi
```

エックスサーバ上には一旦通常のテーブル名で送信しておき、スクリプトでテーブルプリフィクスの変更を行います。

```bash:deploy.sh
# テーブルプリフィクスの変更
${SSH_COMMAND} 'cd '${TARGET_DIR}'; curl -L https://gist.githubusercontent.com/yousan/10b44cea1f3d66ee711cc7161e2d5a8a/raw/d0cd33d0e2fbfad11066628b09af3f325b8255e1/change_db_prefix.sh | bash -s '${TABLE_PREFIX}
```

> WordPressのデータベースプリフィクスを変更するスクリプト - Qiita
> https://qiita.com/yousan/items/b24fb3dc538b28f85111

```bash:bin/deploy.sh
#!/usr/bin/env bash
# 1. 送り先のディレクトリを決定し、なければ作成する
# 2. 構成ファイル（wpコア、プラグイン、テーマ）を送る
# 3. コンフィグファイルの変更
# 4. DBをダンプし、送る
# 5. 通知

set -xe # エラーが有った場合に停止
cd -- "$(dirname "$BASH_SOURCE")" # ファイルがある場所に移動する
cd ../ # リポジトリのルートに移動する


BASE_DIR=/home/example/example.com/public_html/sample/
BASE_URL='https://example.com/sample/'
TARGET_URL=${BASE_URL}
SSH_OPTION="-o StrictHostKeyChecking=no -i ./etc/id_rsa -p 10022"
SSH_HOST="example@svxxxx.xserver.jp"
SSH_COMMAND='ssh '${SSH_OPTION}' '${SSH_HOST}

# 秘密鍵を0600にする
chmod 0600 etc/id_rsa

# テーブル名のプリフィクス MySQLの最大は64byte
if type "md5" > /dev/null 2>&1; then
  TABLE_PREFIX=$(echo ${BRANCH_NAME} | md5 | cut -c 1-8)_
else
  # CircleCIのubuntuはmd5sumコマンドを使う（本当はelseifしたい)
  TABLE_PREFIX=$(echo ${BRANCH_NAME} | md5sum | cut -c 1-8)_
fi

# 送り先ディレクトリの決定
TARGET_DIR=${BASE_DIR}${BRANCH_NAME}

# ここからデプロイのためのコマンドスタート
# デプロイ先にディレクトリを作成
${SSH_COMMAND} 'mkdir -p '${TARGET_DIR}

rsync -avr -e "ssh ${SSH_OPTION}" \
  app/public/ \
  ${SSH_HOST}:${TARGET_DIR}

# コンフィグファイルの上書き
${SSH_COMMAND} 'cd '${TARGET_DIR}'; mv wp-config-xserv.php wp-config.php'

# DBのエクスポートとインポート
cat ./etc/local.sql.gz | gunzip - | \
  ${SSH_COMMAND} 'cd '${TARGET_DIR}'; wp db import -'

# テーブルプリフィクスの変更
${SSH_COMMAND} 'cd '${TARGET_DIR}'; curl -L https://gist.githubusercontent.com/yousan/10b44cea1f3d66ee711cc7161e2d5a8a/raw/d0cd33d0e2fbfad11066628b09af3f325b8255e1/change_db_prefix.sh | bash -s '${TABLE_PREFIX}

${SSH_COMMAND} 'cd '${TARGET_DIR}'; wp option set siteurl ' ${TARGET_URL}
${SSH_COMMAND} 'cd '${TARGET_DIR}'; wp option set home ' ${TARGET_URL}

./bin/slack_deployed.sh ${TARGET_URL}
```

これでコミットされるとデプロイがされるようになります！

# おわりに
WordPressの制作案件でも自動的にデプロイを行えるようになるとアップロードの手間が省け、事故をへらすことができます。
特に中規模以上となる開発者が複数人となる場合には是非オススメです！
今回はエックスサーバで行いましたが、さくらのレンタルなどSSHが使えるサーバであれば大体の環境で可能です。
エックスサーバもさくらのレンタルも、安定していて速度も早いのでとても重宝しています 😊


WordPressに限らず、VueやReactのフロントでも、JavaScriptやGoのサーバサイドでも、ブランチごとにデプロイする「Branches go to deploy」が今後もっと流行っていくといいなと思っています。