Skip to content

Prototype Tool to Create Cloud Run Apps with Firebase CLI - #10898

Open
falahat wants to merge 10 commits into
mainfrom
bapi_prototype
Open

Prototype Tool to Create Cloud Run Apps with Firebase CLI#10898
falahat wants to merge 10 commits into
mainfrom
bapi_prototype

Conversation

@falahat

@falahat falahat commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

This initial draft was ai-generated and must still be reviewed carefully by humans. It has been tested manually:

Description

Scenarios Tested

Sample Commands

firebase init run
firebase deploy --only run --project xxx

…veiwed by humans and brought up to bar.

Testing: This was tested manually by deploying a Cloud Run app
@wiz-9635d3485b

wiz-9635d3485b Bot commented Aug 6, 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 4 Medium 16 Low
Software Management Finding Software Management Findings -
Total 4 Medium 16 Low

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 support for configuring and deploying Cloud Run services via the Firebase CLI, including integration with App Hosting configurations, initialization features, and end-to-end tests. The feedback highlights several critical issues: hardcoded absolute paths in the E2E test script, a bug in constructing the secret resource path for Cloud Run environment variables, and a violation of best practices regarding revision-level versus service-level scaling. Additionally, the modification to updateService in src/gcp/runv2.ts is flagged as highly risky for existing Cloud Functions v2 deployments. Finally, the reviewer recommends removing an accidentally committed backup file (deploy.ts.bak), adding validation for serviceId, and adhering to the repository style guide by throwing FirebaseError instead of generic Error objects.

Comment thread e2e_run_tests/run_all_tests.sh Outdated
Comment thread src/deploy/run/deploy.ts Outdated
Comment thread src/deploy/run/deploy.ts Outdated
Comment thread src/gcp/runv2.ts Outdated
Comment thread src/deploy/run/deploy.ts.bak Outdated
Comment thread src/deploy/run/prepare.ts Outdated
Comment thread src/deploy/run/prepare.ts
Comment thread src/init/features/run.ts Outdated
Comment thread src/init/features/run.ts
Comment on lines +24 to +26
if (!projectId) {
throw new Error("Project ID must be set before initializing Cloud Run.");
}

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.

medium

Throwing a generic Error for expected, user-facing errors violates the repository style guide. Please throw a FirebaseError instead.

Suggested change
if (!projectId) {
throw new Error("Project ID must be set before initializing Cloud Run.");
}
const projectId = setup.projectId;
if (!projectId) {
throw new FirebaseError("Project ID must be set before initializing Cloud Run.");
}

falahat added 7 commits August 7, 2026 10:33
Do Not read apphosting.local.yaml by accident
Add timeout to artifact registry actions
Track Cloud Build operations/results better
* Deduplicated Test CLI Process Wrapper
* Standardized Secret Name Parsing
* Standardized GCP API Verification
* Resolved RunConfig Type Naming Collisions
* Materialized Target Configuration & Target Filtering
* Updated firebase.json schema to include the "run" section
* Cleaned Init Feature Scaffolding
* Gated ABIU Base Image Updates
@falahat

falahat commented Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

/gemini review

@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 support for deploying Cloud Run services via the Firebase CLI, adding configuration schemas, initialization prompts, deployment lifecycle hooks, and an E2E test suite. The code reviewer provided valuable feedback focused on aligning the implementation with repository style guides. Key recommendations include replacing manual polling loops with the central pollOperation utility, avoiding the use of the :latest tag for container images to prevent deployment overwrites, eliminating as any type casts by properly typing options and configurations, and ensuring FirebaseError throws specify non-zero exit codes for precondition violations.

Comment thread src/gcp/runv2.ts
Comment on lines +191 to +244
const op = res.body.buildOperation;
const opName = typeof op === "string" ? op : op?.name || "";
const rawId =
opName
.split("/")
.pop()
?.replace(/^build-/, "") || "";
const buildId = (op as any)?.metadata?.build?.id || rawId;
if (buildId) {
const cloudbuildClient = new Client({
urlPrefix: cloudbuildOrigin(),
auth: true,
apiVersion: "v1",
});
const startTime = Date.now();
const timeoutMs = 15 * 60 * 1000;
let buildSuccess = false;
while (Date.now() - startTime < timeoutMs) {
try {
const buildStatusRes = await cloudbuildClient.get<{
status: string;
statusDetail?: string;
}>(`/projects/${projectId}/locations/${location}/builds/${buildId}`);
const status = buildStatusRes.body?.status;
if (status === "SUCCESS") {
logger.info(`[run:submitBuild] Cloud Build ${buildId} completed with SUCCESS.`);
buildSuccess = true;
break;
}
if (
status === "FAILURE" ||
status === "INTERNAL_ERROR" ||
status === "TIMEOUT" ||
status === "CANCELLED"
) {
throw new FirebaseError(
`Cloud Build failed with status ${status}: ${buildStatusRes.body?.statusDetail || ""}`,
);
}
} catch (err: any) {
if (err instanceof FirebaseError && err.message.startsWith("Cloud Build failed")) {
throw err;
}
if (err.status && err.status >= 400 && err.status < 500) {
throw err;
}
logger.debug(`[run:submitBuild] Polling retry on transient error: ${err.message}`);
}
await new Promise((resolve) => setTimeout(resolve, 3000));
}
if (!buildSuccess) {
throw new FirebaseError(`Cloud Build ${buildId} timed out after 15 minutes.`, { exit: 1 });
}
}

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.

high

Instead of implementing a manual polling loop with setTimeout to wait for the Cloud Build to complete, we should leverage the repository's central pollOperation utility. Cloud Build's build submission returns a standard Long Running Operation (LRO) that can be polled directly. This reduces code complexity, improves robustness (by using standard backoff and error handling), and adheres to the repository style guide rule to look for existing utilities first.

  const op = res.body.buildOperation;
  const opName = typeof op === "string" ? op : op?.name;
  if (opName) {
    await pollOperation({
      apiOrigin: cloudbuildOrigin(),
      apiVersion: "v1",
      operationResourceName: opName,
      masterTimeout: 15 * 60 * 1000,
    });
  }
References
  1. Look for existing utilities first: Before writing common helper functions, check if a suitable function already exists. (link)

Comment thread src/deploy/run/deploy.ts
await artifactregistry.ensureRepository(projectId, region, "cloud-run-source-deploy");

// Construct image URI
const imageUri = `${region}-docker.pkg.dev/${projectId}/cloud-run-source-deploy/${service.serviceId}:latest`;

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.

medium

Using the :latest tag for the container image URI in Cloud Run deployments is risky. If multiple deployments run concurrently or sequentially, they will overwrite the :latest tag. Furthermore, if Cloud Run needs to restart a container or scale up, it might pull the newer :latest image instead of the one that was originally deployed with the revision, leading to silent production issues. It is highly recommended to use a unique tag (such as a timestamp or commit SHA) for each deployment.

Suggested change
const imageUri = `${region}-docker.pkg.dev/${projectId}/cloud-run-source-deploy/${service.serviceId}:latest`;
const imageTag = `${Date.now()}`;
const imageUri = `${region}-docker.pkg.dev/${projectId}/cloud-run-source-deploy/${service.serviceId}:${imageTag}`;

Comment thread src/deploy/run/deploy.ts
Comment on lines +78 to +80
if ((appHostingConfig as any)?.scripts?.build) {
buildEnv["GOOGLE_NODE_RUN_SCRIPTS"] = (appHostingConfig as any).scripts.build;
}

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.

medium

Avoid using as any as an escape hatch. Instead of casting appHostingConfig to any to access scripts, we should define the scripts property on the AppHostingYamlConfig class in src/apphosting/yaml.ts. This ensures type safety and adheres to the repository style guide.

Suggested change
if ((appHostingConfig as any)?.scripts?.build) {
buildEnv["GOOGLE_NODE_RUN_SCRIPTS"] = (appHostingConfig as any).scripts.build;
}
if (appHostingConfig?.scripts?.build) {
buildEnv["GOOGLE_NODE_RUN_SCRIPTS"] = appHostingConfig.scripts.build;
}
References
  1. Never use any or unknown as an escape hatch. Define proper interfaces/types or use type guards. (link)

Comment thread src/deploy/run/deploy.ts Outdated
Comment on lines +285 to +287
if ((runConfig as any).vpcAccess) {
service.template.vpcAccess = (runConfig as any).vpcAccess;
}

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.

medium

Avoid using as any as an escape hatch. Instead of casting runConfig to any to access vpcAccess, we should add vpcAccess to the AppHostingRunConfig interface in src/apphosting/config.ts. This ensures type safety and adheres to the repository style guide.

Suggested change
if ((runConfig as any).vpcAccess) {
service.template.vpcAccess = (runConfig as any).vpcAccess;
}
if (runConfig.vpcAccess) {
service.template.vpcAccess = runConfig.vpcAccess;
}
References
  1. Never use any or unknown as an escape hatch. Define proper interfaces/types or use type guards. (link)

Comment thread src/deploy/run/prepare.ts
Comment on lines +20 to +21
const runtimeOpt = ((options as any).runtime || (options as any).baseImage) as string | undefined;
const clearOpt = !!((options as any).clearRuntime || (options as any).clearBaseImage);

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.

medium

Avoid using as any as an escape hatch. We can define a RunDeployOptions interface in src/deploy/run/args.ts that extends Options with the Cloud Run specific flags (like runtime, baseImage, clearRuntime, clearBaseImage, primaryRegion, and region). Typing options as RunDeployOptions in prepare and deploy will eliminate all as any casts in this file.

Suggested change
const runtimeOpt = ((options as any).runtime || (options as any).baseImage) as string | undefined;
const clearOpt = !!((options as any).clearRuntime || (options as any).clearBaseImage);
const runtimeOpt = (options.runtime || options.baseImage);
const clearOpt = !!(options.clearRuntime || options.clearBaseImage);
References
  1. Never use any or unknown as an escape hatch. Define proper interfaces/types or use type guards. (link)

Comment thread src/init/features/run.ts
Comment on lines +24 to +26
if (!projectId) {
throw new FirebaseError("Project ID must be set before initializing Cloud Run.");
}

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.

medium

When throwing a FirebaseError due to a violation of a precondition (such as projectId being missing during initialization), we should specify a non-zero exit code (e.g., { exit: 1 }) to adhere to the repository style guide.

Suggested change
if (!projectId) {
throw new FirebaseError("Project ID must be set before initializing Cloud Run.");
}
const projectId = setup.projectId;
if (!projectId) {
throw new FirebaseError("Project ID must be set before initializing Cloud Run.", { exit: 1 });
}
References
  1. Throw FirebaseError (src/error.ts) for expected, user-facing errors. If the error is due to a violation of a precondition (e.g. something that is null but should never be), specify a non-zero exit code. (link)

Comment thread src/deploy/run/prepare.ts
Comment on lines +104 to +106
const sourceDir = options.config
? options.config.path(config.source || config.rootDir || ".")
: process.cwd();

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.

medium

Since options.config is guaranteed to be defined at this point (otherwise rawRunConfigs would be undefined and the function would have thrown an error on line 33), we can safely simplify this to directly call options.config.path without the ternary check.

    const sourceDir = options.config.path(config.source || config.rootDir || ".");

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.

2 participants