開発に関わるタスクとして手動で実行するコマンドは日に日に増える一方です。Laravelのシンプルなプロジェクトでも、コード作成後にはpint(コード自動整形ツール)、PHPStan(静的解析ツール)、そしてpest(テスト)を必ず実行します。また、ブラウザで動作チェックするためにローカル環境を立ち上げるコマンドたち、さらに、gitのようなバージョン管理のためのコマンド実行もあります。問題はこれらのコマンドすべてを覚えるのは大変なのと、いくつかのコマンドは1つ1つタイプして実行するより、まとめて実行して簡素化もしたいです。つまり、タスクを整理するツールは必須です。今回はこれらのツールの紹介です。

開発に必要なタスクの種類

まず、私の開発環境をもとにして、どのようなタスクがあるかグループして整理してみます。

コード作成後に実行するタスク

  • コード自動整形ツール:pint、rector
  • 静的解析ツール:phpstan
  • テストツール:phpunit、pest
  • バージョンコントロール:git commit, push, branch, mergeなど

ローカル開発環境を立ち上げるタスク

  • コンテナ―の立ち上げ:sail up
  • ウェブサーバー:php artisan server
  • CSSやスクリプトのビルド・ホットリロード:npm run dev

これらを実行して、ブラウザーでの動作確認が可能となります。

インストール作業のためのタスク

  • バージョンコントロール:git pull
  • ライブラリのインストール: composer install
  • データーベース変更:php artisan migrate
  • キャッシュの作成:php artisan config:cache, event:cache, route:cache

他にも、ログのチェックのために単に、less storage/logs/laravel.logの実行とかもありますね。

さて、これらを整理するツールを紹介です。

composer

composerは基本的にはphpのプロジェクトで使用するパッケージを管理するコマンドですが、新規のLaravelプロジェクトをインストールすると、以下のように、composer.json内に”scripts”のセクションがあり、いくつかのタスクがグループされて実行を可能としています。

{
...
   "scripts": {
        "post-autoload-dump": [
            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
            "@php artisan package:discover --ansi"
        ],
        "post-update-cmd": [
            "@php artisan vendor:publish --tag=laravel-assets --ansi --force"
        ],
        "post-root-package-install": [
            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
        ],
        "post-create-project-cmd": [
            "@php artisan key:generate --ansi",
            "@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"",
            "@php artisan migrate --graceful --ansi"
        ],
        "dev": [
            "Composer\\Config::disableProcessTimeout",
            "npx concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
        ],
        "test": [
            "@php artisan config:clear --ansi",
            "@php artisan test"
        ]
    },
...

例えば、

$ composer dev

を実行すれば、以下のコマンドを、

  • ウェブサーバーの起動:php artisan server
  • キューを継続的にモニター・処理:php artisan queue
  • ログをモニター:php aritsan pail
  • CSSやスクリプトのビルド・ホットリロード:npm run dev

皆いっぺんに並行実行します。以下は実行時のコマンド画面です。

つまり、composer.jsonのファイルを編集して、自由に必要なタスクのコマンドをグループ化して実行が可能というわけです。

npm

npmは、composerと違ってphpではなくjs関連のパッケージの管理ツールですが、composerと同様に、package.jsonに”script”のセクションがあり、先のようなグループタスクを以下のように設定が可能です。

{
    "private": true,
    "type": "module",
    "scripts": {
        "build": "vite build",
        "dev": "vite",
        "start": "concurrently -c \"#93c5fd,#c4b5fd,#fb7185,#fdba74\" \"php artisan serve\" \"php artisan queue:listen --tries=1\" \"php artisan pail --timeout=0\" \"npm run dev\" --names=server,queue,logs,vite"
    },
...

実行は、以下のコマンドとなります。

$ npm run start

make

C言語のプログラマー(昔は誰しもそうであった)ならとても馴染みのあるmakeコマンドは、タスクコマンドの整理にも使用できます。

makeの典型的な使用は、まずMakefileを作成して、そこでターゲットとソースコードの依存を指定し、それを作成するためのコマンドを記述します。

例えば、以下では、

foo: foo.c
	cc foo.c -o foo

1行目は、ターゲットは実行可能なファイル、fooであり、その生成のためにC言語のソースコード、foo.cに依存することを示します。
もし、foo.cがfooより古いあるいは存在しないなら、2行目以降に指定されたコマンドを実行します。そこでは、ccはCのコンパイラーが記述されていて、-oで生成されるバイナリーのファイル名を指定されています。
この2行目では必ず、行先頭にタブ文字を含むことが必要です。空白文字ではだめです。結構センシティブなので注意を。

この実行は、

$ make

となります。

さて、このMakefileでは、コンパイルでなくとも他のタスクの指定にも使うことが可能です。以下では、先のcomposer.jsonやpackage.jsonのように、ブラウザチェックのために必要なコマンドの起動を指定します。

.PHONY: dev

dev:
	npx concurrently -c "#93c5fd,#c4b5fd,#fb7185,#fdba74" \
	"php artisan serve" \
	"php artisan queue:listen --tries=1" \
	"php artisan pail --timeout=0" \
	"npm run dev" \
	--names=server,queue,logs,vite

最初の.PHONY: devは、devが先のCソースコードのコンパイルの例とは異なりターゲットがファイルではないこと意味します。
また、dev:の行は、ターゲット名のみであり、その依存元はありません。そして、続く行は実際に実行されるコマンドです。これはcomposerやnpmと同様です。
しかし、バックスラッシュを使用して改行できたり、コマンドすべてを引用符で囲む必要もないので読みやすくできます。

私がmakeが好きなのは、開発のタスクの実行コマンドを、パッケージ管理が使用目的のcomposer.jsonやpackage.jsonから明確に分けることができること、そしてbashなどのシェルスクリプトでよく使われる変数、条件式、ループを取り入れることも可能です。、さらにLaravelで使用する.envも読み込むことも可能なことです。

例えば、.envと.env.exampleを比べて使う変数名に漏れがないか調べるには、Makefileを以下のように設定できます。

include .env

ENV_EXAMPLE=`grep '=' .env.example | awk -F'=' '{ print $$1}' | sort -u`
ENV_ACTUAL=`grep -v '^\#' .env | grep '=' | awk -F'=' '{ print $$1}' | sort -u`

env:
	for i in $(ENV_ACTUAL); do \
		if ! grep -q "^$$i=" .env.example; then echo "not found $$i in .env.example"; fi; \
	done; \
	for i in $(ENV_EXAMPLE); do \
		if ! grep -q "^$$i=" .env; then echo "not found $$i in .env"; fi; \
	done
メルマガ購読の申し込みはこちらから。

By khino