3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Laravel Sail】Larastanの導入とGitHub ActionsのCIへの組み込み

Posted at

前提条件

公式ページのインストールガイドを参考に Sail を利用した Laravel アプリケーションのインストールが完了していること。

$ sail php -v

PHP 8.1.13 (cli) (built: Nov 26 2022 14:07:55) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.1.13, Copyright (c) Zend Technologies
    with Zend OPcache v8.1.13, Copyright (c), by Zend Technologies
    with Xdebug v3.1.5, Copyright (c) 2002-2022, by Derick Rethans
$ sail artisan --version

Laravel Framework 9.42.2

本記事におけるプロジェクトのディレクトリ構造は以下の通りとします。

git-repository/
└── my-laravel/
    ├── app/
    ├── bootstrap/
    ├── config/
    ├── database/
    ├── composer.json
    ├── composer.lock
    └── ...

Larastan を導入

ソースコードの文法上の問題点を事前に検出するための方法として、ソースコードの静的解析が挙げられます。
PHP には PHPStan という静的解析ツールがあり、Larastan はこれを Laravel プロジェクト用に拡張したラッパーとなっています。

Larastan を導入することで、先に挙げたソースコードの文法上の問題点を事前に検出できるだけでなく、PSR に基づいたコーディングを強制することによって、チームで開発している場合は個人の力量に依存しない一定の品質を保ったコードを書くことに一役買ってくれます。

Larastan は Composer でインストール可能です。
今回は事前に Laravel Sail がインストールされた環境のため、 sail コマンドで composer を実行します。

$ sail composer require --dev nunomaduro/larastan

インストールが完了していれば ./vendor/bin/phpstan --version で PHPStan のバージョン情報が確認できるはずです。

# コンテナの bash を起動する。
$ sail shell

# インストール確認のためにバージョン情報を表示する。
$ ./vendor/bin/phpstan --version

続いて公式の README に従い phpstan.neon ファイルを作成していきます。
下記ファイル内容は一例のため、解析レベルや解析対象パス、各種ルールなどは PHPStan と Larastan の各種情報を確認し、要件に合う適切な設定を施してください。

my-laravel/phpstan.neon
includes:
  - ./vendor/nunomaduro/larastan/extension.neon

parameters:
  # 解析レベル
  # https://phpstan.org/user-guide/rule-levels
  level: 0

  # 解析対象に含めるパス
  paths:
    - app
    - bootstrap
    - config
    - resources
    - routes
    - tests

  # 解析対象に含めないパス
  excludePaths:
    - routes/console.php

後は PHPStan のバージョンを確認した時と同じように、コンテナの bash を起動して ./vendor/bin/phpstan analyze -- app/ といった具合にコマンドを実行すれば解析できます。

$ ./vendor/bin/phpstan analyze -- app/
Note: Using configuration file /var/www/html/phpstan.neon.
 24/24 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
                                    
 [OK] No errors                                                                      

余談 : Larastan を artisan コマンド化する

Larastan の実行はできるようになりましたが、上述した方法ですと毎回コンテナの bash を起動する一手間があり面倒だったので sail artisan larastan といった形で artisan コマンド化しました。

まずは Command クラスのスケルトンを作成します。

$ sail artisan make:command LarastanCommand

Larastan を実行できるよう実装していきます。
こちらを参考にさせていただきました。

Larastan に定義できる設定内容は phpstan.neon に記述することとし、 LarastanCommand クラスがパースできるオプションは非常に簡素なものとしています。
この辺りは使用感に合わせて追々拡充させていけば良いと考えています。

my-laravel/app/Console/Commands/LarastanCommand.php
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Contracts\Console\Isolatable;
use RuntimeException;
use Symfony\Component\Process\Exception\ProcessSignaledException;
use Symfony\Component\Process\Process;

class LarastanCommand extends Command implements Isolatable
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'larastan
        {--without-tty : Disable output to TTY}
    ';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Run the appliation static analyze by Larastan';

    /**
     * Create a new command instance.
     * 
     * @return void
     */
    public function __construct()
    {
        parent::__construct();

        $this->ignoreValidationErrors();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $command = ['vendor/bin/phpstan', 'analyze', '--', 'app/'];

        $process = new Process($command);
        try {
            $process->setTty(!$this->option('without-tty'));
        } catch (RuntimeException $e) {
            $this->output->writeln('Warning: ' .  $e->getMessage());
        }

        $exitCode = Command::FAILURE;
        try {
            $exitCode = $process->run(fn ($type, $line) => $this->output->write($line));
        } catch (ProcessSignaledException $e) {
            if (extension_loaded('pcntl') && $e->getSignal() !== SIGINT) {
                throw $e;
            }
        }
        $this->newLine();

        return $exitCode;
    }
}

artisan コマンドとして実行してみて、先ほどと同様の解析結果になることを確認します。

$ sail artisan larastan
Note: Using configuration file /var/www/html/phpstan.neon.
 24/24 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%
                                      
 [OK] No errors                                                                   

GitHub Actions への組み込み

Larastan を実行することへの面倒臭さは解消されました。
ただ現時点ではまだ手動実行のため、そもそも実行することを忘れる可能性があります。

Git フックを利用して pre-commit や pre-push のタイミングで解析を行うのも一案ですが、開発体験はむしろ悪化する恐れがある上に、チームで開発している場合などは、ローカルに設定する方法では信用に足らないと思います。

上記の課題を解決すべく GitHub Actions で CI/CD ワークフローを構築し「GitHub の master ブランチにプッシュされたら Larastan を実行する。」といった状態を目指していきます。

GitHub Actions で実行するワークフローは、リポジトリ内に配置した .github/workflows ディレクトリ配下の yml ファイルにて定義できます。
本記事における yml ファイル作成後のディレクトリ構成、yml ファイルの内容については以下の通りです。

git-repository/
├── .github/
|   └── workflows/
|       └── my-laravel-ci.yml
└── my-laravel/
    ├── app/
    ├── bootstrap/
    ├── config/
    ├── database/
    ├── composer.json
    ├── composer.lock
    └── ...
.github/workflows/my-laravel-ci.yml
# GitHub Actions 上での当該ワークフロー名です。
# 識別しやすい名称を付けてください。
name: my-laravel-ci

# 当該ワークフローの実行タイミングに関する定義です。
# ここでは main ブランチの my-laravel ディレクトリ配下にある PHP ファイルに対して push が行われたタイミングとしています。
# トリガーや対象ブランチ、パス定義などは変更可能なため、プロジェクトの性格に合わせて変更すべきポイントです。
on:
  push:
    branches: 
      - "main"
    paths:
      - "my-laravel/**.php"

jobs:
  # ubuntu-latest 仮想マシン上で以下の流れでワークフローを実行していきます。
  #   1. 対象プロジェクトをチェックアウト。
  #   2. sail コマンドでアプリケーションコンテナ(+α)を起動。
  #   3. Larastan を実行。
  #   4. PHPUnit を実行。 
  test:
    runs-on: ubuntu-latest

    defaults:
      run:
        working-directory: ./my-laravel

    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Copy .env
        run: php -r "file_exists('.env') || copy('.env.example', '.env');"
      - name: Install Dependencies
        run: composer install -q --no-ansi --no-interaction --no-scripts --no-progress --prefer-install=dist
      - name: Start application
        run: vendor/bin/sail up -d
      - name: Generate key
        run: vendor/bin/sail artisan key:generate
      # 本記事で実装した LarastanCommand クラスを `sail artisan` で呼び出すことで Larastan を実行しています。
      - name: Execute lint via Larastan
        run: vendor/bin/sail artisan larastan --without-tty
      - name: Execute test via PHPUnit
        run: vendor/bin/sail artisan test

yml ファイルを commit 後 my-laravel プロジェクトの PHP ファイルに変更を加えて commit & push をしてみるとワークフローが実行されます。
GitHub 上で test ジョブの実行履歴を開くと、無事 Larastan が実行されていることがわかります。(スクショのワークフロー名が異なっているのはご容赦ください。)

chrome_screenshot_github.com.png
chrome_screenshot_github.com (1).png

一度 GitHub Actions への登録が済んでしまえば、以降は Larastan の実行を意識することなく開発に専念できるため非常に便利だと感じました。

他にも CI/CD ツールはたくさんありますが、 GitHub でコード管理をしていれば別ツールやサービスを利用せずとも CI/CD を始められるため、取っ掛かりとしての心理的ハードルも低いと思います。
GitHub Actions は他にも様々な機能が提供されているので、使いこなせるよう勉強していきたいです🖋

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?