Monorepo で Cloud Functions for Firebase へデプロイ

September 08, 2022

はじめに

.NET におけるプロジェクト分割に慣れている身としては、Node.js でも積極的にモジュールを分割しつつ、Monorepo な構成で進めがちなわけですが、そんな構成でいざ世の中の便利サービスを使おうとすると詰まることも多いです。今回は、Monorepo で Cloud Functions for Firebase へデプロイするためのまとめです。

前提

関連ツール

  • Yarn v1 系
  • Firebase CLI 11.8.0

今回扱うリポジトリ内構造(本題と逸れる内容は省略)

依存関係

Cloud Functions 用パッケージである firebase-functions がサーバー向けモジュール server を参照し、server はサーバー・クライアント共通モジュールである core を参照。

リポジトリルート

> tree -L 1 .
.
├── README.md
├── firebase.json
├── client
├── core
├── firebase-functions
├── server
├── node_modules
├── package.json
└── yarn.lock

5 directories, 4 files

※関係ない内容は割愛

package.json

{
  "private": true,
  "workspaces": {
    "packages": [
      "client",
      "core",
      "server",
      "firebase-functions"
    ]
  },
  "devDependencies": {},
  "dependencies": {}
}

※関係ない内容は割愛

firebase.json

{
  "functions": {
    "source": "firebase-functions"
  }
}

参考材料

公式ドキュメントから押さえておくべきポイント

  • yarn.lock ファイルがプロジェクト内にある場合は、そのロックファイルが優先される。
  • 関数をデプロイする際、Firebase CLI では、ローカルの node_modules フォルダを無視する。
  • file: 接頭辞を使用してローカルの Node.js モジュールを含めることもできる。

観測されたデプロイ挙動

  • firebase.jsonsource に指定したディレクトリ外に配置された Node.js モジュールは参照できない。

参考 Issues@Github

方針

上記参考材料をもとに試行錯誤し、

  1. 何かしらの形で Cloud Functions 用パッケージ内に Node.js モジュールを配置
  2. file: 接頭辞を使用し、配置した Node.js モジュールを参照

が良さそうだと考えました。もちろん、通常の Yarn Workspaces 構成とはギャップがあるため、ギャップを埋めるために何かしらの対応を行う必要があります。

ちなみに、このレベルでのギャップ埋めを許容するのであれば、デプロイ時のみ非公開モジュールの使用も選択肢に上がるかもしれませんが、積極的に採用すべきメリットを見出せなかったため、今回は扱いません。

解決策1

firebase-yarn-workspaces を利用することで問題なくデプロイできました。このパッケージにより、

  1. Cloud Functions 用パッケージ内に .firebase-yarn-workspaces ディレクトリを作成し、Monorepo 参照モジュールをコピー
  2. Cloud Functions 用パッケージの package.json"core": "*""core": "file:.firebase-yarn-workspaces/core" のように Monorepo 参照を file: 接頭辞を使用して書き換え

が行われる結果、うまくデプロイできるようになります。

ちなみに、README に記載されている Turborepo を使った例に従えば、上記操作を turbo prune により作成されたディレクトリで行うことで、既存ディレクトリが汚れるのを防ぐことができる、かつ、yarn.lock ファイルも最適化されるのでより良い形になると思います。

解決策2

Issues@Github で言及されている通り、

  1. 参照先を pack し、Cloud Functions 用パッケージ内に配置
  2. Cloud Functions 用パッケージの package.json"core": "*""file:./core.tgz" のように Monorepo 参照を file: 接頭辞を使用して書き換え

でもデプロイできました。ただ、2段階で参照する構成になっているせいか、色々詰まりました。

おわりに

なんとかデプロイできたものの、結局は(turbo prune で切り出せるものの)リポジトリ内に一時的な差分を作ることにはなるので、(自動化できるものの)本質的ではない定型作業を入れざるを得ない面倒さがありますね。暫定対応と割り切って、公式なサポートを待つ感じでしょうか。


Product

「いつやるかは決めてないけれど、必ずやらなければいけないタスク」を忘れないためのアプリ