feat(functions): add functions:kits:install command - #10900
Conversation
Wiz Scan Summary
To detect these findings earlier in the dev lifecycle, try the Wiz Code extension for VS Code, JetBrains, or Visual Studio. |
There was a problem hiding this comment.
Code Review
This pull request introduces the functions:kits:install command, allowing users to install Cloud Function kits into their Firebase projects. It includes implementation logic, unit tests, command registration under the kits experiment, and a TypeScript entry point template. The review feedback highlights two key areas for improvement: first, a potential runtime crash in functions-kits-install.ts due to unsafe direct access of options.config.src after using optional chaining; second, the need to throw a FirebaseError immediately if parsing the package.nolint.json template fails, rather than silently continuing with an empty object and causing downstream build failures.
2aeea99 to
e4b08d8
Compare
- throw error for failing to parse template - remove redundant ? for options.config.src, it shouldn't be undefined or null
073b681 to
2872d49
Compare
package.json, tsconfig.json, and .gitignore should live in the root of the kit, and index.ts will be in /src.
- Prompt for kit ID - Update npm shrinkwrap warning - change FIREBASE_FUNCTION_KIT_REGION param to FUNCTION_KIT_REGION
ajperel
left a comment
There was a problem hiding this comment.
Thanks for getting this out!
A bunch of minor comments and questions and a few things we should iron out through discussion.
| const res = parsePackageSpecifier("my-kit@^2.0.0"); | ||
| expect(res).to.deep.equal({ | ||
| packageName: "my-kit", | ||
| version: "^2.0.0", |
There was a problem hiding this comment.
Probably also good to test release candidate versions since we'll use them
| }); | ||
|
|
||
| it("should return true for packages outside @firebase-functions-kits scope", () => { | ||
| expect(isThirdPartyPackage("@other-scope/my-kit")).to.be.true; |
There was a problem hiding this comment.
I'd test "@firebase-function-kits-fake/foo" to also be 3rd party. Gotta stop the people trying to be malicious.
| ).to.be.rejectedWith(FirebaseError, /functions.kit must be unique/); | ||
| }); | ||
|
|
||
| it("should reject instance ID that collides with codebase name", async () => { |
There was a problem hiding this comment.
Even now we should also test and reject instance ID that collides with another since you could have two different kits that decide to use the same instance id if the user picks silly ones.
| const INDEX_KIT_TEMPLATE = readTemplateSync("init/functions/typescript/index-kit.ts"); | ||
|
|
||
| export interface FunctionsKitsInstallOptions extends Options { | ||
| npm_package?: string; |
There was a problem hiding this comment.
As I see this.... I am again debating between this and
--package
(future) --package_manager
Pros:
- We only ever need 2 flags (I hope)
- Maybe easier to re-use logic in the future in cases where like... yarn packages and npm packages are the same since they are both npm behind the scenes
Cons:
- Users not using npm have to always specify two flags instead of one.
I am still very torn. Curious what you and @inlined think after getting into it more.
| * e.g., "@firebase-functions-kits/firestore-bigquery-export" -> "firestore-bigquery-export" | ||
| */ | ||
| export function sanitizePackageNameToKitName(packageName: string): string { | ||
| const parts = packageName.split("/"); |
There was a problem hiding this comment.
I think this is true but you've verified that package names can only have one "/" and only if scoped right?
| } | ||
| } | ||
|
|
||
| pkgJson.name = `${kitId}-wrapper`; |
There was a problem hiding this comment.
I think it's worth a comment explaining what you're doing and why.
| await options.config.askWriteProjectFile(relIndexTsPath, indexContent); | ||
| } | ||
|
|
||
| const installArgs = isThirdParty ? ["install", "--ignore-scripts"] : ["install"]; |
There was a problem hiding this comment.
I wonder if we should just always ignore scripts. Yes, we could technically trust ours more... but when would we use them?
| const newKitConfig: KitFunctionConfig = { | ||
| kit: kitId, | ||
| sourcePackage: { | ||
| id: packageName, |
There was a problem hiding this comment.
This is 100% what we put in the design doc... but as I brush up while reviewing...
none of the package systems talk about ids. They talk about package / distribution names. I wonder if we should also name this name. But.... maybe not a big deal? If we did want to do it maybe easier in a separate follow up change? Thoughts.
package systems also talk about the full spec (including version, etc.) but we're not storing that and I don't think we should since it'll get out of date.
| @@ -0,0 +1,101 @@ | |||
| import { setGlobalOptions } from "firebase-functions"; | |||
| import { | |||
| // defineBoolean, | |||
There was a problem hiding this comment.
I have feedback on Victor's PR that maybe changes how we do this completely in which case this comment is not needed.
This is fine but I also was wondering if just to save people effort uncommenting if we should do something like
import * as params from "firebase-functions/params"
and then like params.defineString()
Only to save us this long list of maybe not used things?
| description: "Region where functions should be deployed.", | ||
| }); | ||
|
|
||
| // To configure more https://firebase.google.com/docs/reference/functions/2nd-gen/node/firebase-functions.globaloptions |
There was a problem hiding this comment.
I think we could make this a more user friendly comment if we keep it guide users a bit more.
something like
To require setting a global option for each instance of this kit uncomment the option parameter definition and the line configuring it below. Learn more about these options at: https://firebase.google.com/docs/reference/functions/2nd-gen/node/firebase-functions.globaloptions
or if we go the process.env route a different but equally helpful comment.
Description
Adds the
firebase functions:kits:installcommand (gated behind thekitsexperiment) to allow developers to install and configure reusable Cloud Function kits in their Firebase projects.Note: this is the first iteration of the command that just handles the first instance in a single project.
Key Changes:
functions:kits:installconditionally when thekitsexperiment is enabled.--npm_package <package>flag, and interactively prompts for the kit and initial instance ID.firebase.json.@firebase-functions-kits/*) and third-party packages.npm-shrinkwrap.jsonvianpm pack --dry-run --jsonto warn users when dependencies are unlocked.--ignore-scriptsfor safety.function-kits/<kit-id>/containingpackage.json,tsconfig.json,.gitignore, andsrc/index.tsfrom templates.index.tsusingtemplates/init/functions/typescript/index-kit.ts, re-exporting the kit package with customizable global function parameters.npm installandnpm run build.firebase.json.src/commands/functions-kits-install.spec.ts.Scenarios Tested
kitsexperiment is disabled.firebase.jsonmissing).npm-shrinkwrap.json.npm install --ignore-scripts.package.json,tsconfig.json,index.ts,firebase.json).functions.kitID infirebase.json.instanceIdand existing functioncodebasename.Sample Commands