0x80 / firebase-tools-with-isolate

The Firebase CLI with isolate-package integrated to support monorepos
MIT License
17 stars 0 forks source link

Doesn't seem to work due to isolate being invoked from the workspace root #8

Open segevfiner opened 1 month ago

segevfiner commented 1 month ago

isolate is currently being invoked without any arguments: https://github.com/0x80/firebase-tools-with-isolate/blob/952fd518dd8bdd97b83db8204227bdf8e45dba67/src/deploy/functions/prepareFunctionsUpload.ts#L152

And this causes it to run from the workspace root by default, and fail with:

i  functions: +++ Failed to isolate: Failed to find outDir in tsconfig. If you are executing isolate from the root of a monorepo you should specify the buildDirName in isolate.config.json.

You should pass it targetPackagePath based on the actual sourceDir in this case, no?

segevfiner commented 1 month ago

Just passing targetPackagePath might not be the best though. For a start, it requires a relative path ATM, while sourceDir as passed there is absolute, and isolate-package will still read the top-level isolate.config.json instead of allowing one per codebase. It might be better that isolate-package will have a parameter to invoke it against a different directory than the current working directory.

segevfiner commented 1 month ago

The following patch can fix this, but read what I said above:

diff --git a/lib/deploy/functions/prepareFunctionsUpload.js b/lib/deploy/functions/prepareFunctionsUpload.js
index 32b4556ad2539a8026bbb7e36d7e2a2f79c3179a..dd4a883f28625e8c449a337575b1b97900bec407 100644
--- a/lib/deploy/functions/prepareFunctionsUpload.js
+++ b/lib/deploy/functions/prepareFunctionsUpload.js
@@ -96,7 +96,7 @@ async function prepareFunctionsUpload(sourceDir, config, runtimeConfig) {
         utils.logLabeledBullet("functions", `Start isolating the source folder...`);
         try {
             const { isolate } = (await (0, dynamicImport_1.dynamicImport)("isolate-package"));
-            const isolateDir = await isolate();
+            const isolateDir = await isolate({ config: { targetPackagePath: config.source } });
             utils.logLabeledBullet("functions", `Finished isolation at ${clc.bold(isolateDir)}`);
             return packageSource(isolateDir, config, runtimeConfig);
         }
0x80 commented 4 weeks ago

@segevfiner I'm using this to deploy from multiple packages to firebase, and the idea is that by default it should work. So maybe your setup is different in terms of where you have your firebase.json or where you execute the deploy command.

I have a firebase.json file in each package that I deploy to firebase, and I run the deploy command from that package. AFAIK the workspace root doesn't get involved then.

So maybe you can tell me more about your file structure, or share a working example that illustrates your problem?

segevfiner commented 4 weeks ago

It's a structure where there is a single firebase.json at the root, and the functions are subpackage in a monorepo workspace. I'll build an example and post a link here.

segevfiner commented 3 weeks ago

See https://github.com/segevfiner/firebase-tools-with-isolate-example

0x80 commented 3 weeks ago

@segevfiner This doesn't look like a valid monorepo setup.

Your workspace packages definition is pointing directly to functions, which is a package by itself and not a folder containing packages. Also, a monorepo wouldn't have src folder in its root. That should to be part of its own package.

I suggest to:

segevfiner commented 3 weeks ago

It is valid in terms of being supported by pnpm, and is effective for smaller projects, by I can do a more conventional one, if it helps. It will still have the same issue though with a single firebase.json. You are supposed to have one firebase.json, that way you can just firebase deploy the whole thing, without fear that forgetting an --only will cause something to die, and is also necessary for the firebase emulators.

0x80 commented 3 weeks ago

Yes, please do. I don't really feel like diving into it if it's not structured like a monorepo. I would like support using a single firebase.json if I can but isolate-package scans the packages folders and makes assumptions based on a monorepo structure, so I want to make sure the issues are not related to your project structure.

I'm not aware of the need for a single firebase.json file. I don't have a lot of experience with emulators, but AFAIK you can start them on different ports for each package.

What do you mean with "cause something to die"? I am used to a manifest scripts like "deploy:dev": "firebase deploy --only functions --project development", and trigger that.

segevfiner commented 3 weeks ago

Doing firebase deploy without --only with multiple firebase.json can cause stuff it to delete stuff inadvertently.

You can and they already do each have a port, but for the emulators UI to function, auth to work correctly across both hosting and functions, etc. you do need a single firebase.json and one instance of the emulators suite.

0x80 commented 3 weeks ago

Doing firebase deploy without --only with multiple firebase.json can cause stuff it to delete stuff inadvertently.

Each firebase.json should have a "codebase" name defined. That acts a a namespace for your deployed functions. I have two active projects that each deploy 3-4 packages to firebase, and never experienced issues with deleting things.

The codebase property was there to support multiple packages deploying to firebase, so I'm surprised to hear that it would be incompatible with auth. I will try to look into that at some point.

segevfiner commented 3 weeks ago

With the emulators, cause they emulate the auth in the emulated functions runtime in a way that seems to require them all to run together from a single firebase.json. That was my experience with it anyway.

I'll make a more conventional monorepo layout example project later.

0x80 commented 3 weeks ago

I was just thinking, if "functions" is your only package deploying to firebase, and "src" is a web app hosted somewhere else, than I think there should be no issue splitting your codebase in two separate packages like I suggested, one for functions and one for app/web.

Or is your web app somehow tied to the firebase emulators?

In other words, I wonder why you need the firebase.json to live in the root if you only have one package deploying to firebase.

jayhickey commented 2 weeks ago

I'm not aware of the need for a single firebase.json file. I don't have a lot of experience with emulators, but AFAIK you can start them on different ports for each package.

I'm experiencing the same issue as @segevfiner, I'm starting to integrate cloud functions and using isolate-package for our monorepo but I can't figure out a good config that allows for deploying multiple cloud functions + emulator use with live reloading for development.

If I use multiple firebase.json against each of my function packages, I can't start up a single emulator that will run all of the functions + firestore + auth. If I use a single firebase.json at the project root, I can set an array of functions in the json but I hit the issue described at the top of this thread where I cannot get isolate to run on the function packages and deploy them.

My preference would be to follow your guidance @0x80 and have multiple firebase.json, but the lack of emulator + live reloading makes for an unacceptable developer experience for our needs.

Do y'all have any suggestions? Thanks! 🙏

segevfiner commented 2 weeks ago

@0x80 https://github.com/segevfiner/firebase-tools-with-isolate-example-2

0x80 commented 1 week ago

@jayhickey As I mentioned I don't have a lot of experience running the emulators but in mono-ts you can see I run them on different ports for each package and the web app runs op top of them in dev mode. If you are able to test each package separately, than I think that should be no problem.

But I guess if your integration or e2e tests require 1 global auth emulator instance that would be problematic. I haven't yet used the auth emulator myself.

I will try to allocate some time soon to look into this, but I'm travelling at the moment.

0x80 commented 1 week ago

@segevfiner In your v2 setup, why are you not co-locating all of your firebase files with your "functions" package?

If your repo contains only one package that deploys to firebase, then things can be simple. You can place all config and dependencies where they belong => in the functions package. The root package.json then doesn't need any dependencies, and firebase-tools-with-isolate would be a dev dependency of your functions package.

segevfiner commented 1 week ago

Because it also deploys the web app to hosting, and the web app uses auth and other services with the emulator. (Of course in the real application, not in this sample meant to just show the project structure.