Skip to content

Workspace Dependencies: Firebase Functions vs API Services โ€‹

This guide explains how local workspace packages (@lantern/shared, @lantern/forge) are resolved across the two deployment platforms in this monorepo.

The Two Patterns โ€‹

Firebase FunctionsAPI Services (Cloud Run)
Deploy toolFirebase CLIgcloud CLI
What gets uploadedOnly services/functions/firebase/Service directory + copied packages
package.json referencesfile:../../../packages/shared (workspace paths)file:../../../packages/shared (workspace paths)
Deploy-time transformationpack-workspace-deps.sh creates .tgz tarballs and temporarily patches package.jsonCI copies packages into service directory with cp -r

Both patterns use workspace-relative paths (file:../../../packages/shared) in their committed package.json. The deploy-time transformation is temporary and never committed.

Firebase Functions โ€‹

Firebase CLI uploads only the services/functions/firebase/ directory to Cloud Build. It cannot follow file:../../../packages/shared symlinks because the parent monorepo structure is not included.

Solution: A predeploy hook (firebase.json) runs pack-workspace-deps.sh which:

  1. Runs npm pack on packages/shared and packages/forge to create self-contained .tgz tarballs
  2. Temporarily patches package.json to reference the tarballs (file:lantern-shared-0.1.0.tgz)
  3. Firebase CLI reads the patched version and uploads it with the tarballs
  4. Restores package.json to workspace paths via an EXIT trap

The .tgz files are gitignored (services/functions/firebase/.gitignore).

Do not commit tarball references โ€‹

The committed package.json must always use workspace paths:

json
"@lantern/shared": "file:../../../packages/shared",
"@lantern/forge": "file:../../../packages/forge"

Never commit tarball references like file:lantern-shared-0.1.0.tgz. This breaks:

  • npm ci on fresh clones (tarballs are gitignored)
  • CI pipelines (no tarballs available)
  • Emulator runs (firebase emulators:start)

If you see tarball references in a diff, the restore step in pack-workspace-deps.sh did not run. Revert the package.json to workspace paths.

API Services (Cloud Run) โ€‹

Cloud Run services use gcloud run deploy --source, which uploads the service directory. The CI workflow (and local deploy scripts) copy the shared packages into the service directory before deploy:

bash
# From deploy-dev.yml / local deploy:dev script
cp -r packages/shared services/api/auth/.shared-pkg
gcloud run deploy auth-api --source services/api/auth ...
rm -rf services/api/auth/.shared-pkg

No package.json patching is needed because the file:../../../packages/shared path resolves correctly during npm ci inside the Cloud Build container (the copied .shared-pkg directory serves as the resolution target).

Key Files โ€‹

FilePurpose
services/functions/firebase/pack-workspace-deps.shPredeploy: pack tarballs, patch + restore package.json
firebase.json (functions.predeploy)Triggers pack script before deploy
services/functions/firebase/.gitignoreIgnores *.tgz tarballs
.github/workflows/deploy-dev.ymlCI: copies packages for Cloud Run deploys
services/api/*/package.json (deploy:dev script)Local: copies packages for Cloud Run deploys

Emulators โ€‹

Firebase emulators (firebase emulators:start --only functions) run functions locally and need workspace-relative paths to resolve. This is another reason the committed package.json must use file:../../../packages/shared, not tarballs.

Built with VitePress