Github Actionsによる継続的インテグレーションおよび自動デプロイ
Githubでソースコードを管理している場合、GitHub ActionsというCI/CD(継続的インテグレーション・継続的デプロイ)ツールを利用することができます。このツールを利用してWordPressの公式ディレクトリにデプロイする方法は10upが紹介(英語記事)しています。
しかしながら、受託制作や販売向け(プレミアム)のテーマ・プラグインを開発している場合、プライベートリポジトリでの運用になることが多いでしょう。Travis CIやCircle CI(Kunoichiの関連記事)では、パブリックでないGitHubリポジトリに対する連携は有料プランへの加入が必須となります。また、デプロイ先もプロジェクトごとに異なることが多く、確かな知識が要求されます。
そこで、本項では次のような方に向け、GitHub Actionsの基本的なやり方を説明します。
- GitHubのプライベートリポジトリを利用している。
- 様々なデプロイ先がある
GitHub Actionsの基本
GitHub Actionsはリポジトリのルートに .github/workflows/wordpress.yml
といったファイルを設置することで自動的に動作します。
一連の自動処理をワークフローと呼び、ワークフローはジョブの集合、ジョブはステップの集合です。
- ステップ 一つ一つの処理。たとえば、PHPUnitでテストを走らせる、composerをインストールするなど。
- ジョブ ステップを複数集めたもの。たとえば、Docker環境の設定からPHPUnitでのテスト終了までを一つのジョブ test とするなど。
- ワークフロー ジョブのかたまり。
設定ファイル .github/workflows/wordpress.yml
を視覚的に見ると、次のようになります。
それぞれのジョブにはマトリックスを設定することができます。たとえば、ユニットテストをWordPress 5.0〜5.3、PHP 5.6〜7.3まで20パターン行い、デプロイをWordPress 5.3かつPHP7.3で行うといったようなことが可能です。
GitHub Actionsの実例
それでは、実際のワークフローファイルをみながら、何をしているのかを説明していきます。下記のワークフローでは、次の処理を行なっています。
- masterブランチおよびタグ、プルリクエストでワークフローを実行。
- WordPress5.0〜最新、PHP7系でユニットテストを実行。
- ユニットテストがすべて成功し、なおかつタグがつけられていたら、リリースビルド(ZIP)を作成。
- ZIPを作ったら、GitHubのリリースとしてアップロードする。
name: Kunoichi Theme Test
# ワークフロー発動条件の確認
on:
push:
branches:
- master
tags:
- '*'
pull_request:
branches:
- master
# ジョブとして、testとreleaseを設定しています。
jobs:
test: # ユニットテストを実行します。
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system: [ ubuntu-20.04 ] # OSの設定。新しいPHPだけサポートするならubuntu-latestでよいでしょう。
php: [ '7.2', '7.4', '8.0' ] # PHPバージョンの指定
wp: [ 'latest', '5.9' ] # WordPressバージョン
services:
mysql: # MySQLの利用を宣言
image: mysql:8
options: --health-cmd "mysqladmin ping -h localhost" --health-interval 20s --health-timeout 10s --health-retries 10
env:
MYSQL_ROOT_PASSWORD: root # MySQLのパスワードはGitHub Actionsだと 'root' に設定されるようです。
name: WordPress ${{ matrix.wp }} in PHP ${{ matrix.php }} UnitTest
steps:
- uses: actions/checkout@master
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php }}
tools: composer
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Validate composer.json and composer.lock
run: composer validate
- name: Install dependencies
run: composer install --prefer-dist --no-progress --no-suggest
- name: Start MySQL
run: |
sudo systemctl start mysql
mysql -h 127.0.0.1 --port 3306 -u root --password=root -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'root';"
- name: Install WordPress
run: bash bin/install-wp-tests.sh wordpress root root 127.0.0.1 ${{ matrix.wp }}
- name: Run test suite
run: composer test
release: # リリース用ビルド(zipファイル)を作成し、GitHubにアップロードします。
name: Build Plugin
needs: test # needsを設定すると、testジョブが成功しない限り実行されません。
if: contains(github.ref, 'tags/') # タグが指定されるときだけ実行
runs-on: ubuntu-20.04
strategy:
matrix:
zip: [ 'hakama' ] # Zipファイルの名前。
steps:
- uses: actions/checkout@master
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 7.2
tools: composer
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Install NPM
uses: actions/setup-node@v1
with:
node-version: '16'
- name: Build package.
run: bash bin/cleanup.sh # クリーンアップ(e.g. テスト用ディレクトリを消す)はシェルスクリプトとして用意すると便利です。
- name: Deploy Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # これは自動で入ってくるので、特に登録する必要はありません。
with:
tag_name: ${{ github.ref }}
release_name: Release ${{ github.ref }}
body: |
Release ${{ github.ref }}
draft: false
prerelease: false
- name: Create Zip
run: zip -r ${{ matrix.zip }}.zip ./
- name: Upload Release Zip
id: upload-release-asset
uses: actions/upload-release-asset@v1.0.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ steps.create_release.outputs.upload_url }} # "Deploy Release"で作成したURLです。
asset_path: ./${{ matrix.zip }}.zip
asset_name: ${{ matrix.zip }}.zip
asset_content_type: application/zip
このファイルの見方を簡単に説明します。
- それぞれのステップにある
uses
という項目は、ライブラリとして提供されているアクションです。PHPのインストール、Nodeのインストールなどの複雑な処理はだいたいライブラリにあるため、それを利用します。env
やwith
はライブラリを利用するにあたっての引数のようなものだと考えてください。 run
はコマンドの実行です。テストの実行、デプロイ用のディレクトリ整理、zip作成などはコマンドラインで行います。1ステップあたり1つのコマンドしか実行できません。上記で紹介した通り、定型的な処理はシェルスクリプトにまとめておきましょう。CI/CDツールのデバッグは難しいので、ローカルでも実行できると便利です。- 2020年3月3日より、GitHub Actionsの仮想マシンでMySQLがデフォルトで起動しなくなりました(github blog)。したがって、MySQLの起動コマンド
sudo systemctl start mysql
を追加しています。 - MySQL 8.0.4以上のバージョンでは、デフォルトの認証システムがパスワードではなく
caching_sha2_password
になりました。この認証方式に対応していないPHPバージョンで同様に動作するために、MySQLの起動スクリプトの直後にパスワードを認証方式として追加するスクリプトを挿入しています。 - PHPのバージョン7.2未満ではcomposer 2.3が動かないこと、また、PHP7.4がサポート切れになったこと、WordPress 5.9でPHPUnitの動作対象が変更になったことなどから、OS, PHP, WordPressのバージョンを変更しております。
- PHPをセットアップするActionライブラリは
shivammathur/setup-php@v2
に変更しました。このアクションではGitHubトークンを設定することで、Composerのレイト・リミット制限を緩和できます。
GitHub Actionsによるカスタムデプロイ
さて、上記ではGitHubリリースにZIPをデプロイして完了しています。では、たとえば次のような場合はどうしたらよいでしょうか。
- masterブランチへのコミットは、テストが通ったら開発サーバーにアップロードする。
- さらに、タグが付いていたら本番サーバーにアップロードする。
この場合、ジョブは3つになります。
name: Kunoichi Theme Test
# 中略
# ジョブとして、test, staging, productionを設定しています。
jobs:
test: # ユニットテストを実行します。
# 中略
staging: #開発環境にアップロード
name: Upload Staging
needs: test # testジョブが成功しない限り実行されません。
if: contains(github.ref, 'master') # masterブランチだけ
steps: # 中略
production: #本番環境にアップロード
name: Upload Production
needs: test # testジョブが成功しない限り実行されません。
if: contains(github.ref, 'tags/') # タグが付いているときだけ
steps: # 中略
あとはsteps
の中に何を書くかです。GitHub Actionsでは、それぞれのジョブで環境やデータの受け渡しをすることが可能なのですが、本項では「毎回Docker環境をビルドする」という冗長な方法をとります。
基本的には上記で紹介したZIP作成の直前までと同じステップを経て、rsyncなどを利用してサーバーにアップロードするのですが、アップロードのための変数はどうしたらよいのでしょうか?
GitHub Actionsには暗号化されたシークレットという機能があり、たとえばサーバーに接続するための秘密鍵・パスワードなどのセンシティブな情報を保存しておくことができます。
こうして作成したシークレットは、ステップ内で変数として利用できます。
steps:
- name: Dump SSH Key
run: # コマンドを実行
env: # CLIでの変数名: ${{ secrets.(GitHubで登録した名前) }} の形です。
KUNOICHI_STAGING_SECRET_KEY: ${{ secrets.KUNOICHI_STAGING_SECRET_KEY }}
あとはステップ内でこの鍵をファイルに保存し、SSH接続で使える様にします。パーミッションを600にする必要があることに注意しましょう。
steps:
- name: Dump SSH Key
run echo "$KUNOICHI_STAGING_SECRET_KEY" > staging.pem && chmod 600 staging.pem
env:
KUNOICHI_STAGING_SECRET_KEY: ${{ secrets.KUNOICHI_STAGING_SECRET_KEY }}
それでは、現在のディレクトリをrsyncなどでアップロードしましょう。
steps:
- name: Dump SSH Key
# 中略
- name: Rsync Deploy
run: rsync -rpv --delete --checksum --exclude='staging.pem' -e "ssh -i staging.pem -o StrictHostKeyChecking=no" ./ server-user-name@ssh.example.com:/var/www/wordpress/wp-content/themes/my-theme/
たとえばプレミアムテーマの開発者ならば、デモサイトなどにmasterブランチをアップロードしておくと常に最新の進捗を表示できます。ユーザーへの新機能予告などに活用しましょう。
Kunoichiでは、現在販売中のテーマ・プラグインを自動的にリリースできるよう機能開発中です。ユーザーにとっても、開発者にとっても、素早くリリースされることは大きな価値になるでしょう。完成をお楽しみに!