Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

GitHub Actions で PHP の CI/CD をする

More than 1 year has passed since last update.

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

2019年12月現在、 EC-CUBE organization アカウントが利用している契約プランの関係 で、 EC-CUBEオフィシャルリポジトリで GitHub Actions は利用できなくなっています。 ご利用の際は、 fork した個人アカウントでお願いします

実現したいこと

  • 以下の環境をマトリクスで実行したい
    • 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 をする環境が充実しているとは言い難い感じですが、いろいろ頑張れば柔軟に対応できそうです。

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

参考

nanasess
Emacs のアイコンを作った人です
https://skirnir.co.jp
ec-cube
日本No.1ECオープンソースのEC-CUBEのコミッターやユーザーのコミュニティです。
https://www.ec-cube.net
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away