Skip to content

feat(functions): add functions:kits:install command - #10900

Open
wandamora wants to merge 6 commits into
mainfrom
morawand-kits-install
Open

feat(functions): add functions:kits:install command#10900
wandamora wants to merge 6 commits into
mainfrom
morawand-kits-install

Conversation

@wandamora

@wandamora wandamora commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Description

Adds the firebase functions:kits:install command (gated behind the kits experiment) 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:

  • Command Registration & Experiment Gating: Registers functions:kits:install conditionally when the kits experiment is enabled.
  • Interactive & Flag Input: Accepts a --npm_package <package> flag, and interactively prompts for the kit and initial instance ID.
  • Package Parsing & Validation:
    • Parses scoped and unscoped package specifiers with optional versions/tags.
    • Sanitizes package names into valid kit identifiers.
    • Validates kit ID uniqueness and ensures instance IDs do not collide with existing codebase names or other kit instances in firebase.json.
  • Security & Third-Party Safeguards:
    • Differentiates first-party (@firebase-functions-kits/*) and third-party packages.
    • Inspects packages for npm-shrinkwrap.json via npm pack --dry-run --json to warn users when dependencies are unlocked.
    • Prompts for explicit user confirmation before installing third-party kits.
    • Installs third-party packages with --ignore-scripts for safety.
  • Wrapper Project Scaffolding:
    • Scaffolds a wrapper directory under function-kits/<kit-id>/ containing package.json, tsconfig.json, .gitignore, and src/index.ts from templates.
    • Generates index.ts using templates/init/functions/typescript/index-kit.ts, re-exporting the kit package with customizable global function parameters.
    • Automatically runs npm install and npm run build.
  • Configuration Updates: Adds the kit configuration block and instance mapping into firebase.json.
  • Testing: Comprehensive unit test suite in src/commands/functions-kits-install.spec.ts.

Scenarios Tested

  • Experiment gating: Verified error is thrown when kits experiment is disabled.
  • Precondition checks: Verified error when run outside a Firebase project directory (firebase.json missing).
  • Package specifier parsing for scoped/unscoped packages with and without versions.
  • Package name sanitization to valid kit identifiers (length truncation, character filtering).
  • Third-party package detection and warnings for packages without npm-shrinkwrap.json.
  • User confirmation flow cancellation / acceptance for third-party kits.
  • Validated that third-party kits run npm install --ignore-scripts.
  • Kit installation end-to-end scaffolding (package.json, tsconfig.json, index.ts, firebase.json).
  • Conflict validations:
    • Duplicate functions.kit ID in firebase.json.
    • Collision between kit instanceId and existing function codebase name.

Sample Commands

# Enable the kits experiment
firebase experiments:enable kits

# Install a package CLI
firebase functions:kits:install --npm_package @invertase/example-firebase-kit-hello-world@0.1.0

@wiz-9635d3485b

wiz-9635d3485b Bot commented Aug 7, 2026

Copy link
Copy Markdown

Wiz Scan Summary

Scanner Findings
Vulnerability Finding Vulnerabilities -
Data Finding Sensitive Data -
Secret Finding Secrets -
IaC Misconfiguration IaC Misconfigurations -
SAST Finding SAST Findings 13 Medium
Software Management Finding Software Management Findings -
Total 13 Medium

View scan details in Wiz

To detect these findings earlier in the dev lifecycle, try the Wiz Code extension for VS Code, JetBrains, or Visual Studio.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread src/commands/functions-kits-install.ts Outdated
Comment thread src/commands/functions-kits-install.ts
@wandamora
wandamora force-pushed the morawand-kits-install branch from 2aeea99 to e4b08d8 Compare August 7, 2026 00:11
- throw error for failing to parse template
- remove redundant ? for options.config.src, it shouldn't be undefined or null
@wandamora
wandamora force-pushed the morawand-kits-install branch from 073b681 to 2872d49 Compare August 7, 2026 17:09
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
@wandamora
wandamora marked this pull request as ready for review August 7, 2026 18:55
@wandamora
wandamora requested review from ajperel and inlined August 7, 2026 18:55

@ajperel ajperel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably also good to test release candidate versions since we'll use them

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

or tags like @latest

technically npm distinguished between versions and tags but maybe we can just call it all version and get away with that?

Thoughts @inlined ?

});

it("should return true for packages outside @firebase-functions-kits scope", () => {
expect(isThirdPartyPackage("@other-scope/my-kit")).to.be.true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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("/");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,

@ajperel ajperel Aug 8, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants