Edited at

GitHub Actions で PHP の CI/CD をする

EC-CUBE の CI/CD を GitHub Actions で実装してみたので、メモ程度に知見をまとめてみます。

該当の Pull Request はこちら

- 4系 https://github.com/EC-CUBE/ec-cube/pull/4337

- 2系 https://github.com/EC-CUBE/eccube-2_13/pull/312


実現したいこと


  • 以下の環境をマトリクスで実行したい


    • Linux, Windows

    • PHP5.4〜7.3

    • PostgreSQL/MySQL/SQLite3



  • PHPUnit によるユニットテスト

  • Codeception による E2Eテスト

EC-CUBE は、Travis CI 及び AppVeyor で CI を実行しており、これを GitHub Actions へ移植することにします


直面した課題


GitHub Actions にインストールされている PHP バージョン


  • ubuntu-18.04 は PHP7.1〜7.3

  • ubuntu-16.04 は PHP5.6〜7.3

  • windows-2019, windows-2016 は PHP7.3

上記のような状況ですので、 PHP5.4, 5.5 の環境は自力で構築する必要があります。

GitHub Actions は、独自の Actions を作成できますので、 PHP5.4〜7.3 を Linux, Windows でマトリクス実行する Actions を作成しました。

Setup PHP environment

以下のような感じで実行できます


jobs:
build:
runs-on: ${{ matrix.operating-system }}
strategy:
matrix:
operating-system: [ ubuntu-18.04, windows-2019 ]
php: [ '5.4', '5.5', '5.6', '7.1', '7.2', '7.3', '7.3.3' ]
name: PHP ${{ matrix.php }} sample
steps:
- uses: actions/checkout@master
- name: Setup PHP
uses: nanasess/setup-php@master
with:
php-version: ${{ matrix.php }}
- run: php my_script.php


  • Linux で apt の利用可能なバージョンは apt を使用


    • apt が利用できない場合は phpenv でビルド



  • Windows 環境は chocolatey を利用

  • ubuntu-16.04 は標準パッケージと小々異なる構成(OpenSSL1.1 がインストールされている)のため、 OpenSSL1.0 と libpq.so をソースから構築している

独自の Actions を作りたい場合は、 JavaScript または Docker コンテナを使用する必要があります。 actions/typescript-action テンプレートや actions/javascript-action, actions/hello-world-docker-action が利用できます。


Windows 環境は Composer がインストールされていない

こちらも独自の Actions を作成しました

Composer installer of Github Actions


ChromeDriver の実行

Codeception を実行するために ChromeDriver を設定する必要があります。

以下のような step を作成しました

    - name: Setup chromedriver

run: |
sudo apt-fast install -y xvfb debconf-utils screen google-chrome-stable
wget -c -nc --retry-connrefused --tries=0 http://chromedriver.storage.googleapis.com/2.43/chromedriver_linux64.zip
unzip -o -q chromedriver_linux64.zip
export DISPLAY=:99
./chromedriver --url-base=/wd/hub &
echo ">>> Started chrome-driver"
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &

ここで注意する必要があるのが、環境変数 DISPLAY の設定です。

jobs.<job_id>.steps.env で設定しても、 ChromeDriver が動きません。

export で設定する必要があります。

ちなみに、ファイルダウンロードのテストをする必要があるため、ヘッドレスモードは使用しません。

2019年10月3日追記 Actions を作成しました

nanasess/setup-chromedriver

こんな感じで記述できます

    - name: setup-chromedriver

uses: nanasess/setup-chromedriver@master
with:
chromedriver-version: '77.0.3865.40'

- name: Run chromedriver
run: |
export DISPLAY=:99
chromedriver --url-base=/wd/hub &
echo ">>> Started chrome-driver"
sudo Xvfb -ac :99 -screen 0 1280x1024x24 > /dev/null 2>&1 &
echo ">>> Started xvfb"


Windows 環境での MySQL のテスト

Linux 環境では MySQL Server が動いているのですが、 Windows 環境ではクライアントしかインストールされてないため、自前でインストールします

    - name: Setup to database

run: |
choco install -y mysql --version 5.7.18
mysql --user=root -e "CREATE DATABASE `myapp_test`;"
mysql --user=root -e "ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';FLUSH PRIVILEGES;"
mysql --user=root --password=password -h 127.0.0.1 -e "SELECT version();"


PostgreSQL のテスト

Linux では jobs.<job_id>.services で PostgreSQL を実行できます

    services:

postgres:
image: postgres:11
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_DB: postgres
ports:
- 5432:5432
# needed because the postgres container does not provide a healthcheck
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5

参考 https://github.com/actions/example-services/blob/master/.github/workflows/postgres-service.yml


MailCatcher を使用したテスト

Linux では jobs.<job_id>.services で Docker コンテナを実行します

    services:

mailcatcher:
image: schickling/mailcatcher
ports:
- 1080:1080
- 1025:1025

Windows は gem でインストールします。

shell: bash を指定するのがポイント。デフォルトの cmd ではバックグラウンドで実行できません。。。

    - name: Setup MailCatcher

run: gem install -N mailcatcher -v 0.6.5
shell: bash
- name: Run to MailCatcher
run: mailcatcher &
shell: bash


マイクロサービスの実行

EC-CUBE の E2E テストでは、 EC-CUBEオーナーズストア をエミュレートした Docker コンテナを実行し、プラグインインストールなどのテストをしています。

これを jobs.<job_id>.services で動かしたかったのですが、、 Volume マウントするディレクトリが services を初期化する段階では生成できないため、 docker run コマンドで実行するしかないようです

    ## $ {PWD}/repos does not exist so service cannot be started

- name: Run package-api
run: docker run -d --rm -v ${PWD}/repos:/repos -e MOCK_REPO_DIR=/repos -p 8080:8080 eccube/mock-package-api

本当は以下のように書きたい

    services:

package-api:
image: eccube/mock-package-api
env:
MOCK_REPO_DIR: '/repos'
ports:
- 8080:8080
volumes:
- $GITHUB_WORKSPACE/repos:/repos


イベントのフィルタリング


  • Pull Request を master にマージした場合にワークフローが2つ走らないようにしたい


  • *.md ファイルのみの修正はワークフローを実行しないようにしたい

  • リリースを発行した時にワークフローを実行する

  • tag を打った時にワークフローを実行する

  • 基本的に直接 push しない

以下のようにすることで、うまく動作しています

on:

push:
branches:
- master
tags:
- '*'
paths:
- '**'
- '!*.md'
pull_request:
paths:
- '**'
- '!*.md'
release:
types: [ published ]


デプロイする

GitHub Actions には AWS CLI や Azure CLI コマンドもインストールされているため、各種クラウドへのデプロイも柔軟にできます。

ここでは、 GitHub にて release の発行をすると、自動的に ZIP 及び tar.gz のパッケージを生成し、 Assets にアップロードするようにしました。

スクリーンショット 2019-09-28 0.59.27.png

コードは長いので こちら を参照してください。

ポイントは、以下のようにすることで、タグ名をパッケージのファイル名にできます

    - name: Packaging

working-directory: ../
env:
TAG_NAME: ${{ github.event.release.tag_name }}
REPOSITORY_NAME: ${{ github.event.repository.name }}
run: |
tar czfp eccube-$TAG_NAME.tar.gz $REPOSITORY_NAME
zip -ry eccube-$TAG_NAME.zip $REPOSITORY_NAME 1> /dev/null

github.event オブジェクトには、多くの情報が格納されています。

以下のようにして参照できます

    - name: Dump GitHub context

env:
GITHUB_CONTEXT: ${{ toJson(github) }}
run: echo "$GITHUB_CONTEXT"
- name: Dump job context
env:
JOB_CONTEXT: ${{ toJson(job) }}
run: echo "$JOB_CONTEXT"
- name: Dump steps context
env:
STEPS_CONTEXT: ${{ toJson(steps) }}
run: echo "$STEPS_CONTEXT"
- name: Dump runner context
env:
RUNNER_CONTEXT: ${{ toJson(runner) }}
run: echo "$RUNNER_CONTEXT"
- name: Dump strategy context
env:
STRATEGY_CONTEXT: ${{ toJson(strategy) }}
run: echo "$STRATEGY_CONTEXT"
- name: Dump matrix context
env:
MATRIX_CONTEXT: ${{ toJson(matrix) }}
run: echo "$MATRIX_CONTEXT"


カバレッジを計測する

GITHUB_TOKEN でカバレッジも連携できたら便利ですよね。

Coveralls GitHub Action は LCOV 型式のみのサポートの模様。

PHPUnit から LCOV 形式のレポートは出力できないため、 PHP のプロジェクトを secrets.GITHUB_TOKEN で連携するのは今のところ難しそうです。

(Coveralls.io の token を登録すれば、 php-coveralls で連携できます)

codecovGITHUB_TOKEN での連携はサポートされてない模様


まとめ

まだパブリックベータ版ということもあり、 PHP で CI/CD をする環境が充実しているとは言い難い感じですが、いろいろ頑張れば柔軟に対応できそうです。

またナレッジがたまったら追記します


参考