🎻

[PHP] Herokuへのデプロイでrequire-devの依存もインストールする

2020/05/19に公開

課題

Heroku標準のPHP buildpack を使ってPHPのプロジェクトをデプロイする際、実行されるビルドコマンドは

$ composer install --no-dev --prefer-dist --optimize-autoloader --no-interaction

となっています。(参考

つまり、 require-dev で依存しているライブラリはインストールされません。

しかし、ステージング環境としてHerokuを使っている場合など、一時的にアプリのデバッグモードをONにするために require-dev の依存もインストールしたいことがあります。

例えばSymfonyなら、無造作にHerokuの環境変数で APP_ENVdev にしてしまうと、 require-dev で依存しているクラスが見つからず ClassNotFoundError になってしまいます。

解決策

Herokuでは composer install コマンドの実行後に

composer compile --no-dev --no-interaction

という カスタムコマンド が実行される仕様(参考)なので、ここで改めて require-dev の依存をインストールするようにしてあげれば解決できます。

# composer.json
{
    "scripts": {
        "compile": [
            "composer install --prefer-dist --optimize-autoloader --no-interaction",
        ]
    },
}

ただし、これだと普段 APP_ENV=prod で運用しているときにも常に composer install が2回実行されることになり非効率なので、以下のように APP_ENV=dev の場合にのみ実行するようにしておくのがよいでしょう。

{
    "scripts": {
        "compile": [
            "if [ $APP_ENV = 'dev' ]; then composer install --prefer-dist --optimize-autoloader --no-interaction; fi",
        ]
    },
}

ちなみに、データベースのマイグレーションコマンドなんかもこの compile カスタムコマンド内で実行するのがセオリーですね。

{
    "scripts": {
        "compile": [
            "if [ $APP_ENV = 'dev' ]; then composer install --prefer-dist --optimize-autoloader --no-interaction; fi",
            "php bin/console doctrine:migrations:migrate -n --no-debug"
        ]
    },
}

app.jsonscripts.postdeploy でもできる?

この辺を見る限り、composer.jsoncompile カスタムコマンドを使わずに app.json

{
    "scripts": {
        "postdeploy": "composer install --prefer-dist --optimize-autoloader --no-interaction && php bin/console doctrine:migrations:migrate -n --no-debug"
    }
}

みたいな内容で作っておくでも期待どおり動くのかもしれません。が、未確認ですごめんなさい🙏

ちなみに:Symfonyの場合の注意点

注意点1

なお、先に挙げたSymfonyを APP_ENV=dev で動かしたいというケースでは、たとえこの方法で require-dev の依存をインストールするとしても、デプロイの時点で APP_ENV=dev をセットしてしまっていると ClassNotFoundError になります。

なぜなら、1回目の composer install --no-dev の直後に post-install-cmd フックで bin/console cache:clear などを実行するためにSymfonyが起動されてしまうからです。

なので、あくまで普段は APP_ENV=prod にしておいて、いざデバッグしたいときに APP_ENV=dev に変更してアクセスする、というような使い方になります。

これを回避するには、post-install-cmd@auto-scriptscache:clear などを含む)を --no-dev オプションが付いていない場合のみ実行するようにすればよいです。

具体的には以下のように COMPOSER_DEV_MODE 環境変数が 0 でない(= --no-dev が付いていない)場合にのみ composer auto-scripts によって @auto-scripts を実行するようにします。

  "post-install-cmd": [
-     "@auto-scripts"
+     "if [ $COMPOSER_DEV_MODE -ne 0 ]; then composer auto-scripts; fi"
  ]

参考:Symfony4.4をGAE/phpで動かす | polidog lab

注意点2

ここまでの対応だと、Herokuで実際に画面を開いたときに

Mixed Content: The page at 'https://xxx.herokuapp.com' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://xxx.herokuapp.com/_wdt/xxxxxx'. This request has been blocked; the content must be served over HTTPS.

といったエラーによりデバッグツールバーが表示されないかもしれません。

この場合、config/packages/framework.yaml に以下のような設定を追記することでエラーを解消できます。

信頼できるリバースプロキシに対してしかアプリケーションサーバーが応答しないように構成されていることが前提の設定です。Heroku以外の環境で同様の設定を行う場合は十分に注意してください。

when@dev:
  framework:
    trusted_proxies: REMOTE_ADDR

参考:How to Configure Symfony to Work behind a Load Balancer or a Reverse Proxy (Symfony Docs) #but-what-if-the-ip-of-my-reverse-proxy-changes-constantly

まとめ

  • PHPプロジェクトをHerokuへデプロイする際にrequire-devの依存もインストールしたければ、APP_ENV=dev の場合にのみ compile カスタムスクリプト内で改めて composer install してあげればよい
GitHubで編集を提案

Discussion