Kunoichi Marketドキュメント

Githubでソースコードを管理している場合、GitHub ActionsというCI/CD(継続的インテグレーション・継続的デプロイ)ツールを利用することができます。このツールを利用してWordPressの公式ディレクトリにデプロイする方法は10upが紹介(英語記事)しています。

しかしながら、受託制作や販売向け(プレミアム)のテーマ・プラグインを開発している場合、プライベートリポジトリでの運用になることが多いでしょう。Travis CIやCircle CI(Kunoichiの関連記事)では、パブリックでないGitHubリポジトリに対する連携は有料プランへの加入が必須となります。また、デプロイ先もプロジェクトごとに異なることが多く、確かな知識が要求されます。

そこで、本項では次のような方に向け、GitHub Actionsの基本的なやり方を説明します。

  • GitHubのプライベートリポジトリを利用している。
  • 様々なデプロイ先がある

GitHub Actionsの基本

GitHub Actionsはリポジトリのルートに .github/workflows/wordpress.yml といったファイルを設置することで自動的に動作します。

GitHub Actionsがこのサイトのテーマで動作している様子。

一連の自動処理をワークフローと呼び、ワークフローはジョブの集合、ジョブはステップの集合です。

  • ステップ 一つ一つの処理。たとえば、PHPUnitでテストを走らせる、composerをインストールするなど。
  • ジョブ ステップを複数集めたもの。たとえば、Docker環境の設定からPHPUnitでのテスト終了までを一つのジョブ test とするなど。
  • ワークフロー ジョブのかたまり。

設定ファイル .github/workflows/wordpress.yml を視覚的に見ると、次のようになります。

Kunochiで利用しているワークフロー

それぞれのジョブにはマトリックスを設定することができます。たとえば、ユニットテストを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のインストールなどの複雑な処理はだいたいライブラリにあるため、それを利用します。envwithはライブラリを利用するにあたっての引数のようなものだと考えてください。
  • 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には暗号化されたシークレットという機能があり、たとえばサーバーに接続するための秘密鍵・パスワードなどのセンシティブな情報を保存しておくことができます。

リポジトリ > Settings > Secretsから追加できます。

こうして作成したシークレットは、ステップ内で変数として利用できます。

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では、現在販売中のテーマ・プラグインを自動的にリリースできるよう機能開発中です。ユーザーにとっても、開発者にとっても、素早くリリースされることは大きな価値になるでしょう。完成をお楽しみに!