プルリク時 Github Actions で PHPStan PHPUnit を走らせる
エンジニアの柿添です。
全国的に猛暑日が続き、夏本番がやってきたんじゃないでしょうか。
学生の頃の楽しい思い出をたくさん思い出す季節ですね、
行き先決めずにドライブに行き、ぼーっと入道雲を眺めていたいです。
温泉も良いですね、黒川温泉に行きたい。
さて、 前回の記事 では pre-commit側で
- PHPStan による静的解析
- PHPUnit による自動テスト
- PHP-CS-Fixerによるコード整形
などを行いました。
commit を打つ前にコードを整形したりなんやらで品質を上げるのが狙いでした。
今回は Github Actions 側で PHPStan を走らせる記事になります。
Github Actions って何?
そもそも何なの?という話ですが、
公式の GitHub Actions を理解する が分かりやすいかと思います。
Github Actions 要約
- GitHub Actionsは、ビルド、テスト、デプロイの流れを自動化するCI/CDプラットフォーム
- プルリクエストのビルドやテスト、運用環境へのデプロイなどのワークフローが可能
- リポジトリの様々なイベントに反応してワークフローを実行でき、例えば新しいissueの作成時に自動でラベルを付けることもできる
- Linux、Windows、macOSの仮想マシンがワークフロー実行のために提供されている
- 独自のデータセンターやクラウドでセルフホストランナーを利用することも可能
たくさんの トリガー からワークフローを実行することができる素晴らしいサービスです。
無料枠 もありますので要確認です!
プロジェクトに Github Actions の workflow を仕込もう
前回プロジェクトの構成を変更したと書きましたが、以下のように .github
ディレクトリにワークフローの設定ファイルを配置します。php_checks.yml
という(安易な)名前にしております。
├── .githooks
│ ├── pre-commit
├── .github
│ └── workflows
│ └── php_checks.yml # これです!
├── infrastructure
│ ├── docker
│ │ ├── db
│ │ ├── php
│ ... ...
├── src
...
PHPStan PHPUnit 考慮の workflow php_checks.yml
# GitHub Actions Workflow 名
name: PHP Checks
on:
# 以下のイベントでワークフローが実行されるように指定
# push:
# branches:
# - main
# - master
# - develop
pull_request:
types:
- opened
- synchronize
- reopened
jobs:
php_checks:
# ジョブの名前
name: PHP Checks (PHP ${{ matrix.php-versions }} on ${{ matrix.operating-system }})
# 実行環境
runs-on: ${{ matrix.operating-system }}
services:
# MySQL サービスの設定
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: (MySQLのパスワード)
MYSQL_DATABASE: test_db
MYSQL_ALLOW_EMPTY_PASSWORD: 'yes'
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: false
# マトリックス戦略
matrix:
operating-system: [ ubuntu-latest ]
php-versions: [ '8.1' ]
steps:
# 作業ディレクトリを設定
- name: Set working directory
run: echo "WORKING_DIRECTORY=src" >> $GITHUB_ENV
# リポジトリのコードをチェックアウト
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 100
ref: ${{ github.head_ref }}
# jq をインストール
- name: Install jq
run: sudo apt-get install -y jq
# 変更されたファイルのリストを取得
- name: Get list of changed files
id: get-changed-files
run: |
PR_NUMBER=$(echo $GITHUB_REF | awk 'BEGIN { FS = "/" } ; { print $3 }')
FILES=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/pulls/$PR_NUMBER/files" | \
jq -r '.[].filename' | paste -sd "," -)
echo "Changed files: $FILES"
echo "FILES=$FILES" >> $GITHUB_ENV
# 変更が含まれているファイルの中に PHP ファイルがあるか確認
- name: Check for PHP files in the changes
id: check-php
run: |
echo "FILES=${FILES}"
if echo "${FILES}" | tr ',' '\n' | grep -qE '\.php$'; then
echo "::set-output name=contains_php::true"
else
echo "::set-output name=contains_php::false"
fi
# .env ファイルのコピー
- name: Copy .env
if: steps.check-php.outputs.contains_php == 'true'
run: php -r "file_exists('.env') || copy('.env.testing', '.env');"
# PHP のセットアップ
- name: Setup PHP
if: steps.check-php.outputs.contains_php == 'true'
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, dom, fileinfo, simplexml
coverage: xdebug
# composer のキャッシュディレクトリの取得
- name: Get composer cache directory
if: steps.check-php.outputs.contains_php == 'true'
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
working-directory: ${{ env.WORKING_DIRECTORY }}
# composer の依存関係のキャッシュ
- name: Cache composer dependencies
if: steps.check-php.outputs.contains_php == 'true'
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
# Composer 依存関係のインストール
- name: Install Composer dependencies
if: steps.check-php.outputs.contains_php == 'true'
run: composer install --no-progress --prefer-dist --optimize-autoloader
working-directory: ${{ env.WORKING_DIRECTORY }}
# データベースのセットアップ
- name: Setup Database
if: steps.check-php.outputs.contains_php == 'true'
run: |
mysql -h 127.0.0.1 -u root -phogeh0gePass\& -e 'CREATE DATABASE IF NOT EXISTS test_db;'
# PHPUnit のための DB 接続のセットアップ
- name: Setup DB Connection for PHPUnit
if: steps.check-php.outputs.contains_php == 'true'
run: |
echo "DB_HOST=127.0.0.1" >> $GITHUB_ENV
echo "DB_PORT=3306" >> $GITHUB_ENV
echo "DB_DATABASE=test_db" >> $GITHUB_ENV
echo "DB_USERNAME=root" >> $GITHUB_ENV
echo "DB_PASSWORD=(MySQLのパスワード)" >> $GITHUB_ENV
# PHPStan の実行
- name: Run PHPStan
if: steps.check-php.outputs.contains_php == 'true'
run: vendor/bin/phpstan --error-format=github --configuration=phpstan.neon
working-directory: ${{ env.WORKING_DIRECTORY }}
# PHPUnit テストの実行
- name: Run PHPUnit Tests
if: steps.check-php.outputs.contains_php == 'true'
run: vendor/bin/phpunit
working-directory: ${{ env.WORKING_DIRECTORY }}
ざっとした動きの解説
- ワークフローは「PHP Checks」という名前
- プルリクエストが開かれたとき、同期されたとき、再度開かれたときにトリガーされる
- Ubuntuの最新バージョンでPHP 8.1を使用する想定
- MySQL 8.0 イメージを使用して、MySQL サービスを起動(特定の環境変数とポート設定が含まれている)
- 作業ディレクトリを設定し、ソースコードをチェックアウトする
- 作業ディレクトリはプロジェクトの構造上 src を指定
- jqをインストールし、JSONの操作に使用
- GitHub APIを使用して、変更されたファイルのリストを取得
- 変更が含まれているファイルの中にPHPファイルがあるかどうかを確認
- PHPファイルが変更に含まれている場合のみ、次のステップが実行
- .env ファイルのコピー
- PHPのセットアップ
- Composerの依存関係のキャッシュとインストール
- データベースのセットアップ
- PHPUnit のための DB 接続
- PHPStanによる静的解析
- PHPUnitによる自動テスト
これらを正常に通過しないと PR が merge できなくなります。
※ jq, GitHub API を用いて変更ファイル情報を取得しているのは、squash & force push した際に commit間の差分で変更ファイルを取得する記述 がしっかり動いてくれなかったためです。
※ 記述が甘そうなので要改良
まとめ
PHP-CS-Fixer, PHPStan, PHPUnitを有効活用しよう!の続きでした。
pre-commit でやるよりも導入はさらに容易で、すぐにでも始められます!
( 記述の甘い部分はシステム事業部の誰かがツッコミを入れてくれるはず )