Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deploying to Firebase Functions with a monorepo

As per Firebase Functions with Yarn workspaces, I'd like to deploy a monorepo to Firebase using Yarn Workspaces. I can successfully deploy the "web" aspect to Firebase Hosting, which references a shared "core" workspace. However, attempts to do the same with the "functions" workspace on Firebase Functions fails.

I'm using Yarn Workspaces with the following folder structure:

packages/           
  core/             // name: firebase-monorepo-core: custom core package
  functions/        // name: firebase-monorepo-functions: firebase functions package
  web/              // name: firebase-monorepo-web: react package

These have been configured in the root package.json file:

"workspaces": {
    "packages": [
        "packages/*"
    ],
    "nohoist": [
        "**/firebase-monorepo-core"
    ]
}

In an attempt to enable Yarn workspaces in Firebase (and therefore share my core package) I've used the nohoist feature to create symlinks to the core workspace in functions and web as per twiz's Stackoverflow answer.

The core package also exists as a dependency in functions and web:

"dependencies": {
    "firebase-monorepo-core": "*"
}

There are no problems when any of this runs locally, and in fact deployment of the web package to Firebase hosting works fine. However, deployment of the functions package to Firebase functions throws an error:

Firebase functions deployment error

I've created a Github repository https://github.com/cjmyles/firebase-monorepo to demonstrate this, and the resultant web workspace can be viewed at https://fir-monorepo.firebaseapp.com.

I can get the functions package to deploy if I yarn pack the core workspace and reference it as a tarball in the package.json file, but I really don't like that solution and it will require more overhead to automate it.

Any advice would be appreciated to resolve this issue.

like image 910
Craig Myles Avatar asked Apr 03 '20 16:04

Craig Myles


3 Answers

one potential solution is to use Lerna.

You can then publish your core package to a private GitHub package and use Lerna to create a symlink to the core package for local development.

like image 168
pureth Avatar answered Nov 11 '22 22:11

pureth


The issue is that firebase-tools assumes that all packages used by your function are available in npm.

A quick solution is to use firelink.

cd ./packages/functions
yarn add -D @rxdi/firelink

Add an equivalent section to your functions package.json for all shared dependencies.

  "fireDependencies": {
    "@org/common": "../../packages/common"
  },

Change your deploy script to use firelink binary

  "scripts": {
    "build": "tsc --build tsconfig.build.json",
    "predeploy": "yarn build",
    "deploy:functions": "firelink deploy --only functions"
  }

Just a heads up does not work with "devDependencies", but you should not be needing them in production anyway.

Update your firebase.json to run the predeploy target.

  "functions": {
    "predeploy": ["npm --prefix \"$RESOURCE_DIR\" run predeploy"],
    "source": "packages/functions"
  },

Lastly remove any local yarn.lock files in the workspace package. If you wish to lock the dependencies add npm install to your predeploy step. If anyone has a better solution to this let me know. It seem unfortunate that yarn does not have the ability to generate lock files on demand in a workspace.

like image 1
expelledboy Avatar answered Nov 11 '22 21:11

expelledboy


I got this to work but it was a pain and hacky. So far it's working well though. I'm using NPM Workspaces, but the concept should apply to any project where node modules are hoisted to the root folder. I'm also using GitHub Workflows / Actions for CI and TypeScript, so all of this works within those environments.

  • Build my dependent packages using TypeScript's tsc
  • Copy the compiled files into the functions folder (functions/src/domain to be exact) so that they get uploaded by Firebase CLI
cp -r packages/domain firebase/functions/src/domain
  • Insert local dependency inside functions/package.json. I'm doing it with sed, which GitHub actions supports:
sed -i 's/"dependencies": {/"dependencies": { "@mycompany\/domain": "file:src\/domain",/' package.json

Before that command, my package.json looks like this:

  "dependencies": {
    "lodash": "^4.17.20",
  ...

After that command, it looks like this:

  "dependencies": { "@mycompany/domain": "file:src/domain",
    "lodash": "^4.17.20",
  ...
  • Then deploy: firebase deploy --only functions

Not pretty, but it works with full automation. 😁

like image 1
Johnny Oshika Avatar answered Nov 11 '22 22:11

Johnny Oshika